☰
HTTPS加密原理与TLS握手全解析:从证书体系到部署配置
2026/10/19 8:04:02 网站建设 项目流程

1. 为什么HTTPS值得单独拿出来讲

做后端开发这些年,被问到最多的问题里,“HTTPS到底是怎么加密的”绝对排得进前三。很多人能说出“用了证书”“非对称加密”“对称加密”这几个词,但再往下追问一句“那证书里到底装了什么”“为什么抓包抓不到内容”,就卡住了。这个系列写到第十五篇,前面把TCP、UDP、HTTP、DNS这些基础协议都过了一遍,轮到HTTPS的时候,我觉得有必要把它彻底拆开讲清楚,因为它不是一个单独的协议,而是HTTP加上一层安全传输机制之后的产物,是当前所有Web服务的默认形态。

这篇文章面向的是有一定网络基础、写过HTTP接口、但没系统梳理过HTTPS内部机制的开发者。我会从设计动机讲起,把握手过程、证书体系、加密算法协商、实际部署配置这几个环节逐一拆解,最后给出可以直接参考的配置方案和排查思路。读完你应该能做到:看到一个HTTPS连接,脑子里能自动跑出它从建立到传输的完整流程;遇到证书报错、握手失败这类问题,知道从哪里下手排查;自己部署服务的时候,能配出一套合理的参数。

需要先明确一个前提:HTTPS不是一种全新的协议,它是HTTP over TLS。HTTP负责语义——请求方法、状态码、头部字段这些;TLS负责安全——加密、完整性校验、身份认证。两者是叠加关系,不是替代关系。理解这一点,后面很多设计上的取舍就顺了。

2. HTTPS的整体设计与核心思路拆解

2.1 明文HTTP到底缺了什么

要理解HTTPS为什么长成现在这样,得先看清楚HTTP裸奔的时候到底暴露了哪些问题。我把它归纳成三个层面。

第一是机密性缺失。HTTP报文在网络上是以明文形式传输的,中间任何一个节点——路由器、交换机、代理服务器——都能直接看到你发了什么、收了什么。早期大家觉得“反正也没什么秘密”,但后来登录表单、支付信息、个人数据全走HTTP,这就等于把内容写在明信片上寄出去。

第二是完整性缺失。明文传输意味着中间人不仅能看,还能改。你在页面上看到的可能是被篡改过的内容,比如插入的广告、替换的下载链接,而浏览器和用户都无从察觉。

第三是身份不可验证。你访问的到底是不是你以为的那个服务器?在HTTP世界里,这个问题没有答案。DNS可以被污染,IP可以被冒充,客户端没有任何手段去确认对面站点的真实身份。

这三个问题对应到TLS的设计目标,就是加密、完整性校验、身份认证。HTTPS的所有机制,本质上都是在解决这三件事。

2.2 为什么不是“全程非对称加密”

刚接触HTTPS的人常有一个疑问:既然非对称加密能解决密钥分发问题,为什么不干脆全程用它?

原因很直接:性能。非对称加密(比如RSA)的运算开销比对称加密(比如AES)高出好几个数量级。用它来加密大量数据,CPU根本扛不住。我做过一个粗略的测试,同样加密1MB数据,RSA的处理时间大概是AES的几百倍。在HTTPS这种需要承载大量并发请求的场景下,全程非对称加密完全不现实。

所以TLS的设计思路是:用非对称加密来安全地协商出一个对称密钥,然后用这个对称密钥来加密实际的数据传输。非对称加密只在握手阶段用一次,负责“把钥匙安全地交给对方”;之后的通信全部走对称加密,兼顾安全和性能。这个思路是整个HTTPS的核心,理解了它,握手流程就很好懂了。

2.3 证书体系解决的是“信任从哪来”

对称密钥协商好之后,还有一个问题没解决:你怎么知道跟你协商密钥的对方,真的是你想访问的那个服务器?

这就是证书体系要干的事。证书的本质是一份经过权威机构签名的公钥声明,它说:“我,某权威机构,证明这个公钥属于这个域名。”客户端内置了一批它信任的权威机构(根证书),通过这些根证书去验证服务器证书的签名链,最终确认对方身份。

这里的关键在于信任的起点。客户端不可能凭空信任任何服务器,它必须信任一些预置的根证书。这些根证书是操作系统或浏览器出厂时就带好的,是整个信任链的锚点。服务器证书由中间证书签发,中间证书由根证书签发,形成一条链,客户端从服务器证书一路验到根证书,链条完整且签名有效,才认为对方可信。

注意:证书验证的是“这个公钥确实属于这个域名”,而不是“这个服务器是好人”。证书只能证明身份,不能证明意图。一个钓鱼网站也可以申请到合法证书,只是它的域名和真实网站不一样。

3. TLS握手过程的核心细节与实操要点

3.1 握手到底在交换什么

TLS握手是整个HTTPS里最复杂的部分,但拆开看,它就是在完成四件事:协商算法、验证身份、交换密钥、确认握手完整性。

