加密握手概述

加密握手是个啥?说白了就是通信双方在正式发数据之前,先互相打个招呼、验明正身、商量好怎么加密的一个过程。你想想看,两个人要在不安全的信道上安全聊天,总得先确认对方不是冒充的,再约定一个只有你俩知道的暗号,对吧?加密握手干的就是这件事。

我记得刚接触SDK开发那会,老觉得握手是底层的事,跟应用层没关系。直到有一次对接第三方服务,对方要求必须用双向认证,我随手写了个单向的,结果连接建立失败,日志里全是“handshake failure”。那之后我就老老实实把握手细节吃透了。

加密握手的作用

握手阶段主要解决三个核心问题:

  • 身份认证:确保对方就是它声称的那个实体。通常靠数字证书来实现,客户端验证服务器证书,双向认证时两边都要验。
  • 密钥协商:在不安全的信道上安全地达成一个共享密钥。主要靠非对称加密来传递预主密钥,然后用它派生出会话密钥。
  • 参数协商:商量用哪个加密套件、哪个协议版本、是否支持特定扩展等。

这三个问题的答案会在握手完成后固化下来,之后的数据传输就靠这些约定好的参数进行对称加密,效率高又安全。

常见协议对比:TLS 1.2 vs TLS 1.3

目前最常用的两个版本是TLS 1.2和TLS 1.3。1.3出来好几年了,但很多老系统还在跑1.2。我在项目中遇到过不少因为版本不兼容导致的握手失败,所以选版本时得小心。

下面这个表把关键差异列出来了:

对比项TLS 1.2TLS 1.3
握手往返次数2-RTT(完整握手)1-RTT(完整握手),0-RTT(支持前导数据)
支持的加密套件大量组合,包含许多已废弃的算法仅5种安全套件,剔除不安全算法
密钥交换支持RSA、DH、ECDH等多种方式仅支持(EC)DHE,强制前向安全
证书传输时机Server Hello之后单独发Certificate合并到Server Hello中发送
会话恢复Session ID或Session TicketPSK + Ticket机制
安全性存在一些已知攻击(如POODLE、Lucky13)设计更简洁,默认安全

从表里能看出来,TLS 1.3明显更高效、更安全。我建议新项目直接上1.3,除非要兼容老旧客户端。不过要注意,1.3的0-RTT虽然快,但有重放攻击风险,适合幂等请求,比如HTTP GET,别用在转账这种操作上。

双向认证概念

平时大家说的“单向认证”只有客户端验证服务器证书。但有些场景下服务器也需要确认客户端的身份,这就叫双向认证,也叫mTLS(mutual TLS)。

双向认证的流程多了一步:服务器在发完自己的证书后,会向客户端索要证书(Certificate Request),然后客户端把自己的证书发过去,服务器验证后握手才能继续。

核心要点:双向认证要求客户端持有合法的数字证书,且服务端信任签发该证书的CA。常用于API网关、微服务间通信、金融级接口等场景。

我踩过的一个坑:客户端证书的私钥一定要安全存储。有次我把私钥硬编码在配置里,虽然开发方便,但上线前安全审计被直接挂红牌。后来改用HSM或密钥链存储,安全多了。

核心握手流程图示

下面这张SVG图把单向和双向认证的握手流程画出来了,方便对比理解:

TLS 握手流程对比(单向 vs 双向) 客户端 服务端 单向认证流程 ① ClientHello(支持版本、密码套件) ② ServerHello + Certificate + ServerKeyExchange ③ ClientKeyExchange + ChangeCipherSpec + Finished ④ ChangeCipherSpec + Finished 双向认证额外步骤 ⑤ CertificateRequest(在步骤②之后) ⑥ ClientCertificate + CertificateVerify 说明:虚线箭头表示双向认证中额外增加的步骤 单向认证共4步,双向认证共6步,多2步客户端证书

避坑指南

我曾经犯过一个低级错误:在SDK初始化时把证书链验证给跳过了。后果是中间人攻击可以直接伪造服务端,用户数据全裸奔。后来我养成了一个习惯——任何生产环境的加密握手,必须开启完整的证书链验证,包括主题名(CN/SAN)校验和吊销状态检查(CRL/OCSP)。

另外,TLS 1.3的0-RTT虽然好,但如果你在SDK里启用了它,务必注意重放检测。我习惯在应用层加一个nonce字段,让服务端检查是否被重复使用。这样既能享受速度,又能防重放。