金融涉密级双向加解密架构 —— 混合加密(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)
一旦服务器被攻破,攻击者可以冒充任何客户端,导致身份伪造,非常危险。
