关于SM2
SM2算法是中国密码学研究所(中国国家加密管理局)发布的一种非对称加密算法,适用于数字签名、密钥交换和公钥加密等场景。该算法基于椭圆曲线密码学,使用椭圆曲线上的点进行运算。
SM2算法的主要特点如下:
安全性高:SM2算法基于椭圆曲线离散对数问题,具有较高的安全性。
算法效率高:SM2算法的计算量相对较小,适合在资源受限的环境中使用。
适用性广泛:SM2算法可用于数字签名、密钥交换和公钥加密等多种密码应用场景。
算法标准化:SM2算法已被国际电信联盟(ITU-T)和国际标准化组织(ISO)认可为国际标准。
SM2算法的应用场景
安全通信:SM2算法可用于加密电子邮件、即时通讯等通信内容,保护通信的机密性和完整性。
数字签名:在电子合同、电子票据等场景中,SM2算法可用于生成和验证数字签名,确保文件的不可否认性和真实性。
身份认证:SM2算法可用于实现安全的身份认证机制,如在电子政务、金融服务等领域。
数据保护:在数据存储、传输过程中,SM2算法可用于保护数据的安全性,防止数据泄露。
引入jar包依赖
<!-- SM2加密 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.65</version>
</dependency>
生成 SM2 公私钥对
公私钥只需要在第一次使用时生成,保存,后期直接使用即可。
public static X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
public static ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
public static String publicKey = "041620e22b3389db4ec89233a2cba7f7efed06cea1990e82fe5c27f85d41f1b44802f05b125391663bc9809a2e5bc86444139d1cb15c4939189e6386e6a865a3b5";
//私钥,用来解密
public static String privateKey = "9c3e9ba372448da70b387529acb6f271eb7c25e2e3cca315678c770dcf3b9739";
/**
* 生成公私钥
* @return
* @throws Exception
*/
public static String[] key() throws Exception{
String[] key = new String[2];
ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));
AsymmetricCipherKeyPair keyPair = keyPairGenerator.generateKeyPair();
//私钥
BigInteger privatekey = ((ECPrivateKeyParameters) keyPair.getPrivate()).getD();
String privateKey = privatekey.toString(16);
key[0] = privateKey;
//公钥
ECPoint ecPoint = ((ECPublicKeyParameters) keyPair.getPublic()).getQ();
String publicKey = Hex.toHexString(ecPoint.getEncoded(false));
key[1] = publicKey;
return key;
}
公钥用来加密,私钥用来解密,加密可以在前端使用js加密,也可以在用端加密。
前端js加密
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>SM2加密</title>
<script src="./js/jquery-2.1.1.js"></script>
<script src="./js/crypto-js.js"></script>
<script src="./js/sm2.js"></script>
<script type="text/javascript">
var privateKey = "9c3e9ba372448da70b387529acb6f271eb7c25e2e3cca315678c770dcf3b9739";
//公钥,用来加密
var pubkeyHex = "041620e22b3389db4ec89233a2cba7f7efed06cea1990e82fe5c27f85d41f1b44802f05b125391663bc9809a2e5bc86444139d1cb15c4939189e6386e6a865a3b5";
//加密,每次加密得到的加密串都不一样
function encrypt() {
var cipher = sm2Encrypt($('#password').val(), pubkeyHex, 0);
$('#cipher').val(cipher);
}
</script>
</head>
<body>
<input id="initData" name="initData" type="text" placeholder="原始数据" />
<input id="cipher" name="cipher" type="text" placeholder="加密数据" />
<button type="button" id="btn_submit" onclick="encrypt()">
<span>加密</span>
</button>
</body>
</html>
后端加密
加密时需要将公钥串转换为ECPublicKeyParameters。
//提取公钥点
ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKey));
ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
/**
* 数据加密
* @param clearData
* @return
*/
public static String encrypt(String clearData) {
String cipher = "";
byte[] in = clearData.getBytes();
//提取公钥点
ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKey));
ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
//用公钥加密
SM2Engine sm2Engine = new SM2Engine();
sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
byte[] arrayOfBytes = null;
try {
arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
cipher = Hex.toHexString(arrayOfBytes);
} catch (Exception e) {
e.printStackTrace();
}
return cipher;
}
后端解密
针对js加密和后端加密,在解密时会有一点不同的地方。
解密时需要将密钥串转换为privateKeyParameters。
BigInteger privateKeyD = new BigInteger(privateKey, 16);
ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);
针对前端js加密数据进行解密
/**
* 解密,如果内容并非使用SM2加密或者加密内容被篡改或者内容是普通内容,解密会异常
* 前端js方式加密,后端解密后需要Base64.getDecoder().decode
* @param cipherData
* @return
*/
public static String decryptForJS(String cipherData) {
String clear = "";
byte[] cipherDataByte = Hex.decode(cipherData);
BigInteger privateKeyD = new BigInteger(privateKey, 16);
ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);
//用私钥解密
SM2Engine sm2Engine = new SM2Engine();
sm2Engine.init(false, privateKeyParameters);
byte[] arrayOfBytes;
try {
arrayOfBytes = Base64.getDecoder().decode(sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length));
//得到明文
clear = new String(arrayOfBytes);
} catch (InvalidCipherTextException e) {
e.printStackTrace();
}
return clear;
}
针对后端加密数据进行解密
/**
* 解密,如果内容并非使用SM2加密或者加密内容被篡改或者内容是普通内容,解密会异常
* @param cipherData
* @return
*/
public static String decrypt(String cipherData) {
String clear = "";
byte[] cipherDataByte = Hex.decode(cipherData);
BigInteger privateKeyD = new BigInteger(privateKey, 16);
ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);
//用私钥解密
SM2Engine sm2Engine = new SM2Engine();
sm2Engine.init(false, privateKeyParameters);
byte[] arrayOfBytes;
try {
arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
//得到明文
clear = new String(arrayOfBytes);
} catch (InvalidCipherTextException e) {
e.printStackTrace();
}
return clear;
}
数据加解密测试
public static void main(String[] args) {
String data = "Hi SM2";
String cipher = encrypt(data);
System.out.println(cipher);
String clear = decrypt(cipher);
System.out.println(clear);
//前端加密生成的加密串
cipher = "041c0a87b68d195ee5528b0265a2bae2fd5c214c7dc3d3310612d02df2cdaf563ed74f4d98c69bd4acf79bdf1b0e562c01bec241b277560c083bd3e323d0bb3a384d64a7cb93fab1613b8e96d243078b7ba583b92fedd7a3eeab53c70a5b4af4ef1326722fbaf1078b53e4b3a0";
clear = SM2Utils.decryptForJS(cipher);
System.out.println(clear);
}
所有代码在这里,源码