目录
文章目录
- 目录
- SSL/TLS
- TLS 1.2
- 1. client_hello
- 2. server_hello + server_certificate + sever_hello_done
- 3. Certificate authentication
- 4. client_key_exchange + change_cipher_spec + encrypted_handshake_message
- 5. change_cipher_spec + encrypted_handshake_message
- TLS 1.3
- 更强的安全性
- 加密了整个 TLS Handshake 握手流程
- 使用支持向前保密的临时 Diffie-Hellman 替代 RSA 加密算法
- 更快的访问速度
- 升级 OpenSSL 1.1.1 支持 TLS 1.3
- HTTPS
- HTTPS 单向认证
- HTTPS 双向认证
- TLS SNI(Server Name Indication)
- 抓包示例
- ESNI(加密的 SNI)
- 升级 curl 支持 HTTP2 与 TLS 1.3
- 编译安装
- YUM 升级
- curl 常用选项
- curl 指令使用 SNI
SSL/TLS
SSL(Secure Sockets Layer,安全套接层)协议最初由 Netscape(网景公司)在 1994 年设计并开发,为了给 HTTP 提供一个安全的传输层。
到 1999 年,因为 SSL 应用广泛,已经成为 Internet 上的事实标准。所以 IETF(Internet Engineering Task Force,互联网工程任务组)在 SSL 的基础上将其标准化为 TLS(Transport Layer Security,传输层安全协议)协议。
目前,我们最常见的是 TLS v1.2 版本,而最新的 v1.3(2018 年,RFC8446)版本有望成为有史以来最安全,但也最复杂的 TLS 协议。
TLS 1.2
TLS 协议是 CIAA 网络安全模型的具体实现,基于混合加密方案和 PKI/CA 数字证书技术制定了一套安全通信协议标准,所以理解 TLS 协议之前需要先对 CIAA 安全模型有一定的认识。
TLS 主要由 2 个部分组成:
-
TLS 记录数据(TLS Record):是一种数据结构,用于将应用层协议的数据分割成多个 TLS Records。数据结构的概览如下图所示:
-
TLS 握手协议(TLS Handshake):是一种协议,用于在 TLS 通信的初始阶段进行身份验证和协商安全参数。协议处理流程如下图所示:
协议交互抓包示例如下图所示:
1. client_hello
当 TCP 连接建立后,TLS 握手的第一步由 Client 发起,发送 ClientHello Msg 到 Server。
Client Hello Msg 包含以下内容:
- Client 支持的 TLS 版本。
- Client 支持的 Cipher Suites(加密套件候选列表)。
- Compression Methods(压缩算法候选列表)。
- Extensions(扩展字段)。
- Client Random(随机数),用于后续的对称密钥协商。
- 可选的 Session ID,用于支持 TLS 会话恢复。如果提供了,那么 Server 会复用对应的握手信息,避免短时间内重复握手。
2. server_hello + server_certificate + sever_hello_done
Server 收到了 ClientHello Msg 后,比对 Server 支持的 TLS 版本和 Cipher Suites,然会回复 ServerHello Msg,以此来完成 Cipher Suites 协商阶段。
Server Hello Msg 包含以下内容:
- Server 所能支持的最高 TLS 版本。
- Server 选择使用的 Cipher Suites(加密套件)。例如:Nginx 的
ssl_ciphers HIGH:!aNULL:!MD5;
配置项。 - Server 选择使用的 Compression Method(压缩算法)。
- Server Random(随机数),用于后续的对称密钥协商。
- 可选的 Session ID,用于支持 TLS 会话恢复。如果提供了,则 Client 会复用当前握手的信息,避免短时间内重复握手。
可选的 Session ID 会话恢复,是指在一次完整协商的连接断开时,Client 和 Server 都会将 TLS Session 协商的安全参数保留一段时间,用于后续的会话恢复,起到类似 Cache 的功能。希望恢复会话的 Client 需要将相应的 Session ID 放入 Client Hello Msg 发出。如果 Server 愿意恢复会话,则将相同的 Session ID 放入 Server Hello Msg 返回。
Server Certificate Msg 包含以下内容:
- Server 的 CA 证书,该证书由 CA 签发,是一个由 CA 背书的 Server “公钥“。
- 在双向安全认证场景中,Client 也需要提供它的 CA 证书,还需要包含 Client Certificate Request(客户端证书请求)。
Server Hello Done Msg:向 Client 发送 Server Hello Done 表示 Hello 协商阶段完成。
3. Certificate authentication
Client 收到 Server Hello Msg 后,会对收到的 Server CA 证书进行合法性验证。只有通过验证才会进行后续通信,否则根据错误情况不同做出提示和操作,合法性验证的内容包括:
- Trusted Certificate Path(证书链的可信性)。
- Revocation(证书是否已经吊销):有 2 种方式:离线 CRL 验证、在线 OCSP 验证。不同的 Client 会采用不同的方式。
- Expiry Date(有效期):证书是否在有效时间范围内。
- Domain(域名):核查证书域名是否与当前的访问域名匹配。
4. client_key_exchange + change_cipher_spec + encrypted_handshake_message
Client 完成了 Server CA 证书的合法性验证之后,就可以从 Server CA 证书中得到了 Server Public Key,继而开始非对称加密行为。具体采用的非对称加密算法由 Server 选择的 Cipher Suites 决定,例如:ECDHE。
Client Key Exchange Msg:内含了 Client 本地运算生成的 Pre-Master 随机数,并用 Server Public Key 加密,然后发送给 Server,再由 Server Private Key 解密。
此时,Client 就获得了混合加密方案所需要的全部通信密钥信息,包括:
- 在 C/S 协商阶段获得的 2 个明文随机数 random_C 和 random_S。
- Client 自己生成的加密 Pre-Master 随机数。
- 用于对称加密的 Master Secret 对称密钥,Client 通过
Fuc(random_C, random_S, Pre-Master)
计算得到。使用 3 个随机数来计算,一方面为了防止 “random_C” 被中间人猜出,另一方也增加 Master Secret 的随机性。 - 用于非对称加密的 Server Public Key。
Change Cipher Spec Msg:Client 通知 Server 后续的通信都采用协商的通信密钥和加密算法进行加密通信。
Encrypted Handshake Msg / Finished Msg:TLS v1.2 采用了 MAC(Message authentication code,消息认证码)来确保 Handshake 流程未被篡改。具体的行为是:对 Client 之前已经发送过的所有 Msgs,再次使用协商好的 Master Secret 对称密钥进行 HASH 计算得到数字摘要,最后发送给 Server 用于最后的安全握手验证。
5. change_cipher_spec + encrypted_handshake_message
Server 收到 Client Key Exchange Msg 后,使用 Server Private Key 解密得到 Client 的 Pre-Master 随机数,并基于之前交换的两个明文随机数 random_C 和 random_S,同样可以计算出协商 Master Secret 对称密钥。
Server 收到 Encrypted Handshake Msg 后,同样通过协商好的加密算法进行解密,然后再对 Server 收到的所有 Msgs 使用同样的 Master Secret 对称密钥进行一次数字摘要计算。
最后通过对比数据签名,来最终验证数据的完整性。如果失败,则触发致命的 Alert 异常:Bad Record MAC(Message authentication code,消息认证码),表示安全通道建立过程中有恶意篡改行为。
Change Cipher Spec Msg:验证通过之后,Server 同样发送该消息以告知 Client 后续的通信都采用协商的通信密钥与算法进行加密通信。
Encrypted Handshake Msg / Finished Msg:Server 也会以同样的方式计算出数字签名并发送给 Client。
至此,Client 和 Server 双方都拥有了合法的 Master Secret 对成密钥,接下来进入到业务数据的对称加密安全传输阶段。整个过程通常需要几百毫秒。
TLS 1.3
相对于 TLS v1.2 而言,v1.3 是迄今为止最大的一次改动,主要的改进目的如下:
- 更强的安全性:进一步加密握手流程、改善跨协议攻击的弹性、删除不安全的加密算法。
- 更快的访问速度:减少握手等待时间。
引入的新特性如下:
- 引入了新的 PSK 密钥协商机制。
- 支持 0-RTT 数据传输,在建立连接时节省了往返时间。
- 废弃了过时的 3DES、RC4、AES-CBC 等加密组件,以及 SHA1、MD5 等哈希算法。
- 对 Server Hello Msg 之后的所有握手消息采取了加密操作,可见明文大大减少。
- 不再允许对加密报文进行压缩、不再允许双方发起重协商。
- DSA 证书不再允许使用。
目前最新的 Chrome 和 Firefox 都已支持 TLS 1.3,但需要手动开启。下面是各大浏览器的 TLS 1.3 支持情况:
测试网站是否开启了 TLS 1.3:
$ git clone --depth 1 https://github.com/drwetter/testssl.sh.git
$ cd testssl.sh
$ ./testssl.sh -p <domainName>
Testing protocols via sockets except NPN+ALPN
SSLv2 not offered (OK)
SSLv3 not offered (OK)
TLS 1 offered
TLS 1.1 offered
TLS 1.2 offered (OK)
TLS 1.3 offered (OK): draft 28, draft 27, draft 26
NPN/SPDY h2, spdy/3.1, http/1.1 (advertised)
ALPN/HTTP2 h2, spdy/3.1, http/1.1 (offered)
更强的安全性
加密了整个 TLS Handshake 握手流程
TLS v1.2 中的 Handshake 流程并没有实现完全加密,协商加密算法类型和随机数阶段、以及握手结束(Encrypted Handshake Msg / Finished Msg)都是明文的,通过对称 MAC(Message authentication code,消息认证码)来确保握手未被篡改。
这种疏忽导致了许多备受瞩目的安全漏洞(e.g. FREAK、LogJam etc.),所以在 TLS v1.3 中,会对整个握手进行非对称加密。
以最简单的 FREAK 攻击为例,它利用了以下 2 个漏洞:
- 许多浏览器和服务器仍然支持 20 世纪 90 年代的弱密码(称为 Export 密码)。
- TLS v1.2 用于协商使用哪种加密算法的握手部分没有加密。
使得 FREAK 中间人可以篡改 Client 和 Server 最终选用的 Supported ciphers(加密套件),让双方都降级了加密强度(HIGH => LOW => EXPORT)。然后中间人就可以暴力破解 Encrypted Handshake Message 得到 3 个随机数,并计算出 Master Secret 对称密钥信息 ,最终就可以伪造了彼此的 Finished Message,即 MAC 值。如下图所示。
在 TLS v1.3 中,这种类型的安全降级攻击是不可能的,因为整个握手流程都进行了加密,同时 v1.3 还删除了那些不安全的加密算法,包括:
- RSA 密钥传输:不支持前向安全性。
- CBC 模式密码:易受 BEAST 和 Lucky 13 攻击。
- RC4 流密码:在 HTTPS 中使用并不安全。
- SHA-1 哈希函数:建议以 SHA-2 取而代之。
- 任意 Diffie-Hellman 组:CVE-2016-0701 漏洞。
- 输出密码:易受 FREAK 和 LogJam 攻击。
- 等等。
使用支持向前保密的临时 Diffie-Hellman 替代 RSA 加密算法
RSA 非对称加密算法是由 Rivest,Shamir 和 Adelman 在 1977 年发现的,一直都被视为是密码学领域的重大成就之一。但现在看来,RSA 存在不满足前向保密(Forward Secret)的严重缺陷。即:如果中间人记录存储了加密对话数据,后面假如某一天中间人通过 Heartbleed(心脏出血)之类的技术窃取了 Server 的 RSA Private Key,那么中间人依然可以将对话解密。
因此,TLS 1.3 移除了 RSA,而仅采用了临时 Diffie-Hellman 作为唯一的秘钥交换机制。
临时 Diffie-Hellman 由 Diffie 和 Hellman 在 1976 年发明,要求 Client 和 Server 都创建一对非对称密钥,并且都交换彼此的 Public Key,一旦收到了对方的 Public Key,那么就会与自己的 Private Key 进行组合,最后以相同的 Pre-Master Secret 值作为结尾。
所谓 “临时”,指的是在每个 TLS 会话中,协商密钥所使用的 Pre-Master Secret 参数都是临时生成的,可以实现每个会话的密钥唯一。因此,即使在以后的时间里,攻击者获得了以前的临时密钥,也无法利用这些密钥来破解之前或之后的会话。即使一个密钥被泄漏,也不会影响其他会话的安全性。
更快的访问速度
在 Web 领域,传输延迟(Transmission Latency)是重要的性能指标之一,低延迟意味着更流畅的页面加载以及更快的 API 响应速度。而一个完整的 HTTPS 连接的建立大概需要以下 4 步:
- DNS 查询
- TCP 握手(1 RTT)
- TLS v1.2 握手 (2 RTT)
- 建立 HTTP 连接(1 RTT)
可见在 TLS v1.2 中,新建一个完整的 HTTPS 连接最少需要 4 个 RTT(Round-Trip Time 往返时延),而重连则可以通过 TLS 的会话恢复机制节省 1 个 RTT。
- TLS v1.2 新建连接握手流程:
- TLS v1.2 会话恢复流程(指定了 Hello Msg 的 Session ID):
在 TLS v1.3 中,由于仅支持向前保密的临时 Diffie-Hellman 对称加密算法,所以 Client 可以在一条 Msg 中就完成 Diffie-Hellman 密钥共享,即:只需要一次往返( 1-RTT )就可以完成握手。
- TLS v1.3 新建连接握手流程:
另外,TLS v1.3 在会话恢复时,Client 会将 Server 发送过来的 Session Ticket 进行计算,组成一个新的 PSK (PreSharedKey,预共享密钥)。Client 将 PSK 缓存在本地。会话恢复时,在 Client Hello Msg 带上 PSK 扩展,同时通过之前 Client 发送的 Finished Msg 计算出 Resumption Secret(恢复密钥)。通过该密钥加密数据发送给 Server,然后 Server 就会从 Session Ticket 中算出 PSK,使用它来解密刚才发过来的加密数据。至此完成了该 0-RTT 会话恢复的过程。
- TLS v1.3 0-RTT 会话恢复流程:
升级 OpenSSL 1.1.1 支持 TLS 1.3
并非所有的 Client 和 Server 都支持相同版本的 TLS,因此大多数 Server 都会同时支持多个版本,并且进行协商。TLS 的版本协商非常简单。Client 会通知 Server 它支持的协议的最新版本,Server 则会回复支持的协议版本,如果存在交集则协商成功。否则,连接失败。虽然版本协商的过程很简单,但事实证明,很多连接场景并未能正确地实现这一功能,从而导致安全事故。
OpenSSL 最新的 1.1.1 版本提供了 TLS 1.3 的支持,而且和 1.1.0 版本完全兼容。在特定的 Linux 发行版中,可能需要手动安装。
以 CentOS7 为例,检查是否开启了 TLS 1.3:
$ openssl s_client --help | grep tls1_3
如果没有,则需要手动安装:
- 下载 OpenSSL 1.1.1 版本
$ cd /opt
$ wget https://github.com/openssl/openssl/archive/OpenSSL_1_1_1-stable.zip
$ unzip OpenSSL_1_1_1-stable.zip
- 编译安装
$ ./config enable-tls1_3 --prefix=/usr/local/openssl
$ make && make install
- 配置
$ mv /usr/bin/openssl /usr/bin/openssl.old
$ mv /usr/lib64/openssl /usr/lib64/openssl.old
$ mv /usr/lib64/libssl.so /usr/lib64/libssl.so.old
$ ln -s /usr/local/openssl/bin/openssl /usr/bin/openssl
$ ln -s /usr/local/openssl/include/openssl /usr/include/openssl
$ ln -s /usr/local/openssl/lib/libssl.so /usr/lib64/libssl.so
$ echo "/usr/local/openssl/lib" >> /etc/ld.so.conf
$ ldconfig -v
- 查看版本并验证
$ openssl version
$ openssl s_client --help | grep tls1_3
HTTPS
HTTPS 就是 HTTP 与 TLS 的组合,本质为 HTTP over SSL/TLS。在以往的文章中,我们已经分别介绍了 HTTP 和 TLS 协议,所以在这里主要关注 HTTPS 的 2 种安全认证方式,并梳理 HTTPS 连接建立流程。
HTTPS 单向认证
- Client 向 Server 发送 TLS 协议版本号、加密算法种类、随机数等信息。
- Server 给 Client 返回 TLS 协议版本号、加密算法种类、随机数等信息,同时也返回 Server 的 CA 证书。
- Client 使用 Server 返回的信息验证 CA 证书的合法性,包括:
- 证书是否过期。
- 签发证书的 CA 机构是否可靠。
- 安装的 CA 公钥证书是否能正确解开 CA 证书并返回数字签名。
- CA 证书上的 DN(域名)是否和 Server 的实际域名相匹配。
- 验证通过后,将继续进行通信,否则,终止通信。
- Client 向 Server 发送自己所能支持的对称加密算法,供 Server 进行选择。
- Server 端在 Client 提供的加密方案中选择加密程度最高的方式。
- Server 将选择好的加密方案通过明文方式返回给 Client。
- Client 接收到 Server 返回的加密方式后,使用该加密方式生成产生 Pre-Master 随机码,使用 Server Public Key 进行加密,并发送至 Server。
- Server 收到 Client 发来的加密信息后,使用自己的 Private Key 进行解密,获取 Pre-Master 随机码,并计算出 Master 对称密钥。
- 在接下来的会话中,Server 和 Client 将会使用 Master 对称密钥进行对称加密,保证通信过程中信息的安全。
HTTPS 双向认证
双向认证和单向认证类似,主要是额外增加了 Server 对 Client 的认证。如下图红色部分。
TLS SNI(Server Name Indication)
SNI(Server Name Indication,服务器名称指示,RFC 4366)是一项用于改善 TLS 在 HTTPS 场景中应用的技术,它允许 Client 在发起 TLS 握手请求时(Client Hello Msg),同时提交请求的 HTTP Hostname 信息,使得支持 VirtualHosts 的 HTTP Server 能够切换到对应的 Domain 并返回正确的 CA 证书。
早期的 SSLv2 根据经典的 PKI 标准进行设计,它默认认为:一台 HTTP Server(或者说一个 IP 地址)只会提供一个服务(只有一个 Domain Name),所以在 SSL 握手时,Client 无需指明具体的 Hostname。然而在 Apache 等 HTTP Server 中应用了 VirtualHosts 之后,就出现了一个 IP 会对应多个 Domain Name 的情况。为了支持 VirtualHosts,HTTP/1.1 Header 协议增加了 Host 字段,相应的 TLS 也需要类似的手段,这就是 SNI,它使得共享同一个 IP 地址的 Virtual HTTP Server 也可建立 TLS 连接。
所以,当多个 Web 网站(具有不同的 Domain Names)被托管在同一台 HTTP Server 上并共享一个 IP 地址时,、并且每个网站都有自己 Domain Name 的 TLS 证书时,客户端尝试 HTTPS 连接到其中的一个网站时,Web 服务器就很可能不知道应该显示(发送)哪个 TLS 证书给客户端了。
因为 TLS 握手会发生在客户端通过 HTTP Header host
来指示连接到某个具体的网站之前。典型的例子就是显示指定 HTTP Header host
无效:
$ curl -vvv --cacert /root/nginx-cert/ca_01.pem -X GET "https://127.0.0.1:443/" -H "host: web.apigw.com"
curl: (51) Unable to communicate securely with peer: requested domain name does not match the server's certificate.
SNI 就是为了解决这个问题,SNI 是 TLS 协议的扩展,它包含在 TLS 握手流程中,以确保客户端能够接收到他们尝试访问的网站的 TLS 证书。SNI 扩展使得可以在 TLS 握手期间指定网站的 Domain Name,而不是在 TLS 握手之后建立 HTTP 连接时指定(实际上也走不到 TLS 握手之后的流程)。所以 SNI 称之为 “服务器名称指示”,即:指示此次 TLS 请求的目的 Domain Name(通常是 Web 服务器的虚拟主机名)。
举个例子:网站 https://www.example.com、https://www.something.com、https://www.another-website.com、https://www.example.io 被托管在相同的 Web 服务器中(相同的 IP 地址),若 SNI 扩展指定为 https://www.example.com,那么 Web 服务器就会返回 https://www.example.com 的 TLS 本地设备证书到客户端,继而建立正确的安全的连接。
SNI 可以防止所谓的 “公共名称不匹配错误” 问题,即:虽然客户端到达了网站的正确 IP 地址,但由于 TLS 证书上的 Domain Name 与网站 Domain Name 不匹配导致无法建立安全连接。通常的,这种错误会导致用户浏览器中出现 “您的连接缺乏隐私性” 错误消息。
要使用 SNI,需要客户端和服务器端同时满足条件,幸好对于现代浏览器来说,大部分都支持 SSLv3/TLSv1,所以都可以享受 SNI 带来的便利。
抓包示例
- 发出 SNI
SSLKEYLOGFILE=ssl_log.txt curl \
--cacert ~/ca_01.pem \
--resolve www.app1.com:443:172.18.22.68 \
-X GET "https://www.app1.com:443/" \
-H 'Content-type: application/json' \
-H 'Accept: application/json' \
-H 'host: www.app1.com'
-
通信流程
-
Client Hello Extensions
ESNI(加密的 SNI)
SNI 是 TLS 协议的补充,它告诉 Web Server 在 Client 与 Server 之间开始建立 HTTPS 连接时显示哪个 TLS 证书。SNI 使 Web Server 可以在同一个 IP 地址上托管多个 TLS 证书。
SNI 作为 TLS client_hello 消息中的一部分,Client 以此来指定要查看的 Web Server 的 TLS 证书。Server 响应时,将发送指定的证书。矛盾的是,只有在使用 SNI 成功完成第一次 TLS 握手后,才能进行后续的加密。结果,由于 client_hello 消息是在 TLS 握手开始时发送的,所以,普通的 SNI 不会被加密。
也就是说,任何监视 Client 和 Server 之间连接的攻击者都可以读取到 TLS 握手的 SNI 部分,以此了解到 Client 正在与哪个网站建立 HTTPS 连接,即便攻击者无法解密进一步通信。攻击者可以通过多种方式使用此信息,例如:建立一个钓鱼网站来欺骗用户。
由此,就出现了 SNI 加密需求 — ESNI(加密的 SNI),通过加密 client_hello 消息的 SNI 部分(仅此部分),来保护 SNI 的私密性,确保攻击者无法监视到 SNI 明文信息并以此确定用户正在访问哪些网站。
加密仅在通信双方都有用于加密和解密信息的密钥时才起作用,就像两个人只有在都有储物柜密钥时才能使用同一储物柜一样。由于客户端问候消息是在客户端和服务器协商 TLS 加密密钥之前发送的,因此 ESNI 加密密钥必须以其他方式进行传输。
解决方案:公钥加密。Web Server 在其 DNS 记录中添加一个公钥,这样,当 Client 查找到正确的服务器 IP 地址时,同时也能找到该服务器的公钥。这有点像将房门钥匙放在屋外的密码箱中,以便访客可以安全地进入房屋。然后,Client 即可使用公钥来加密 SNI 记录,以便只有特定的 Server 才能解密它。
例如:当用户在浏览器中输入 URL:https://www.bobisawesome.example.com 时,浏览器将通过以下流程加载网站:
- 笔记本电脑向 DNS Server 发送 Domain Name 查询,以查询 Web Server 的 IP 地址。
- DNS 响应 IP 地址,DNS 响应中还包括了 Web Server 的 ESNI 公钥。
- 笔记本电脑向指定的 IP 地址发送 TLS client_hello 消息,并使用 Web Server 的 ESNI 公钥对 SNI 部分进行加密。
- Web Server 根据 SNI 指示返回指定的 TLS 证书。
- TLS 握手继续进行。
注意:有了 ESNI 也并非绝对的安全,因为普通的 DNS 未加密,这意味着任何人都可以看到某人正在查找哪个地址,并且任何人都可以伪装成地址簿。即使安装了 ESNI,攻击者仍然可以查看用户正在查询的 DNS 记录,并确定他们正在访问哪些网站。
所以,你可能还需要:基于 TLS 的 DNS、基于 HTTPS 的 DNS,以及 DNSSEC。基于 TLS 的 DNS 和基于 HTTPS 的 DNS 都在做同样的事情:使用 TLS 加密来加密 DNS 查询。DNSSEC 则用于确认 DNS 记录真实并来自合法的 DNS 服务器,而非来自冒充 DNS 服务器的攻击者。
升级 curl 支持 HTTP2 与 TLS 1.3
编译安装
安装编译环境:
yum -y groupinstall "Development Tools"
yum -y install libev libev-devel zlib zlib-devel openssl openssl-devel git
安装 OpenSSL:
mkdir /var/tmp
cd /var/tmp
wget https://openssl.org/source/openssl-1.0.2.tar.gz
tar -zxf openssl-1.0.2.tar.gz
cd openssl-1.0.2
mkdir /opt/openssl
./config --prefix=/opt/openssl
make
make test
make install
安装 nghttp2:
git clone https://github.com/tatsuhiro-t/nghttp2.git
cd nghttp2
autoreconf -i
automake
autoconf
./configure
make
make install
echo '/usr/local/lib' > /etc/ld.so.conf.d/custom-libs.conf
ldconfig
ldconfig -p| grep libnghttp2
安装 curl:
cd /var/tmp
git clone https://github.com/bagder/curl.git
cd curl
./buildconf
./configure --with-ssl=/opt/openssl --with-nghttp2=/usr/local --disable-file --without-pic --disable-shared
make
验证:
$ /var/tmp/curl/src/curl --version
curl 7.70.0-DEV (x86_64-unknown-linux-gnu) libcurl/7.70.0-DEV OpenSSL/1.0.2o nghttp2/1.41.0-DEV
Release-Date: [unreleased]
Protocols: dict ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS HTTP2 HTTPS-proxy IPv6 Largefile NTLM NTLM_WB SSL TLS-SRP UnixSockets
注意:curl 从 7.52.0 版本开始也已经支持 TLS 1.3 了,curl 7.61.0 及以上在 TLS 握手过程中协商 TLS 版本时,curl 默认使用 TLS 1.3,但也取决于 curl 正在使用的 TLS 库及其版本,例如:要求 OpenSSL 1.1.1 版本以上。
YUM 升级
安装新版 libcurl 的 yum 源:
rpm -ivh http://mirror.city-fan.org/ftp/contrib/yum-repo/city-fan.org-release-1-13.rhel6.noarch.rpm
升级:
yum upgrade libcurl
升级完成后可以卸载此 yum 源:
rpm -e city-fan.org-release
curl 常用选项
语法格式:
curl [options] [URL...]
常用选项如下所示:
-A/--user-agent <string>:
设置用户代理发送给服务器
-e/--referer <URL>:
来源网址
--cacert <file>:
CA 证书(SSL)
-k/--insecure:
允许忽略证书进行 SSL 连接
--compressed:
要求返回是压缩的格式
-H/--header <line>:
自定义首部信息传递给服务器
-i:
显示页面内容,包括报文首部信息
-I/--head:
只显示响应报文首部信息
-D/--dump-header <file>:
将 URL 的 header 信息存放在指定文件中
--basic:
使用 HTTP 基本认证
-u/--user <user[:password]>:
设置服务器的用户和密码
-L:
如果有 3xx 响应码,重新发请求到新位置
-O:
使用 URL 中默认的文件名保存文件到本地
-o <file>:
将网络文件保存为指定的文件中
--limit-rate <rate>:
设置传输速度
-0/--http1.0:
数字 0,使用 HTTP 1.0
-v/--verbose:
更详细
-C:
选项可对文件使用断点续传功能
-c/--cookie-jar <file name>:
将 URL 中 Cookie 存放在指定文件中
-x/--proxy <proxyhost[:port]>:
指定代理服务器地址
-X/--request <command>:
向服务器发送指定请求方法
-U/--proxy-user <user:password>:
代理服务器用户和密码
-T:
选项可将指定的本地文件上传到 FTP 服务器上
--data/-d:
方式指定使用 POST 方式传递数据
-b name=data:
从服务器响应 set-cookie 得到值,返回给服务器
curl 指令使用 SNI
由以上过程可以知道,没有 SNI 的情况下,服务器无法预知客户端到底请求的是哪一个域名的服务。SNI 的 TLS 扩展通过发送虚拟域名做为 TLS 协商的一部分修正了这个问题,在 Client Hello 阶段,通过 SNI 扩展,将域名信息提前告诉服务器,服务器根据域名取得对应的证书返回给客户端已完成校验过程。
curl 7.18.1+ & openssl 0.9.8j+ 的组合就可以支持 SNI 了:
curl \
--cacert /root/CA/nginx1.com/cacert.pem \
-X GET "https://webserver.com:8443/" \
-H 'Content-type: application/json' \
-H 'Accept: application/json' \
-H 'host: nginx1.com'
如果没有配置 DNS 解析的话可以使用 curl 7.21.3 支持的 --resolve 参数:
curl \
--cacert /root/CA/nginx1.com/cacert.pem \
--resolve webserver.com:8443:127.0.0.1 \
-X GET "https://webserver.com:8443/" \
-H 'Content-type: application/json' \
-H 'Accept: application/json' \
-H 'host: nginx1.com'
–resolve 主要用于直接定位到 IP 地址进行访问,对于一个 Domain Name 有多个服务器(多个不同的 IP)的服务来说,这个参数可以指定的访问某个设备。