sm2多端加密解密,java,js,android,ios實戰(zhàn)
SM2非對稱加密
公鑰 = 04xxxxxxxxxxxxxxxxxxxx,
私鑰 = 276xxxx
原文:你哦哈1232154 3654 {} ,俺可接受不符點
公鑰私鑰是我后臺自己生成的
java代碼實現(xiàn)
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輔助工具類
采用數(shù)字證書類型 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進制格式,發(fā)給前端,格式如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實現(xiàn)
需要引入crypto-js.js和sm2.js,加老王微信,老王私下發(fā)你,文件太大,不方便放到文章上,關注公眾號后,直接輸入 “ 加好友” ,可以加老王微信精彩不斷
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實現(xiàn),上面不是hutool,因為個人喜歡hutool,必須要基于hutool實現(xiàn)一個
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){
//創(chuàng)建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實現(xiàn)和上面的Android可用的版本一樣,當然,后端也可以直接使用Android和java通用的版本
ios端必須和js和java和Android端一致,采用 C1C2C3模式
下面是ios的實現(xiàn)。采用國密 國密的 Objective-C 封裝
查看具體實現(xiàn)過程,請至開源項目地址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端實現(xiàn)的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解密
你們具體可根據(jù)自己的情況來處理,但是最好搞清楚邏輯,不然很容易出錯,或者直接按照我們的略記處理,直接copy,省事兒
作者:IT學習道場
歡迎關注微信公眾號 : IT學習道場