金融涉密级双向加解密架构 —— 混合加密(Hybrid Encryption)
-
传输一个对称密钥(Session Key)。
-
后续数据传输使用对称加密(AES/SM4)
非对称加解密性能很捉急,性能瓶颈主要就就在这玩意上面。
所以我们才要去用速度快的对称加密,去加密真实业务数据,而用非对称加密,去加密那个对称加密的 SessionKey(临时会话密钥) ,因为 Session Key 长度不长,所以非对称加解密的性能消耗可以接受。同时由于 Session Key 在每次传输时随机生成,我们还可以确保他的安全性。
-
:用于安全传输 SM4 会话密钥。
-
SM4(对称加密):用于高效加密业务数据。
-
SHA-256(哈希)
请求流程(Client → Server)
- (随机 128 位)。
- 使用
SM4 Key
加密业务数据(Base 64 处理)。 - 使用
SM2 公钥
加密SM4 Key
(Base 64 处理)。 - 计算
data
的SHA-256
哈希值。 - 使用
SM2 私钥
对dataHash + requestId + sessionKey + timestamp
进行签名。 - 将
sessionKey
、data
、dataHash
、signature
等放入请求 JSON。
响应流程(Server → Client)
- 服务器用
SM2 私钥
解密sessionKey
,获取SM4 Key
。 - 服务器用
SM4 Key
解密data
,获取业务数据。 - 服务器计算
data
的SHA-256
,比对完整性。 - 服务器使用
SM2 公钥
验证signature
,确保数据未被篡改。 - 服务器处理业务逻辑后,按相同方式加密响应数据。
问:为什么要附带个 base64 编解码?
答:因为不用的话,JSON 数据会失真,除非你传输的是纯二进制数据,其他的我只能说都建议你走一道 base 64
// 1. 生成随机 SM4 Key byte[] sm4Key = generateSM4Key(); // 2. 使用 SM4 加密数据 String encryptedData = sm4Encrypt(sm4Key, businessData); // 3. 使用 SM2 公钥加密 SM4 Key String encryptedSessionKey = sm2Encrypt(serverPublicKey, sm4Key); // 4. 计算数据哈希值 String dataHash = sha256(encryptedData); // 5. 生成签名(SM2 私钥) String signature = sm2Sign(clientPrivateKey, dataHash + requestId + encryptedSessionKey + timestamp); // 6. 发送请求 JSON sendRequest({ "requestId": requestId, "timestamp": timestamp, "appId": "client-abc123", "version": "1.0", "sessionKey": encryptedSessionKey, "data": encryptedData, "dataHash": dataHash, "signature": signature });
// 1. 使用 SM2 私钥解密 Session Key byte[] sm4Key = sm2Decrypt(serverPrivateKey, encryptedSessionKey); // 2. 使用 SM4 解密数据 String decryptedData = sm4Decrypt(sm4Key, encryptedData); // 3. 计算 Hash 并比对完整性 if (!sha256(decryptedData).equals(dataHash)) { throw new SecurityException("数据篡改"); } // 4. 验证 SM2 签名 if (!sm2Verify(clientPublicKey, dataHash + requestId + encryptedSessionKey + timestamp, signature)) { throw new SecurityException("签名校验失败"); }
{ "requestId": "uuid-123456", "timestamp": 1700000000, "appId": "test", "version": "1", "sessionKey": "Base64(SM2加密的SM4会话密钥)", "data": "Base64(SM4加密的业务数据)", "dataHash": "SHA256(data原始内容)", "signature": "Base64(SM2签名(dataHash + requestId + timestamp + sessionKey))" }
具体来讲,会有一个随机生成的 requestId 用来定位响应数据,同时保证一个请求的唯一性和幂等性
而 timestamp 是用来校验时钟,同时用来支持超时机制
appId 用来作为客户端标识,version 可能是你请求接口的版本
接着,下面是一个响应 JSON 示例,你可以作为参考。实际上请求、响应的 JSON 字段里,除了那几个必要参数,其他都是可以动态调整的。
{ "requestId": "uuid-123456", "timestamp": 1700000001, "appId": "test", "version": "1", "sessionKey": "Base64(SM2加密的SM4会话密钥)", "code": "200", "message": "Success", "data": "Base64(SM4加密的业务数据)", "dataHash": "SHA256(data原始内容)", "signature": "Base64(SM2签名(dataHash + requestId + timestamp + sessionKey))", "success": true }
密钥对处理
这个东西一定得谨慎,在混合加密的架构下,双方都持有各自的密钥对(公钥 + 私钥)。
服务器的公钥(Server PublicKey)
服务器的私钥(Server PrivateKey)
一旦服务器被攻破,攻击者可以冒充任何客户端,导致身份伪造,非常危险。