Https协议

words: 2.3k    views:    time: 9min

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


参考: