sm2多端加密解密,java,js,android,ios实战
SM2非对称加密
公钥 = 04xxxxxxxxxxxxxxxxxxxx,
私钥 = 276xxxx
原文:你哦哈1232154 3654 {} ,俺可接受不符点
公钥私钥是我后台自己生成的
java代码实现
pom.xml
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.71</version>
</dependency>
国密sm2算法秘钥对
package com.sm.mscmsm2;
/**
* 描述: 国密sm2算法秘钥对 - 对象 <br>
* 时间: 2022-07-14 16:01 <br>
* 作者:IT学习道场
*/
public class SM2KeyPairs {
/**
*公钥
*/
private String publicKey;
/**
* 私钥
*/
private String privateKey;
public SM2KeyPairs() {
}
public SM2KeyPairs(String publicKey, String privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
public String getPublicKey() {
return publicKey;
}
public void setPublicKey(String publicKey) {
this.publicKey = publicKey;
}
public String getPrivateKey() {
return privateKey;
}
public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
}
@Override
public String toString() {
return "SM2KeyPairs{" +
"publicKey='" + publicKey + '\'' +
", privateKey='" + privateKey + '\'' +
'}';
}
}
SM2辅助工具类
采用数字证书类型 X9
package com.sm.mscmsm2;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
/**
* 描述: 美术传媒sm2 <br>
* 时间: 2022-07-25 17:32 <br>
* 作者:王林冲
*/
@Slf4j
public class SM2Util {
private static final ECDomainParameters domainParameters;
private static final X9ECParameters sm2ECParameters;
static {
sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
}
public static SM2KeyPairs getSM2KeyPairs(){
//生成密钥对
ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
try {
keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));
AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();
//私钥,16进制格式,自己保存,格式如a2081b5b81fbea0b6b973a3ab6dbbbc65b1164488bf22d8ae2ff0b8260f64853
BigInteger privatekey = ((ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate()).getD();
String privateKeyHex = privatekey.toString(16);
//公钥,16进制格式,发给前端,格式如04813d4d97ad31bd9d18d785f337f683233099d5abed09cb397152d50ac28cc0ba43711960e811d90453db5f5a9518d660858a8d0c57e359a8bf83427760ebcbba
ECPoint ecPoint = ((ECPublicKeyParameters) asymmetricCipherKeyPair.getPublic()).getQ();
String publicKeyHex = Hex.toHexString(ecPoint.getEncoded(false));
SM2KeyPairs pairs = new SM2KeyPairs(publicKeyHex, privateKeyHex);
return pairs;
} catch (NoSuchAlgorithmException e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e.getMessage());
}
}
public static String decrypt(String data, String privateKey) {
byte[] cipherDataByte = Hex.decode(data);
//刚才的私钥Hex,先还原私钥
BigInteger privateKeyD = new BigInteger(privateKey, 16);
ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);
//用私钥解密
SM2Engine sm2Engine = new SM2Engine();
sm2Engine.init(false, privateKeyParameters);
//processBlock得到Base64格式,记得解码
byte[] arrayOfBytes = new byte[0];
try {
arrayOfBytes = Base64.getDecoder().decode(sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length));
} catch (InvalidCipherTextException e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e.getMessage());
}
//得到明文:SM2 Encryption Test
return new String(arrayOfBytes);
}
public static String encrypt(String data, String publicKey){
//提取公钥点
ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKey));
//刚才的私钥Hex,先还原私钥
ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
SM2Engine sm2Engine = new SM2Engine();
sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
byte[] arrayOfBytes = new byte[0];
try {
byte[] in = Base64.getEncoder().encode(data.getBytes("utf-8"));
arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e.getMessage());
}
return Hex.toHexString(arrayOfBytes);
}
public static void main(String[] args) throws InvalidCipherTextException, UnsupportedEncodingException {
//SM2KeyPairs sm2KeyPairs = getSM2KeyPairs();
//System.out.println(sm2KeyPairs.toString());
String publicKey = "0419080a9bdb6968f0ef31b2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
String privateKey = "2766001cc5d2888xxxxxxxxxxxxxxxxxxxxxxxxx";
String text = "你哦哈1232154 3654 {} ,俺可接受不符点";
String encrypt = encrypt(text, publicKey);
System.out.println("encrypt = "+ encrypt);
//String encrypt = "041fccce91d7a35a429f449aea758364826f688577bad9d3b9c99a1ab5c390fdf63f00232e0af4fcab8fccd70c46b636e42024f260973d73c8d1b6c3d41c2b26238b8a09c994e0a912ba2022b342af049b164979018af08e75cb63a66408fe9dfd553e7a350fb0d12405b984bde185ba38ca693c9c12b06dbe445abc5b9fc754f2424bab2a766d62c12b1832c51b2cab44ba4dc0049b5f3b479fe1cc348bcfd77f65db4267";
String jm = decrypt(encrypt, privateKey);
System.out.println(jm);
}
}
JavaScript实现
需要引入crypto-js.js和sm2.js,加老王微信,老王私下发你,文件太大,不方便放到文章上,关注公众号后,直接输入 “ 加好友” ,可以加老王微信精彩不断
html使用demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>sm2加密</title>
<script src="./lib/crypto-js.js"></script>
<script src="./lib/sm2.js"></script>
</head>
<body>
<script type="text/javascript">
//私钥:2766001cc5d2888553efe566781d8fb25557aecd6435e731d21ad362af8a4eaf
//公钥:前缀04+x坐标+y坐标
var pubkeyHex = "04190xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
var msg='你哦哈1232154 3654 {} ,俺可接受不符点';
//加密格式0: C1C2C3、1: C1C3C2
var encryptData = sm2Encrypt(msg, pubkeyHex, 0);
document.write(encryptData);
</script>
</body>
</html>
Android端和java端一样,jar包采用 bcprov-jdk15to18-1.71.jar
java端基于hutool实现,上面不是hutool,因为个人喜欢hutool,必须要基于hutool实现一个
pom.xml
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.4</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.57</version>
</dependency>
SM2KeyPairs
package com.sm.hutoolsm2;
/**
* 描述: 国密sm2算法秘钥对 - 对象 <br>
* 时间: 2022-07-14 16:01 <br>
* 作者:IT学习道场
*/
public class SM2KeyPairs {
/**
*公钥
*/
private String publicKey;
/**
* 私钥
*/
private String privateKey;
public SM2KeyPairs() {
}
public SM2KeyPairs(String publicKey, String privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
public String getPublicKey() {
return publicKey;
}
public void setPublicKey(String publicKey) {
this.publicKey = publicKey;
}
public String getPrivateKey() {
return privateKey;
}
public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
}
@Override
public String toString() {
return "SM2KeyPairs{" +
"publicKey='" + publicKey + '\'' +
", privateKey='" + privateKey + '\'' +
'}';
}
}
SM2Util
package com.sm.hutoolsm2;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.BCUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import com.sm.sm2.SM2KeyPairs;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import java.nio.charset.Charset;
import java.util.Base64;
/**
* 描述: todo <br>
* 时间: 2022-07-25 17:32 <br>
* 作者:IT学习道场
*/
public class SM2Util {
/**
* 获取公钥私钥
* @return SM2KeyPairs 公私钥对象
*/
public static SM2KeyPairs getKeyPairs(){
SM2 sm2 = SmUtil.sm2();
// sm2的加解密时有两种方式即 C1C2C3、 C1C3C2,
sm2.setMode(SM2Engine.Mode.C1C2C3);
// 生成私钥
String privateKey = HexUtil.encodeHexStr(BCUtil.encodeECPrivateKey(sm2.getPrivateKey()));
// 生成公钥
String publicKey = HexUtil.encodeHexStr(((BCECPublicKey) sm2.getPublicKey()).getQ().getEncoded(false));
SM2KeyPairs keyPairs = new SM2KeyPairs(publicKey, privateKey);
return keyPairs;
}
/**
* 公钥加密
* @param publicKey 公钥
* @param text 预加密文本
* @return 加密后文本
*/
public static String encrypt(String publicKey, String text){
// 通过密钥解密
SM2 sm2 = SmUtil.sm2(null, publicKey);
sm2.setMode(SM2Engine.Mode.C1C2C3);
//1 先把明文转成base64
text = Base64.getEncoder().encodeToString(text.getBytes());
//2 把base64的文本用公钥加密后在转成密文为16进制,否则前端解密前需要先转换格式
String encryptStr = sm2.encryptHex(text, Charset.forName("utf-8"), KeyType.PublicKey);
return encryptStr;
}
/**
* 私钥解密
* @param privateKey 私钥
* @param text 加密文本
* @return 解密后文本
*/
public static String decrypt(String privateKey, String text){
//创建sm2 对象
SM2 sm2 = SmUtil.sm2(privateKey, null);
sm2.setMode(SM2Engine.Mode.C1C2C3);
// 私钥解密
String decrypt = sm2.decryptStr(text, KeyType.PrivateKey);
byte[] decode = Base64.getDecoder().decode(decrypt);
String decryptStr = new String(decode);
return decryptStr;
}
public static void main(String[] args) {
//SM2KeyPairs keyPairs = getKeyPairs();
//System.out.println(keyPairs.toString());
String publicKey = "0407125a6dc1d73e41dc1b57xxxxxxxxxxxxxxxxxxxxxx";
String privateKey = "0f3c459c2090eb5c35108xxxxxxxxxxxxx";
String text = "你哦哈1232154 3654 {} ,俺可接受不符点";
String encrypt = encrypt(publicKey, text);
System.out.println("encrypt = " + encrypt);
//String encrypt ="04ddb0f3622c51af847bbd528d9fb8cd264aa9341c817cb3077bb1464766b3b7e3a5ff523004d4f2259676267a080f66c4682844adef2e4b612e604071af16c6285fc48795e4ae1277e7d79b4bf420584dea2345eae0d5f2e12293013d25af09eaff4fabf5109a18fed74d07ed30a34b1623f161f7a70e2746accf9334b96cfc33f2aa6aae13c08b131604a9caa7d2c5453b8a021a6354ba27cd7d3f4ebcf93376efa9cf5d";
String decrypt = decrypt(privateKey, encrypt);
System.out.println("decrypt = " + decrypt);
}
}
hutool实现和上面的Android可用的版本一样,当然,后端也可以直接使用Android和java通用的版本
ios端必须和js和java和Android端一致,采用 C1C2C3模式
下面是ios的实现。采用国密 国密的 Objective-C 封装
查看具体实现过程,请至开源项目地址GitHub - muzipiao/GMObjC: SM2/SM3/SM4/ECDH library based on OpenSSL.。
在终端运行以下命令:
git clone https://github.com/muzipiao/GMObjC.git
cd GMObjC
pod install
open GMObjC.xcworkspace
向项目中引入GMObjC即可
使用代码如下
SM2 加解密
SM2 加解密都很简单,加密传入待加密明文和公钥,解密传入密文和私钥即可,加密逻辑代码:
NSString *pubKey = @"0408E3FFF9505BCFAF9307E665E9229F4E1B3936437xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
NSString *priKey = @"90F3A42B9FE24AB196305FD92EC82E647616C3A369xxxxxxxxxx";
NSString *plaintext = @"你哦哈1232154 3654 {} ,俺可接受不符点";
//先对明文进行base64加密,再用GMSm2Utils 对其进行sm2加密,返回asn1编码格式的密文
NSString *encode = [GMSm2Utils encryptText:[self base64:plaintext] publicKey:pubKey];
//把asn1编码格式的密文的 encode 解码成C1C3C2的密文字符串 = c1c3c2
NSString *c1c3c2 = [GMSm2Utils asn1DecodeToC1C3C2:encode];
//再把c1c3c2这个字符串转成 C1C2C3 模式的密文字符串 = c1c2c3 ,这个可以直接传给java端,用上面的java端实现的sm2Util进行解密
NSString *c1c2c3 = [GMSm2Utils convertC1C3C2ToC1C2C3:c1c3c2 hasPrefix:NO];
NSLog(@"c1c2c3 : %@",c1c2c3);
注意这里的密文要拼上 "04" 这个前缀,后端才能进行解密
这是base64加密方法
- (NSString *)base64:(NSString *)string{
NSString *target = string;
NSData *data = [target dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64Str = [data base64EncodedStringWithOptions:nil];
// NSString *base64DecodeStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
return base64Str;
}
我们前后端的逻辑是 先对明文进行base64加密,再sm2加密,再转16进制。
解密的逻辑,是先对密文进行16进制解密,再sm2解密,再base64解密
你们具体可根据自己的情况来处理,但是最好搞清楚逻辑,不然很容易出错,或者直接按照我们的略记处理,直接copy,省事儿
作者:IT学习道场
欢迎关注微信公众号 : IT学习道场