Https协议就是在TCP协议与Http协议之间加了一层TLS/SSL协议,TLS是SSL的后续版本,目前主流使用1.2版本。
HTTPS握手过程 TLS 1.2 RSA 1 2 3 4 5 6 7 8 9 10 欧拉定理:如果 e 与 n 互质(最大公约数为1),则e^(φ(n)) ≡ 1 (mod n),φ(n)是欧拉函数,表示小于等于n,且与n互质的正整数的个数; 于是可以找两个大的质数p和q,计算 n = p × q,那么就有 φ(n) = (p-1) × (q-1); 然后选一个公钥指数e(通常为65537),再计算私钥指数d,使他们满足e × d ≡ 1 (mod φ(n)); 也就是要满足 e × d = φ(n) × k + 1,这里k为常数,d有且仅有唯一解,在φ(n)和e已知的情况下,可以通过辗转相除法得到d 那么将(n, e)作为公钥匙,(n, d)作为私钥,这样: 明文^e mod n = 密文 密文^d mod n = (明文^e mod n)^d mod n = 明文^(e×d) mod n = 明文^(φ(n) × k + 1) mod n = [(明文^(φ(n)))^k × 明文] mod n = 明文
1、Client Hello:客户端给服务端发一个随机数Client_Random,告诉服务端它支持的加密协议版本,比如TLS 1.2,以及使用什么加密套件,比如RSA;
2、Server Hello:服务端给客户端发一个随机数Server_Random,和服务器证书,以及使用的加密协议版本;
3.1、Client Key Exchange:客户端再生成一个随机数pre_master,然后从服务器证书中取出公钥进行加密得到pre_master_key,发给服务端;
3.2、Change Cipher Spec:客户端使用Client_Random、Server_Random、pre_master计算得到会话密钥;
3.3、Encrypted Handshake Message:客户端使用会话密钥对之前所有握手消息进行摘要加密,发给服务端进行校验,确认对方身份;
4.1、Change Cipher Spec:服务端收到pre_master_key后使用私钥进行解密,然后以同样的方式使用三个随机数计算得到会话密钥;
4.2、Encrypted Handshake Message:服务端同样使用会话密钥对之前所有握手消息进行摘要加密,发给客户端进行校验,确认对方身份;
整个过程中,第三个随机数pre_master是关键,只有通信双方能知道这个值是什么,所以各自使用这个信息来计算,得到一个相同的会话密钥。加上前面两个随机数一起计算,只是为了增加会话密钥的随机程度,保证每次HTTPS通信使用的会话密钥都是不同的;
RSA的思路是一方生成一个随机数,然后用对方的公钥加密发给对方,对方再用自己的私钥解密得到这个随机数。但如果对方的私钥遭到泄露,那么双方过去和未来的所有通信内容都可能被窃取,因为这个交换的随机数的加密不再有效,所以RSA无前向安全性。
TLS 1.2 DHE 1 2 3 4 5 6 7 8 9 10 11 模幂运算:g^a mod p = A 和 g^c mod p = C g取2或5,p取一个2048位的大质数,a和c取一个1到p-2之间的随机数作为私钥,然后计算得出公钥A和C; 这里g和p是公开的,如果知道a,很容易根据计算得到A,但是知道A,则几乎不可能计算得出a; 所以,客户端私钥 = a,客户端公钥 = A,服务端私钥 = c,服务端公钥 = C; 模幂运算的交换律: 客户端密钥 = C^a mod p = (g^c mod p)^a mod p = g^(c·a) mod p 服务端密钥 = A^c mod p = (g^a mod p)^c mod p = g^(a·c) mod p 所以互相交换公钥后,双方能计算得出一个相同的密钥
DHE与RSA流程类似,但同信过程中没有直接传递随机数pre_master,而是交换公钥后计算出来的,并且每次通信客户端使用的都是临时公钥私钥对,也就解决了前向安全性问题;
TLS 1.3 ECDHE ECDHE利用的椭圆曲线离散对数特性,能达到与DHE一样的效果,但是计算效率更高,公钥体积更小,所以TLS 1.3固定使用ECDHE;
另外,TLS 1.3简化了握手流程,将四次握手合并成了三次握手,在第一步中,客户端直接传递公钥,然后第二步中,服务端直接可以finished;
签名证书 服务器证书中的内容包括服务器公钥、服务器身份信息、和CA的数字签名 (公钥+身份信息进行哈希后,用权威数字证书机构CA私钥进行加密的值);
然后客户端可以通过CA公钥(任何人都可以得到)对签名进行验证,确保服务器公钥是可信的,这个过程就是数字签名验证 ;进行数字签名是必要的,否则如果服务端直接传递公钥给客户端很可能被中间人攻击 ,中间人可以在第二步换一个自己的公钥发给客户端,这样就能截取本应该被加密的pre_master,凑齐了三个随机数它可以计算出会话密钥,后续所谓的加密通信对中间人就完全透明了。
Chrom抓包Https 1 2 3 4 5 6 7 打开wireshark抓包:http.host=="www.baidu.com" 打开终端,执行:export SSLKEYLOGFILE=ssl.key 然后在同一个终端下执行curl 'https://www.baidu.com' wireshark的protocl中找到TLS,导入ssl.key,然后就能抓到对应的报文,查看对应的tcp stream,就能看到握手通信过程
附录:shell自动化脚本 make_https_crt.sh 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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 # !/bin/bash set -e echo"使用方法: $0 [域名]" echo"1. 制作证书分为两个阶段,先创建CA证书, 再创建域名证书" echo"2. 默认会在脚本所在的目录创建证书相关文件夹" echo"3. 默认创建的证书有效期为 100 年" cd"$(dirname $0)" DOMAIN=$1 if [ -z "$DOMAIN" ];then read -p "请输入需要制作证书的域名: " DOMAIN fi echo"输入的域名是: $DOMAIN, 默认会签发 *.$DOMAIN 的证书(含 $DOMAIN 自身)" if [ ! -x "$(command -v openssl)" ];then echo"未检测到openssl命令, 请安装 openssl" exit 1 fi make_ca() { # 添加一个空行 echo echo"#阶段一: 创建/检查 CA 证书" if [ -f ca/ca.crt ];then echo" CA证书已存在, 不需要重新创建。跳过" echo return fi echo" 在默认的 ./ca 目录中未发现证书, 创建新CA证书。共3步" mkdir ca echo" (1/3)创建CA的私钥" openssl genrsa -out ca/ca.key 2048 echo" (2/3)创建CA的证书请求文件,ca.csr包含公钥和身份信息" ## • C=CN:国家代码(China) ## • ST=ZJ:州/省(ZheJiang) ## • L=HZ:城市(HangZhou) ## • O=testca.com:组织名称 ## • OU=testca:组织单位 ## • CN=testca.com:通用名称(对于CA证书,这通常是CA的名称) ## • emailAddress=admin@testca.com:管理员邮箱 openssl req -new -key ca/ca.key \ -subj "/C=CN/ST=ZJ/L=HZ/O=testca.com/OU=testca/CN=testca.com/emailAddress=admin@testca.com" \ -out ca/ca.csr echo" (3/3)CA自签证书,预先导入到目标客户端" openssl x509 -req -in ca/ca.csr -signkey ca/ca.key -out ca/ca.crt -days 36500 echo" CA证书创建完毕, 相关证书文件路径为: " echo" CA私钥: `pwd`/ca/ca.key" echo" CA证书请求文件: `pwd`/ca/ca.csr" echo" CA证书: `pwd`/ca/ca.crt" } make_crt(){ echo echo"#阶段二: 创建/检查域名证书" if [ -d "$DOMAIN" ];then echo" 证书目录已存在, 请勿重复创建。若需重新创建, 请mv备份或删除该目录" return fi echo"在默认域名同名的 ./${DOMAIN} 目录创建域名证书,共3步" mkdir $DOMAIN echo" (1/3)创建用户的私钥, 并从RSA私钥文件中提取公钥" openssl genrsa -out ${DOMAIN}/${DOMAIN}.key 2048 openssl rsa -in${DOMAIN}/${DOMAIN}.key -pubout -out ${DOMAIN}/${DOMAIN}.pem echo" (2/3)创建域名证书请求文件" openssl req -new -key ${DOMAIN}/${DOMAIN}.key \ -subj "/C=CN/ST=ZJ/L=HZ/O=test.com/OU=test/CN=*.${DOMAIN}/emailAddress=pritest@test.com" \ -addext "subjectAltName = DNS:*.${DOMAIN}, DNS:${DOMAIN}" \ -out ${DOMAIN}/${DOMAIN}.csr echo" (3/3)使用CA证书给域名的请求文件添加数字签名制作用户证书" openssl x509 -req -CA ca/ca.crt -CAkey ca/ca.key -CAcreateserial -in${DOMAIN}/${DOMAIN}.csr -out ${DOMAIN}/${DOMAIN}.crt -days 36500 echo" 域名私钥: `pwd`/${DOMAIN}/${DOMAIN}.key" echo" 域名公钥: `pwd`/${DOMAIN}/${DOMAIN}.pem" echo" 域名证书请求文件: `pwd`/${DOMAIN}/${DOMAIN}.csr" echo" 域名证书: `pwd`/${DOMAIN}/${DOMAIN}.crt" } show_crt(){ echo echo"域名证书详情" openssl x509 -in${DOMAIN}/${DOMAIN}.crt -noout -text } main(){ make_ca make_crt show_crt } main
参考: