工作中一次机缘巧合下,碰到了一次使用curl请求第三方接口报错,日志中记录报错信息是ssl connect error
。这套请求的代码是一个公共方法,调用其他第三方接口的时候并没有异常,但是这次却遇到了这样的问题,第三方那边也没有提供有用的信息,因此只能自己排查了。最终排查的结果是使用ssl进行握手的时候,我方服务器上支持的加密算法没有一个与第三方服务器支持的加密算法匹配上,因此最后只能对我方服务器上的NSS(与OpenSSL一样是是提供SSL/TLS的开源实现,RedHat服务器的curl默认使用NSS,所以我方服务器一直是NSS)进行升级,这里记录一下SSL/TLS协议的握手过程。
HTTPS建立连接的过程
通过openssl工具查看https的握手过程,以百度为例子,通过命令openssl s_client -connect www.baidu.com:443 -state
可以看到如下信息:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22CONNECTED(00000003)
SSL_connect:before/connect initialization
SSL_connect:SSLv2/v3 write client hello A
SSL_connect:SSLv3 read server hello A
depth=2 C = BE, O = GlobalSign nv-sa, OU = Root CA, CN = GlobalSign Root CA
verify return:1
depth=1 C = BE, O = GlobalSign nv-sa, CN = GlobalSign Organization Validation CA - SHA256 - G2
verify return:1
depth=0 C = CN, ST = beijing, L = beijing, OU = service operation department, O = "Beijing Baidu Netcom Science Technology Co., Ltd", CN = baidu.com
verify return:1
SSL_connect:SSLv3 read server certificate A
SSL_connect:SSLv3 read server key exchange A
SSL_connect:SSLv3 read server done A
SSL_connect:SSLv3 write client key exchange A
SSL_connect:SSLv3 write change cipher spec A
SSL_connect:SSLv3 write finished A
SSL_connect:SSLv3 flush data
SSL_connect:SSLv3 read server session ticket A
SSL_connect:SSLv3 read finished A
---
.....
以上信息可以看出SSL/TLS握手的过程,下面将讲述客户端和服务端发送的消息
The Hello Exchange
Client Hello
由客户端发送到服务端,包含以下信息:
Protocol Version
:客户端希望使用的SSL/TLS版本Random
:客户端生成的一个随机数(《SSL Introduction with Sample Transaction and Packet Exchange - Cisco》中没有提及到,不过可以在其截图中看到Random参数)Session ID
:客户端希望这次连接使用的session的ID,第一次通讯为空Cipher Suite
:客户端支持的加密算法
Server Hello
服务端接收到客户端的client hello
之后的回复,包含以下信息
Protocol Version
最终选定的SSL/TLS版本Session ID
:如果客户端发送的client hello
带有有效的Session ID
,而且服务端在cache中存在此ID,则返回此Session ID
,否则创建一个新的Session ID
返回。Cipher Suite
:服务端从客户端支持的加密算法中选择其中一种加密算法。Compression Method
:服务端从客户端支持的压缩方式中选择其中一种。Extensions
:可选,客户端可以提供的参数,可以用于服务端验证客户端的身份。
Server Certificate, Server Key Exchange, and Certificate Request (Optional)
Server Certificate
:服务端的证书,跟随在Server Hello
消息之后。Server Key Exchange
:当服务端没有证书或者证书中没有足够多的信息让客户端进行premaster secret
的交换的时候发送此参数- 需要
Server Key Exchange
的交换算法:DHE_DSS、DHE_RSA、DH_anon - 不需要
Server Key Exchange
的交换算法:RSA、DH_DSS、DH_RSA
- 需要
Certificate Request
:服务端可以要求客户端的证书
Server Hello Done
服务端在发送Server Hello
之后发送Server Hello Done
,表示消息结束。客户端在接收此消息之前,会验证服务端的证书,还有Server Hello
的参数(如果需要)
Client Exchange
Client Certificate (可选)
客户端接收到Server Hello Done
之后发送的消息,只有当服务端要求的时候才发送,如果没有合适的证书,则发送no_certificate
。服务端可以根据自身设定中断握手,或者继续执行。
Client Key Exchange
这个参数的内容,与The Hello Exchange
阶段时候客户端与服务端协商的交换算法有关系
- RSA:客户端会创建一个48-byte的
premaster secret
,使用服务端的公钥对其进行加密,服务端使用私钥解密出premaster secret
,然后双方将premaster secret
转换成master secret
(转换方式见Computing the Master Secret) - Diffie-Hellman:传送的是一个
public value
(简称Yc),而服务端的证书中也会附带有用于DH算法的参数,客户端与服务端可以计算出相同的premaster secret
,然后转换成master secret
。pre_master_secret转换成master_secret
master_secret = PRF(pre_master_secret, “master secret”, ClientHello.random + ServerHello.random)[0..47];
Certificate Verify (可选)
当服务端需要验证客户端的时候需要带上此参数。
Cipher Change
Change Cipher Spec Protocol
加密策略变更协议存在,表示加密策略的变更。这个协议包括一个消息,用当前等加密策略来加密和压缩。客户端和服务端双方都需要发送变更消息,用于通知接收方,接下来的内容将会使用新的加密策略和key进行处理。发送了加密策略变更消息一方,发送忙马上使用新的加密策略处理写出的数据,而接收到加密策略变更消息的一方,马上使用新的加密策略来出路读取的数据。
- 注意:如果在接下来发生了重新握手,通讯放也许会使用旧的加密策略。然而只要加密策略变更消息发送之后,就必须使用新的加密策略。最先发送加密策略变更消息的一方,不知道另一方是否已经完成了新密钥的生成(如果由于公钥操作太耗时间,可能会比较慢)。因此有会有一个很小的时间窗口,接收方会缓存接收到的数据。
Finished Messages
当发送完加密策略变更消息之后发送此消息,为了校验密钥交换与认证过程是否成功。加密策略变更消息的接收方的时机必须在其他握手消息与Finished Message
之间。Finished Message
是使用新的加密方式、新的算法和新的密钥进行加密的第一条消息,接收方需要校验信息是否正确。当一方发送了Finished Message
、接收到了Finished Message
并且校验接收到的Finished Message
之后,就可以开始发送或者接收应用层的消息了。