支付系统中的通信安全
支付系统中的通信安全
如果对加解密与加签和验签有疑惑,可以先去上一篇文章中看看哦。
这篇文章讨论一个在支付系统对接中很常见的问题:HTTPS 已经提供了链路加密,为什么还要做“报文级二次加密”?
在高敏业务里,TLS 可能终止在网关/WAF/反向代理;排障抓包、链路追踪、日志脱敏不彻底,都可能让敏感字段在中间环节以明文出现。
因此我们常会在 HTTPS 之上再做一层“报文级”的加密与签名:即使报文在中间环节被看到也仍是密文,同时还能校验来源与防篡改。
方案一:专线
网络专线,简单来说,就是运营商(如电信、联通、移动)为企业或机构提供的一条专用、独享、端到端的通信通道。
你可以把它想象成一条信息高速公路上的专属快车道,而普通家庭宽带则是和大家共用的普通车道。
这样的好处是这条路搭建了别人与你的支付系统的专属车道,安全性和速度会大大提升。
当然设置专线是比较昂贵的,那么有没有什么方案是不通过专线也能提高安全性的呢?
方案二:报文加解密(消息级安全)
我们可以模仿 HTTPS 的思想,对报文使用自定义方式进行加解密与加签验签。
在进入流程前,先补充一个基础概念:Hash 函数。
Hash 函数
Hash(散列)函数是一类把任意长度的输入数据映射成固定长度输出的函数,输出通常称为 哈希值 / 摘要(digest)。
1)核心特性
- 确定性:同样的输入必然得到同样的输出。
- 固定输出长度:不管输入是 10 字节还是 10MB,输出长度固定(例如 SHA-256 恒为 256 bit)。
- 雪崩效应:输入只改 1 bit,输出会发生显著变化。
- 单向性(预映像困难):已知哈希值,很难反推出原文。
- 抗碰撞:很难找到两段不同的输入,使它们哈希结果相同。
注意:哈希函数不是加密算法,它不支持“解密”,更像是“指纹生成器”。
2)常见算法与选择建议
- MD5 / SHA-1:已存在实用碰撞攻击,不建议用于安全场景。
- SHA-256 / SHA-512(SHA-2):工程上最常用,兼容性与安全性都很好。
- SHA-3:新一代标准,在一些高安全要求场景也会使用。
3)Hash 在通信安全中的典型用法
- 完整性校验:发送方计算
hash(body),接收方重算并比对,判断报文是否被篡改。 - 签名的输入:通常对摘要签名(例如
Sign(hash(body))),性能更好也更统一。 - 消息认证码(MAC)/ HMAC:只传
hash(body)不安全(攻击者能改 body 后重算 hash)。常用 HMAC(key, body) 来保证“完整性 + 身份性”。
报文级安全流程(参考实现)
本文的“二次加密”主要解决两件事:
- 让敏感字段端到端保持密文(即使经过网关/WAF/代理、抓包、日志/链路追踪等环节,也尽量不暴露明文)。
- 让服务端能验证请求来源与完整性(防篡改、可追责,并配合
ts + nonce防重放)。
0)AEAD / AAD 是什么?
- AEAD(Authenticated Encryption with Associated Data)可以理解为“加密 + 认证”一次完成。
- 加密:保证内容看不懂。
- 认证:保证内容没被篡改(篡改会直接解密失败)。
- AAD(Associated Data)是“关联数据”:不加密,但会参与认证计算。
- 适合放:
merchantId、ts、nonce、method、path等“无需保密但必须防篡改”的字段。 - 好处:攻击者哪怕只改了这些元信息,AES-GCM 校验也会失败,服务端会直接拒绝。
- 适合放:
1)证书/公钥交换与信任建立
在所有步骤之前,双方先完成证书/公钥交换与信任建立:服务端持有自己的私钥与证书;客户端保存并校验服务端证书指纹/证书链;客户端也有自己的私钥与证书(或公钥)供服务端验签(同时约定 merchantId / keyId / 证书序列号等标识)。
1 | |
2)生成 ts + nonce(防重放)
1 | |
3)计算 bodyHash 并加上 canonicalString
1 | |
4)私钥签名
1 | |
5)生成随机对称密钥 + iv
1 | |
6)AEAD(AES-GCM)加密 body
如果要把元信息与密文绑定,建议把 merchantId/ts/nonce/method/path 作为 AAD 传入(代码略)。
1 | |
7)服务端公钥加密 aesKey(封装密钥)
1 | |
8)请求包字段一览
| 字段 | 类型 | 用途 |
|---|---|---|
| merchantId / keyId / certSerial / alg | 明文元信息 | 定位商户与密钥,选择验签/解密方式 |
| ts / nonce | 明文元信息 | 防重放(时间窗校验 + 去重) |
| encKey | 密钥封装 | 用服务端公钥加密后的对称密钥(会话 key) |
| iv | 密钥封装 | 对称加密使用的 IV/nonce |
| ciphertext | 密文 | 业务 body 的密文(含认证 tag) |
| signature | 完整性与身份 | 对 canonicalString 的签名,用于验签与防篡改 |
9)服务端处理:防重放、解密、验签
服务端收到后先做基础校验与防重放:检查 ts 是否在允许窗口内;检查 nonce 是否已使用过(Redis/DB 去重,过期删除);然后用服务端私钥解出 aesKey,并用 AEAD 解密 ciphertext 得到 body(解密失败直接拒绝)。最后重建 canonicalString 并验签。
(下方 Java demo 与原文一致,这里略。)
安全性提升(上线常用加强项)
1)密钥与证书治理
- 证书/公钥轮换:在请求里携带
keyId/certSerial,服务端支持同一商户多把证书并行有效(灰度切换),并定义旧证书下线时间。 - 私钥保护:私钥禁止明文落盘,优先放在 KMS/HSM 或操作系统密钥库中,限制导出与访问权限。
- 权限隔离:把“签名/解密”能力做成独立组件或独立权限账户(最小权限原则),避免业务进程直接接触私钥。
2)防重放(ts + nonce)的工程落地
- 时间窗:明确服务端允许窗口(例如 ±5 分钟),超窗直接拒绝。
- nonce 去重:服务端把
merchantId + nonce存入 Redis/DB 去重,TTL ≥ 时间窗(并留冗余)。 - 业务幂等:支付类接口建议额外提供
requestId(或orderId + attemptNo)并在服务端做幂等,避免“合法重放”造成重复扣款。
3)canonicalString 规范(避免验签失败与歧义)
- 覆盖范围:建议至少覆盖
method + path + query + bodyHash + ts + nonce + merchantId。 - 规范化规则:明确编码(UTF-8)、换行符(n)、大小写、空值处理、query 排序方式。
- 避免直接签 JSON 原文:更推荐签
bodyHash或“规范化 JSON”结果,避免字段顺序/空格导致的验签不一致。
4)风控、审计与告警
- 记录安全事件日志:验签失败、重放命中、时间窗超限、解密失败、keyId 不存在、证书过期等。
- 对同一
merchantId的连续失败做限流/熔断/告警,防止被探测与撞库。
5)国密与硬件
- 国密组合(示例):SM2(签名/密钥交换) + SM4-GCM(对称加密) + SM3(Hash)。
- HSM/KMS 的价值:密钥不可导出、硬件内签名/解密、可审计、可轮换,适合高合规与高安全场景。