第三章 国密SSL/TLS协议标准:GM/T 0024-2014 规范解析与核心差异

国密SSL/TLS的官方标准叫 GM/T 0024-2014,全称是《SSL VPN 技术规范》。别被名字骗了,它本质上就是国密版的TLS协议。我最早接触这个标准时,还以为它是改了几个密码套件就完事了。后来真去实现了一遍才发现——嗯,事情没那么简单。

3.1 协议架构概览

GM/T 0024-2014 的协议分层跟标准TLS非常像。我把它拆成四层,你看这张图就能明白:

GM/T 0024-2014 协议栈分层 应用协议层(HTTP / FTP / SMTP 等) 承载用户业务数据,与标准TLS一致 握手协议层(Handshake Protocol) 包含密钥交换、身份认证、协商国密套件 记录协议层(Record Protocol) 分片、压缩(可选)、加密(SM4/CBC/GCM)、MAC 底层传输协议(TCP / UDP) TCP默认,UDP用于DTLS场景 国密密码算法 SM2/SM3/SM4 SM9(可选) 国密套件核心差异

从图里可以看出来,底层传输和应用层几乎没动。真正的改动集中在握手协议层和记录协议层,以及密码算法那一块。说白了,就是把标准TLS里用的RSA、ECDHE、AES、SHA-256等,换成SM2、SM3、SM4这些国内自主设计的算法。

3.2 与标准TLS 1.2的核心差异

我整理了一个对照表,把最关键的差异点列出来。你对照着看,面试时问起来也方便:

对比维度 标准TLS 1.2 (RFC 5246) GM/T 0024-2014
密钥交换算法 RSA / DH / ECDHE SM2密钥交换协议(基于ECC)
签名算法 RSA / ECDSA SM2签名(SM2withSM3)
对称加密 AES / 3DES / ChaCha20 SM4(CBC / GCM模式)
哈希算法 SHA-256 / SHA-384 SM3(256位哈希)
证书格式 X.509 v3(RSA/ECDSA公钥) X.509 v3(SM2公钥,OID标识)
密码套件格式 TLS_RSA_WITH_AES_128_CBC_SHA ECC_SM4_SM3(0xE0xx系列)
扩展字段 SNI、ALPN等 增加国密签名算法扩展、国密密钥交换扩展

核心要点:GM/T 0024-2014 并不是全新发明的协议,而是在TLS 1.2的框架下替换了密码原语。身份认证强制使用SM2证书,密钥协商走SM2的密钥交换协议(类似ECDHE但加上了签名认证),对称加密和完整性校验统一用SM4和SM3。

3.3 握手协议的两个关键阶段

国密握手和标准TLS最大的不同是:客户端和服务端在交换密钥之前,必须先完成双向证书验证。标准TLS允许服务器证书单方向验证,国密要求双向。原因很实际——国密场景往往是政务内网或金融专线,双向认证是标配。

具体流程我分了两个阶段说:

3.3.1 证书验证阶段(SM2签名)

客户端发送 ClientHello,带上国密扩展(比如 signature_algorithms_cert 里只列SM2withSM3)。服务端回复 ServerHello,带上自己的SM2证书。客户端拿到证书后,用SM3算一遍证书摘要,再用SM2验签。嗯,这里容易踩坑——很多开发者把SM2验签和SM2加密搞混。记住:签名用的SM2私钥,验签用公钥;加密和解密是另一套流程,不能混淆。

3.3.2 密钥交换阶段(SM2密钥交换协议)

双向证书验证通过后,双方各自生成临时SM2密钥对,然后走SM2密钥交换协议。这个协议跟标准TLS里的ECDHE很像,但内部多了身份绑定(对端的证书公钥被用于派生共享密钥)。

为什么要多这一层身份绑定?我见过一个实际案例:某项目直接把标准TLS的ECDHE换成SM2密钥交换,但没有把证书公钥和临时密钥关联起来,导致中间人可以伪造临时密钥...后来我们不得不改实现,强制做了一次SM2签名来绑定身份。

个人经验:实现国密握手时,建议用专门的SM2密钥交换API,而不要自己拼凑。OpenSSL 1.1.1g以上版本支持原生国密,但需要把 SSL_CTX 的密码列表设成 "ECC-SM4-SM3"。我曾经在CentOS 7上编译过OpenSSL国密补丁,编译参数多加 enable-ec_nistp_64_gcc_128 反而会编译失败——直接走 no-asm 更稳妥。

3.4 密码套件命名规则

标准TLS的密码套件用 TLS_ 开头 + 密钥交换 + 认证 + 加密 + 哈希。国密用 ECC_ 开头,目前只有两种主流套件:

  • ECC-SM4-SM3(hex code: 0xE013)—— 最常用,强制使用SM2证书
  • ECDHE-SM4-SM3(hex code: 0xE018)—— 支持临时密钥交换,兼容标准TLS 1.3的PSK模式

我个人建议初期直接上 ECC-SM4-SM3。为什么?因为 ECDHE-SM4-SM3 虽然灵活性高,但很多国密网关并不支持,遇到兼容性问题很难排查——我在某省级政务平台上就遇到过,服务端只认 0xE013,对接方发 0xE018 直接报握手失败。

3.5 记录协议层的变化

标准TLS的记录协议里,加密和MAC是分开的(CBC模式先加密后MAC,或者先MAC后加密)。国密要求使用 GM/T 0024-2014 定义的记录保护模式:

  • 首选 SM4-GCM(认证加密,AEAD),类似TLS 1.2的AES-GCM
  • 兼容 SM4-CBC + HMAC-SM3(先MAC后加密,类似TLS 1.2最早的CBC模式)

避坑指南:SM4-GCM的nonce(IV)长度固定为12字节,如果用了其他长度,必须截断或补齐。我曾经在移植libressl的GCM实现时,发现SM4-GCM的加密上下文里没有初始化nonce计数器,导致每次加密结果都一样——调试了一个下午才发现是static变量没清零。切记,每个记录都要重新初始化nonce。

3.6 兼容性方面的碎碎念

最后说点实战经验:GM/T 0024-2014 跟国际标准TLS不能直接互通。客户端和服务端必须都支持国密套件。如果你想在同一个端口上同时支持国密和标准TLS(比如金融行业的双栈部署),可以用 SSL_CTX_set_cipher_list() 设置多个密码套件,把国密套件放前面。但要注意,如果客户端发了国密扩展而服务端不识别,协议会回退到标准套件——这可不是降级攻击,而是规范允许的。你最好在业务层做一次国密套件协商日志记录,方便追溯。