我用TLS 1.2的经典握手流程来说明,因为它的步骤最清晰,理解了它再看TLS 1.3的优化就很容易。

客户端先发ClientHello,里面包含:支持的TLS版本、支持的加密套件列表、一个客户端随机数、支持的扩展(比如SNI,用来指明要访问的域名)。服务器回ServerHello,选定TLS版本和加密套件,给出服务器随机数。然后是Certificate消息,服务器把证书链发过来。接着是ServerKeyExchange(如果选的密钥交换算法需要额外参数的话),再是ServerHelloDone,表示服务器这边说完了。

客户端收到后,先验证证书链,确认服务器身份。验证通过后,生成一个预主密钥,用服务器证书里的公钥加密后发过去(这是RSA密钥交换的情况)。服务器用自己的私钥解密拿到预主密钥。双方用客户端随机数、服务器随机数、预主密钥三个值,通过一个约定的算法算出主密钥,再派生出会话密钥。

最后双方各发一个Finished消息,用会话密钥加密一段之前所有握手消息的摘要,验证握手过程没有被篡改。Finished验证通过,握手完成,开始用会话密钥加密应用数据。

3.2 加密套件怎么选

加密套件(Cipher Suite)决定了握手和传输用哪些具体算法。一个典型的套件名字长这样:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256。它由四部分组成:密钥交换算法(ECDHE)、身份认证算法(RSA)、对称加密算法(AES_128_GCM)、消息认证算法(SHA256)。

选套件有几个原则。优先选支持前向保密的,也就是密钥交换用ECDHE或DHE而不是纯RSA。前向保密的意思是:即使服务器的私钥以后泄露了,之前抓到的历史流量也无法解密,因为每次会话的密钥是临时协商的,不依赖长期私钥。对称加密优先选AES_GCM或ChaCha20_Poly1305,这两种都是AEAD算法,同时提供加密和完整性校验,比老的CBC模式更安全也更高效。哈希算法选SHA256及以上,SHA1已经被认为不安全了。

实际配置的时候,我会把套件列表按优先级排好,让服务器优先选前向保密+AEAD的组合。下面是一个参考配置:

ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305'; ssl_prefer_server_ciphers on;

这个列表里全是ECDHE开头的,保证了前向保密;加密部分全是GCM或ChaCha20,保证了AEAD;哈希全是SHA256或SHA384,强度足够。ssl_prefer_server_ciphers on让服务器按自己的优先级选,而不是听客户端的,这样可以确保选出来的套件是服务器认为最安全的。

3.3 证书链的验证逻辑

客户端验证证书的时候,不是只看服务器证书本身,而是要验证整条链。服务器证书通常由中间证书签发,中间证书由根证书签发。客户端需要拿到中间证书才能把链补全,所以服务器在握手时要把中间证书一起发过来。

验证过程是这样的:客户端拿服务器证书里的签发者信息,去中间证书里找对应的公钥,用这个公钥验证服务器证书的签名。然后再拿中间证书的签发者信息,去根证书里找对应的公钥,验证中间证书的签名。根证书是自签名的,客户端直接信任它。整条链验证通过,且服务器证书里的域名和实际访问的域名匹配,证书在有效期内,没有被吊销,才算验证成功。

这里有个常见的坑:服务器只发了服务器证书,没发中间证书。浏览器可能因为缓存了中间证书而正常显示,但一些客户端(比如某些语言的HTTP库、命令行工具)会因为链不完整而报错。所以部署的时候一定要确认证书链是完整的。

实操心得:用openssl s_client -connect example.com:443 -showcerts可以查看服务器实际发送的证书链。如果只看到一张证书,那大概率是缺中间证书了。

4. 从TLS 1.2到TLS 1.3的演进与实操配置

4.1 TLS 1.3砍掉了什么

TLS 1.3相比1.2做了大幅简化,核心思路是砍掉所有不安全的选项,把握手轮次压到最少。

它删掉了RSA密钥交换,只保留前向保密的密钥交换方式。删掉了CBC模式、RC4、SHA1这些老算法。删掉了重协商、压缩这些容易出问题的特性。结果是TLS 1.3的握手只需要一个往返(1-RTT),而TLS 1.2需要两个往返。如果利用会话恢复,TLS 1.3甚至能做到0-RTT,也就是客户端在第一个消息里就带上加密的应用数据。

握手流程也变了。客户端发ClientHello的时候,直接带上密钥共享(key_share),服务器回ServerHello的时候也带上自己的密钥共享,双方立刻就能算出会话密钥。然后服务器把证书、Finished一起发过来,客户端验证完发自己的Finished,握手就结束了。整个过程比1.2清爽很多。

4.2 实际部署怎么配

现在主流服务器都支持TLS 1.3了,配置上建议同时开启1.2和1.3,因为还有少量老客户端不支持1.3。等确认所有客户端都升级了,再考虑只留1.3。

Nginx的配置参考:

ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305'; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; ssl_session_tickets off;

ssl_session_cache开启会话缓存,让后续连接可以复用会话,减少握手开销。ssl_session_tickets off是关掉会话票据,因为票据的密钥管理比较麻烦,用缓存就够了。

