-
-
代码角度看SSL握手过程
-
2022-3-7 16:16 11342
-
代码角度看SSL握手过程
-- 以 ECDHE_RSA_WITH_AES_128_GCM_SHA256 密码套件为例
QQ :181S3493S7
名词解释:密码套件--SSL握手过程联合使用了一系列加密&摘要算法,这样一组算法的集合,称为密码套件。
以下调试信息输出代码为:
https://github.com/DeDf/atls -- windows
fork自
https://github.com/mrpre/atls -- linux
测试方法:
编译运行运行atls;
下载安装openssl,openssl s_client -connect 127.0.0.1:44444;
用wireshark抓loopback包,过滤条件是tls;
(SSL服务器视角)
a_tls_handshake[1]: a_tls_get_client_hello()
a_tls_handshake[2]: a_tls_send_srv_hello()
a_tls_handshake[8]: a_tls_send_srv_cert()
a_tls_handshake[3]: a_tls_send_srv_ke() Key Exchange
a_tls_handshake[4]: a_tls_send_srv_hello_done()
a_tls_handshake[12]: a_tls_get_client_ke() Key Exchange
a_tls_handshake[11]: a_tls_get_client_ccs() Change Cipher Spec
a_tls_handshake[14]: a_tls_get_client_finished()
a_tls_handshake[6]: a_tls_send_srv_ticket()
a_tls_handshake[5]: a_tls_send_srv_ccs() Change Cipher Spec
a_tls_handshake[10]: a_tls_send_srv_finished()
a_tls_get_client_hello()
这里有个两个重要信息,和一些扩展信息
1.重要的是Client random(32 Bytes),
2.重要的是支持的加密套件list
扩展信息里给出了
1.说明了client支持的椭圆曲线list
2.支持的hash与sign算法的list;sigalg_pair_t g_sigalg_pair[A_TLS_MAX_SIG_ALG]
a_tls_send_srv_hello()
1重要的是Server random(32 Bytes),
2服务器检查client支持的密码套件组,如果服务器支持,则从中选则一个返回给client,
本文以ECDHE_RSA_WITH_AES_128_GCM_SHA256为例
1 2 | ECDH - Elliptic Curve Diffie–Hellman key exchange - 椭圆曲线迪菲 - 赫尔曼密钥交换 ECDHE - (ECDH Ephemeral) 临时ECDH - 保证前向安全 |
a_tls_send_srv_cert()
服务器把自己的证书(链)发给client
a_tls_send_srv_ke()
服务器用ECDHE算法,选择一个server支持的椭圆曲线,本例为已命名的椭圆曲线,secp256r1,
注意client hello的扩展里可能说明了client支持的椭圆曲线list
用该椭圆曲线,生成一个65bytes的公钥;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 这里选择用client_hello里说明的 hash 与sign算法,rsa_pss_rsae_sha256 : 0x0804 ; hash 算法为sha256; client random 32bytes + server random 32bytes + 1 + 2 + 1 + ECDH 公钥 65bytes , 上面共 0x85 字节过一次 hash ; 接着是对上面 hash 的结果做rsa_pss_padding; 有一个等于当前RSA密钥长度的buf( 0x100 字节),内容清 0 ,密钥长度是证书决定的; 生成一个hashlen( 0x20 字节)的随机数作为salt; buf - 1 - 0x20 * 2 - 1 处一字节存一个 "1" ,后面 20 字节存这个salt; 0x00 * 8 ( 8 字节) + 上次 hash 结果( 0x20 字节) + salt( 0x20 字节),再来一次 hash ; 这个 hash 挨着上面的salt存。 然后进入mgf1算法(掩码生成函数), { 用上面最后的 hash 值( 0x20 字节),加一个count( 4 字节 大端序),count从 0 开始计数; 循环(count + + )给这 0x20 + count4字节做 hash ,用这个 hash ,与buf[i]处异或填充buf[i],i从 0 到HashLen. 也就是每做一次 hash 填 32 个字节(HashLen = = 0x20 字节); 填充范围是上面的buf从头到(buf + 0x100 - HashLen - 1 )处; 最后不到 20 字节的,按实际剩余字节数填充。 buf最后一个字节置 0xbc ,首字节out[ 0 ] & = 0xFF >> 1 ; } mgf1算法结束,拼出buf( 0x100 字节:RSA密钥长度); 然后 RSA_private_encrypt( 0x100 , buf( 0x100 字节), out, rsa, RSA_NO_PADDING) 生成的out也是 0x100 字节; 这 0x100 个字节就是RSA sign的内容; |
a_tls_send_srv_hello_done()
简单(略)
a_tls_get_client_ke()
收到client的ECDH的公钥65字节,算出pms 0x20字节,
用"master secret"13字节,+ client_random 0x20字节,+ srv_random 0x20字节,共0x4d字节;
与pms 0x20字节一起,经过phash算法(内部调用HMAC算法(参数调用sha256,一种标准的hash加盐算法)),算出48字节的master_Key.
a_tls_get_client_ccs()
用""key expansion"13字节,+ srv_random 0x20字节,+ client_random 0x20字节,共0x4d字节;
与master_Key 0x30字节一起,经过phash算法(内部调用HMAC算法(参数调用sha256,一种标准的hash加盐算法)),算出0x68字节的Key_Block;
Key_Block包含读与写的cipher_key 0x10字节,IV 4字节。
a_tls_init_cipher,用Key_Block各部分(读的cipher_key 0x10字节,IV 4字节)初始化对称解密算法(AES_128_GCM);
这里初始化的是读取client消息的对称解密算法。
a_tls_get_client_finished()
第一次收到被对称加密的数据,具体解密方式(AES_128_GCM)为以前写的demo的解密算法,不再叙述;
解出来的值为,当前所有接收和发送的握手(数据类型为 HANDHSHAKE = 0x16)数据(除当前的finished消息外),经过一次sha256得到一个hash值,
"client finished" 15个字节, + 上面hash值 0x20个字节,与MASTER_KEY(48 字节)一起;
经过phash算法(内部调用HMAC算法(参数调用sha256,一种标准的hash加盐算法)),得到12字节的值,
这个值就是client发来的值,不一致的话说明被篡改;
a_tls_send_srv_ticket()
atls这里只是简单实现,不是标准;
a_tls_send_srv_ccs() Change Cipher Spec
a_tls_init_cipher,用Key_Block各部分(写的cipher_key 0x10字节,IV 4字节)初始化对称解密算法(AES_128_GCM);
这里初始化的是server发送消息的对称加密算法。
a_tls_send_srv_finished()
第一次发送被对称加密的数据;
当前所有接收和发送的握手(数据类型为 HANDHSHAKE = 0x16)数据(除当前的finished消息外),经过一次sha256得到一个hash值,
"server finished" 15个字节, + 上面hash值 0x20个字节,与MASTER_KEY(48 字节)一起;
经过phash算法(内部调用HMAC算法(参数调用sha256,一种标准的hash加盐算法)),得到12字节的值,
这个值就是发给client的值,client验证不一致的话说明被篡改;
到这里握手完毕,到client向服务器发GET了。
注意GCM加密模式,这里开始读/写都算第二次加密了,因为读/写finished消息就是第一次加密。
- END -