HTTPS(Hypertext Transfer Protocol Secure)是一种网络安全传输协议,是一种用于在不可信网络上进行安全通信的传输协议,HTTPS经由HTTP进行通讯,使用SSL/TLS协议对数据进行加密。主要为解决以下三点问题。
SSL安全套接层(Secure Sockets Layer)与TLS传输层安全协议(Transport Layer Security)都是一种用于保障数据安全传输的协议。
SSL由网景公司(Netscape)公司设计,一直发布到SSLv3.0版本,后面其有安全漏洞被许多公司禁用。
TLS由IETF发布,SSL是TLS的前身。
下面是其发布时间表:
协议 | 年份 |
---|---|
SSL 3.0 | 1996 |
TLS 1.0 | 1999 |
TLS 1.1 | 2006 |
TLS 1.2 | 2008 |
TLS 1.3 | 待定 |
那么TLS/SSL位于OSI或TCP/IP模型中的哪一层呢?以下取自维基百科:
TLS and SSL are defined as ‘operating over some reliable transport layer’, which places them as application layer protocols in the TCP/IP reference model and as presentation layer protocols in the OSI model.
结合下图及上面描述可看出,在OSI模型中TLS/SSL位于表现层,在HTTP(应用层)协议之下。
总体流程如下,用*号标注的是可选流程:
Client Server
ClientHello -------->
ServerHello
Certificate*
ServerKeyExchange*
CertificateRequest*
<-------- ServerHelloDone
Certificate*
ClientKeyExchange
CertificateVerify*
[ChangeCipherSpec]
Finished -------->
[ChangeCipherSpec]
<-------- Finished
Application Data <-------> Application Data
Hello Request是一个用于通知客户端需要重新发起握手的简单通知,客户端可以以
ClientHello相回应,也可以忽略,服务器在任何时间都可以发送此消息。
客户端要建立HTTPS连接时会发送一个ClientHello消息,消息体结构如下:
struct {
ProtocolVersion client_version; //客户端支持的TLS最高版本
Random random; //客户端生成的随机数(暂命名client_hello_random),用于之后生成会话密钥
SessionID session_id;
CipherSuite cipher_suites<2…2^16-2>; //支持的加密算法,如RSA
CompressionMethod compression_methods<1…2^8-1>; //支持的压缩算法
select (extensions_present) {
case false:
struct {};
case true:
Extension extensions<0…2^16-1>;
};
} ClientHello;
服务器端收到ClientHello消息后,如果有支持的TLS版本和相关算法,会回复客户端一个ServerHello消息,否则会关闭加密通信,所带信息如下:
struct {
ProtocolVersion server_version; //确定使用的SSL/TLS版本
Random random; //服务器端生成的随机数(暂命名server_hello_random),之后用于生成会话密钥
SessionID session_id;
CipherSuite cipher_suite; //确定使用的加密算法
CompressionMethod compression_method; //确实使用的压缩算法
select (extensions_present) {
case false:
struct {};
case true:
Extension extensions<0…2^16-1>;
};
} ServerHello;
ServerHello消息中确定使用的加密算法(cipher_suite)是从ClientHello消息中 cipher_suites 选择一个出来,如此次微信Web版抓包得到的消息:
Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
- ECDHE : 密钥交换算法,用于生成premaster_secret(用于生成会话密钥master secret),具体生成在Client Key Exchange Message节。
这条消息将会在服务器发送ServerHello会立即发送给客户端,用于携带服务器证书信息。
struct {
ASN.1Cert certificate_list<0…2^24-1>; //服务器端证书链
}
这条消息是用于会话密钥(premaster secret)生成的,首先这条消息不是必须的,只有在客户端与服务器端双方协商采用的加密算法是DHE_DSS、DHE_RSA、DH_anon
时Server Certificate消息中的服务器证书中的信息不足以生成premaster secret时服务器端才需要发送此消息。
struct {
select (KeyExchangeAlgorithm) {
case dh_anon:
ServerDHParams params;
case dhe_dss:
case dhe_rsa:
ServerDHParams params;
digitally-signed struct {
opaque client_random[32];
opaque server_random[32];
ServerDHParams params;
} signed_params;
case rsa:
case dh_dss:
case dh_rsa:
struct {} ;
/* message is omitted for rsa, dh_dss, and dh_rsa /
/ may be extended, e.g., for ECDH – see [TLSECC] */
};
} ServerKeyExchange;
如果此次HTTPS连接是双向认证的话,服务器就可以通过此消息请求客户端证书以便进行身份验证。
struct {
ClientCertificateType certificate_types<1…2^8-1>;
SignatureAndHashAlgorithm
supported_signature_algorithms<2^16-1>; //服务器端可处理的签名算法列表
DistinguishedName certificate_authorities<0…2^16-1>;
} CertificateRequest;
微信Web版只需要服务器端认证所以不会发送此消息。
此消息由服务器端发送,表示服务器端已经完成了为支持密钥交换(生成premaster key)所做的事情,下面开始等待客户端为密钥交换所要发的消息。
客户端在收到此消息后,要验证服务器端证书是否合法,过期或不合法则浏览器会提示警告。
12306未导入它的证书前就会提示这个。
客户端在收到Server Hello Done与Certificate Request后,表示服务器端需要验证客户端证书,这时客户端需要发送此Client Certificate消息给服务器端,即使没有客户端证书也要发个 certificate_list 列表为空的此消息,服务器端未收到或收到此消息后验证客户端证书,验证结果可酌情处理,如关闭加密通信或忽略此错误等。
struct {
ASN.1Cert certificate_list<0…2^24-1>; //客户端证书链
} Certificate;
客户端收到Server Hello Done消息后就发送此消息用于将生成的premaster secret(用于生成会话密钥master secret)发送到服务器端。根据所采用的非对称加密的算法不同生成premaster secret的方式有所区别。
Client Key Exchange Message 消息结构如下:
struct {
select (KeyExchangeAlgorithm) {
case rsa:
EncryptedPreMasterSecret; //premaster secret
case dhe_dss:
case dhe_rsa:
case dh_dss:
case dh_rsa:
case dh_anon:
ClientDiffieHellmanPublic;
} exchange_keys;
} ClientKeyExchange;
服务器收到此消息后终于可以生成会话密钥了,客户端也会同步生成同样的会话密钥,我们要解答几个问题:
RSA等非对称加密算法加密大量数据时的性能远小于AES等对称加密算法,所以前面所做的 Key Exchange 操作都是为了生成一个对称加密算法的密钥,以便提高通信性能。
使用前面协商的如SHA_256哈希算法由三个随机数client_hello_random、server_hello_random、premaster_secret生成一个安全随机数,采用三个随机数是为了随机性更强。
三个随机数中premaster_secret是保密的,所以master_secret也是保密的。
如果是双向认证过程时,客户端会在Client Key Exchange Message之后发送此消息,携带有前面所发消息用客户端证书私钥所做的签名,服务器收到此信息可利用公钥验证,是典型的数字签名验证场景。
struct {
digitally-signed struct {
opaque handshake_messages[handshake_messages_length];
}
} CertificateVerify;
客户端生成master secret之后即可发送密钥改变通知通知服务器,以后要使用对称密钥master secret来加密数据了!
客户端发送握手结束通知,同时会带上前面所发内容的签名到服务器端,保证前面通信数据的正确性。
服务器端收到 Client Key Exchange Message 通知后即可通知客户端已生成master secret,后面的通信数据用此key加密。
服务器端发送握手结束通知,同时会带上前面所发内容的签名到客户端,保证前面通信数据的正确性。
在整个过程中我们得到一个对称加密密钥可以用来加密数据,但怎么确保数据的完整性 这个问题还没有得到解答,让我们探究到底!
TLS记录协议是对TLS握手协议更高级别的封装(它俩都属于TLS),提供了对数据的分片与安全传输功能。维基百科上TLS Record的数据格式:
不用细看,值得注意的值是MAC,没错,就是它。
每次传输的数据都会通过Hash算法算出一个MAC值附加在传输报文中,这个Mac值就用于数据的完整性校验。
是在数据被加密前就被计算出来,再由密钥同数据一块加密。为什么这么说?
直接上官方文档截图:
原文:The encryption and MAC functions translate a TLSCompressed
structure into a TLSCiphertext.
有很多文章说是通过TLS中的未对称加密算法(如RSA)来进行加解密的,由上个问题我们得知MAC与数据其实采用的同一个密钥,那么这个密钥是怎么生成的呢?
[MAC计算节](https://tools.ietf.org/html/rfc5246#section-6.3)
中描述:
The master secret is expanded into a sequence of secure bytes, which
is then split to a client write MAC key, a server write MAC key, a
client write encryption key, and a server write encryption key.
TLS握手协议结束后最后得到的对称加密密钥即是master secret,master secret又延伸出了client write encryption key与server write encryption key。
文章提到的write MAC key貌似与MAC生成有关?搜索一下。
The MAC is generated as:
MAC(MAC_write_key, seq_num +
TLSCompressed.type +
TLSCompressed.version +
TLSCompressed.length +
TLSCompressed.fragment);
答案得出:MAC值的生成与master secret强相关,与TLS采用的未对称加密算法无直接关系。
如果说的不对麻烦留言指点下~
通过上面的分析我们可以得出其解决最开始三个问题的工作方式:
TLS1.2协议官方文档