证书方面,建议用ECDSA证书而不是RSA证书,因为ECDSA的密钥更短、握手更快。如果考虑兼容性,可以同时部署RSA和ECDSA两张证书,让服务器根据客户端支持情况自动选。

4.3 HSTS的作用和配置

HSTS(HTTP Strict Transport Security)是一个响应头,告诉浏览器“以后访问这个域名,一律用HTTPS,不要走HTTP”。它的作用是防止降级攻击——攻击者把HTTPS连接降级成HTTP,从而窃取信息。

配置很简单,在服务器返回的响应里加上:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

max-age是告诉浏览器记住这个策略多久,单位是秒,31536000就是一年。includeSubDomains表示所有子域名也适用。always确保所有响应都带上这个头。

注意:HSTS一旦生效,浏览器在max-age时间内会强制用HTTPS访问,如果证书出了问题,用户连“继续访问”的选项都没有。所以配置之前一定要确认HTTPS已经稳定运行,证书续期机制也没问题。

5. 常见问题与排查技巧实录

5.1 证书报错的几种典型情况

证书问题是最常见的HTTPS故障,我整理了一个速查表:

报错信息可能原因排查方向
证书已过期证书超过有效期检查证书有效期,及时续期
证书域名不匹配证书里的域名和访问的域名不一致确认证书覆盖的域名列表
无法验证证书链缺少中间证书用openssl检查服务器发送的证书链
自签名证书使用了自签名证书客户端需要手动信任或换正式证书
证书已被吊销证书被CA吊销检查吊销列表或OCSP状态

排查证书问题,我习惯先用openssl s_client连上去看详细信息:

openssl s_client -connect example.com:443 -servername example.com

这个命令会输出服务器证书、证书链、协商的协议版本和加密套件。看输出里的Verify return code,如果是0就是验证通过,非0就是有问题,后面会跟具体原因。

5.2 握手失败的排查思路

握手失败的原因比较多,可能是协议版本不匹配、加密套件不匹配、证书问题、网络中间设备干扰等。

排查的时候,我会先用openssl s_client指定不同的协议版本和套件去试,看是哪一步失败。比如:

openssl s_client -connect example.com:443 -tls1_2 openssl s_client -connect example.com:443 -tls1_3

如果某个版本能通,另一个不能,那就是协议版本的问题。如果都通不了,再看证书和套件。

还有一种情况是客户端和服务端都支持某个套件,但中间设备不支持。比如某些老旧的负载均衡器可能不支持TLS 1.3的某些特性,导致握手在中间被中断。这种情况需要抓包分析,看握手是在哪一步断的。

5.3 性能优化的几个实操点

HTTPS的性能开销主要在握手阶段,优化也从这里入手。

开启会话复用是最直接的手段。TLS 1.2用Session ID或Session Ticket,TLS 1.3用PSK(预共享密钥)。会话复用可以让后续连接跳过完整的握手,直接复用之前的会话密钥,大幅降低延迟。

启用OCSP Stapling可以减少客户端验证证书吊销状态的时间。正常情况下,客户端需要自己去CA的OCSP服务器查询证书状态,这个查询有延迟。OCSP Stapling让服务器把OCSP响应缓存下来,握手时一起发给客户端,客户端就不用自己去查了。

ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate /path/to/ca-bundle.crt;

用ECDSA证书比RSA证书握手更快,因为ECDSA的密钥短、运算快。如果客户端支持,优先用ECDSA。

开启TLS 1.3的0-RTT可以进一步降低延迟,但0-RTT有重放攻击的风险,只适合幂等的请求。实际使用要谨慎评估。

5.4 抓包分析HTTPS流量的正确姿势

调试HTTPS的时候,直接抓包看到的是加密后的数据,没法分析。有两种办法可以解密。

一种是在客户端或服务器上配置SSLKEYLOGFILE,让TLS库把会话密钥写到一个文件里,Wireshark读取这个文件就能解密流量。这个方法需要能控制客户端或服务器,适合调试自己的服务。

另一种是用中间人代理,代理自己生成证书,客户端信任代理的证书,代理再和服务器建立连接。这样代理就能看到明文。这个方法适合调试客户端行为,但需要客户端信任代理的证书。

注意:这两种方法都只应该用于调试自己的服务,不要用于分析他人的流量。

6. 我个人在实际操作中的几点体会

HTTPS的配置看起来简单,但细节很多,踩过的坑也不少。最大的体会是:证书管理要有自动化机制。手动续期迟早会忘,一旦证书过期,服务直接不可用。用Let's Encrypt这类免费CA配合自动续期工具,可以省很多心。

另一个体会是:不要盲目追求最新配置。TLS 1.3很好,但如果你的用户里有大量老设备,强行只开1.3会导致他们连不上。配置要跟着实际用户群体走,先开兼容模式,再逐步收紧。

最后,监控要覆盖证书有效期。我见过好几次因为证书过期导致的事故,都是因为没有提前告警。在监控系统里加一个证书有效期检查,提前30天告警,能避免绝大多数这类问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询