Vue+Springboot前后端完整使用国密算法SM2双公私钥对数据加密传输交互完整解决方案
Vue+Springboot 前后端完整使⽤国密算法SM2双公私钥对数据加密传输交互完整解决⽅案项⽬,特别是企事业单位的项⽬,第三方测试公司做安全测试时,常常要求使用国密算法,因涉及服务端和客户端的交互,传递关键数据时要求使用SM2非对称加密。
引入相关依赖
这里我使用的是jdk1.8 的maven项目,需要在pom.xml里引入以下依赖:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.22</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.14</version>
</dependency>
可复用代码
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.BCUtil;
import cn.hutool.crypto.asymmetric.SM2;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class SM2Utils {
static final BouncyCastleProvider bc = new BouncyCastleProvider();
public static Map<String,Object> generateKey(){
Map<String,Object> map = new HashMap<>();
SM2 sm2=new SM2();
BCECPrivateKey privateKey = (BCECPrivateKey) (sm2.getPrivateKey());
BigInteger d = privateKey.getD();
BCECPublicKey publicKey = (BCECPublicKey) sm2.getPublicKey();
ECPoint q = publicKey.getQ();
String hutoolPrivateKeyHex = HexUtil.encodeHexStr(BCUtil.encodeECPrivateKey(privateKey));
String hutoolPublicKeyHex = HexUtil.encodeHexStr(q.getEncoded(false));
map.put("publicKeyHex",hutoolPublicKeyHex);
map.put("privateKeyHex",hutoolPrivateKeyHex);
return map;
}
}
服务端在登录校验成功后生成服务端公私钥对,并把改公私钥对存放到session中,便于下次会话提取,同时将公钥字符串返回给客户端:
//生成服务器公私钥对,并将服务器公私钥放入session
Map<String,Object> mapKey = SM2Utils.generateKey();
session.setAttribute("publicKeyHex",mapKey.get("publicKeyHex").toString());
session.setAttribute("privateKeyHex",mapKey.get("privateKeyHex").toString());
//公钥和正常消息一起返回给客户端
map.put("publicKeyHex", mapKey.get("publicKeyHex").toString());
客户端收到服务端给出的公钥后,存入localStorage,并生成客户端公私钥对,存入localStorage,并用接收到的服务端公钥加密客户端公私钥对,将加密后的密文发给服务端
npm install --save sm-crypto
const sm2 = require('sm-crypto').sm2
//生成客户端公私钥
const { publicKey, privateKey } = sm2.generateKeyPairHex()
window.localStorage.setItem("publicKey", publicKey)
window.localStorage.setItem("privateKey", privateKey)
//获取服务器端公钥
let serverPublicKey = res.spk
window.localStorage.setItem("serverPublicKey", serverPublicKey)
//加密客户端公私钥
encryptpublickeyClient = sm2.doEncrypt(publicKey, serverPublicKey,cipherMode);
encryptpublickeyClient = '04' + encryptpublickeyClient;
encryptprivatekeyClient = sm2.doEncrypt(privateKey, serverPublicKey,cipherMode);
encryptprivatekeyClient = '04' + encryptprivatekeyClient;
服务端在二次接收客户端请求时,从参数中获取加密后的客户端公私钥对密文,使用服务端秘钥解密后,将客户端公私钥对存入会话,并在服务器生成随机数,用客户端公钥加密后返回数据给前端:
String publicKeyHex = session.getAttribute("publicKeyHex").toString();
String privateKeyHex = session.getAttribute("privateKeyHex").toString();
//privateKey 为上述生成的私钥 publicKey为生成的公钥,注意 此处不是Q值
SM2 sm2 = SmUtil.sm2(privateKeyHex, publicKeyHex);
//body为加密后的数据(注意:此处加密数据可能缺少04开头,解密会失败,需要手动在body前拼上04,body="04"+body)
String clientPublicKeyStr = sm2.decryptStr(userDTO.getClientPublicKey(), KeyType.PrivateKey);
String clientPrivateKeyStr = sm2.decryptStr(userDTO.getClientPrivateKey(), KeyType.PrivateKey);
SM2 sm2Client = SmUtil.sm2(clientPrivateKeyStr, clientPublicKeyStr);
String uid = UUID.randomUUID().toString().replaceAll("-","");
String uidEncode = sm2Client.encryptHex(uid,KeyType.PublicKey);
map.put("uid", uidEncode);
客户端接收到服务端的uid密文后,使用客户端私钥解密,并存入localStorage中
let uidEcrpyptCode = res.uid;
let headerStr = uidEcrpyptCode.substring(0, 2)
if(headerStr == '04'){
uidEcrpyptCode = uidEcrpyptCode.substring(2, uidEcrpyptCode.length)
}
//解密uid
let uidStr = sm2.doDecrypt(uidEcrpyptCode, '00'+privateKey)
window.localStorage.setItem("uid", uidStr);
更多推荐



所有评论(0)