三步輕松實(shí)現(xiàn)Java的SM2前端加密后端解密
話不多說,先上需要用到的js文件下載鏈接 和 jsp前端代碼。
第一步:下載兩個必備的js文件—— crypto-js.js、sm2.js 。
它們的下載鏈接如下↓(該網(wǎng)頁會很卡,畢竟github,建議卡的話就多重新加載幾次,我差不多試了8次才進(jìn)去):
https://github.com/Saberization/SM2
進(jìn)入網(wǎng)頁,下載后拿取下方↓圖片上的兩個js就行:

然后,將這兩js放到存放靜態(tài)資源(static)的目錄下,我放的是static中的sm2目錄(sm2這個目錄是我創(chuàng)的)
后面,在jsp的<head></head>中引入這兩個js文件
<!--jsp引入sm2,實(shí)現(xiàn)前端加密-->
<script type="text/javascript" src="${pageContext.request.contextPath}/static/sm2/crypto-js.js" charset="utf-8"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/static/sm2/sm2.js" charset="utf-8"></script>第二步:前端jsp代碼實(shí)現(xiàn),下方的代碼寫在jsp頁面的<script type="text/javascript"></script>中。
作用是,當(dāng)調(diào)用下方↓函數(shù)的時(shí)候,會給你傳入的值進(jìn)行加密并返回。
注:【你生成的公鑰】的生成方式可以在文章末尾的SM2Utils.java工具類中可以找到,是個main方法,一鍵生成,簡單粗暴
<!--sm2加密函數(shù)-->
function sm2EncryptPwd(data) {
<!--sm2公鑰-->
var publicKey = '你生成的公鑰';
return sm2Encrypt(data, publicKey, 1);
}至此,前端加密結(jié)束,進(jìn)入后端解密環(huán)節(jié)——
后端需要的jar包是:bcprov-jdk15on-1.68.jar
注意?。?!該jar包必須要1.60版本以上的,不然解密的時(shí)候會缺少必要的條件!?。〔⑶椅矣玫臅r(shí)候,本地環(huán)境是JDK1.8的,其他環(huán)境沒試過,建議如有不同,先查一下。
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.68</version> </dependency>
第三步:后端代碼實(shí)現(xiàn)。
導(dǎo)入jar包后,就是必要的工具類實(shí)體類:SM2Utils.java
你可以用工具類本身的加密方法解密,也可以用工具類中我寫的 getJieMiJieGuo() 方法解密。
我個人建議用我寫的,因?yàn)椴僮髌饋砀唵涡?,只需要傳入加密后的字符串,就可以返回解密的?shù)據(jù)。
其中【你生成的公鑰】和【你生成的私鑰】可以通過工具類中的方法生成,生成完復(fù)制粘貼進(jìn)去就行
注!后端和前端的公鑰要保持一致!因?yàn)楣€私鑰是配對使用的!
package com.test.common.utils;
import org.apache.commons.net.util.Base64;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
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.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
public class SM2Utils {
private static final String publicKey = "你生成的公鑰";
private static final String privateKey = "你生成私鑰";
/**
* 解密,并且獲取解密后的字符串
*
* @return 秦?zé)o解
*/
public static String getJieMiJieGuo(String cipherData) {
byte[] cipherDataByte = Hex.decode(cipherData);
BigInteger privateKeyD = new BigInteger(privateKey, 16);
X9ECParameters sm2ECParameters1 = GMNamedCurves.getByName("sm2p256v1");
ECDomainParameters domainParameters1 = new ECDomainParameters(sm2ECParameters1.getCurve(), sm2ECParameters1.getG(), sm2ECParameters1.getN());
ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters1);
//用私鑰解密,SM2Engine.Mode.C1C3C2得加,不然就會報(bào)錯invalid cipher text
SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
sm2Engine.init(false, privateKeyParameters);
//processBlock得到Base64格式,記得解碼
byte[] arrayOfBytes = null;
try {
arrayOfBytes = Base64.decodeBase64(sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length));
} catch (Exception e) {
e.printStackTrace();
return null;
}
//得到明文:SM2 Encryption Test
return new String(arrayOfBytes);
}
/**
* @Description 生成秘鑰對
* @Author msx
* @return KeyPair
*/
public static KeyPair createECKeyPair() {
//使用標(biāo)準(zhǔn)名稱創(chuàng)建EC參數(shù)生成的參數(shù)規(guī)范
final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
// 獲取一個橢圓曲線類型的密鑰對生成器
final KeyPairGenerator kpg;
try {
kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
// 使用SM2算法域參數(shù)集初始化密鑰生成器(默認(rèn)使用以最高優(yōu)先級安裝的提供者的 SecureRandom 的實(shí)現(xiàn)作為隨機(jī)源)
// kpg.initialize(sm2Spec);
// 使用SM2的算法域參數(shù)集和指定的隨機(jī)源初始化密鑰生成器
kpg.initialize(sm2Spec, new SecureRandom());
// 通過密鑰生成器生成密鑰對
return kpg.generateKeyPair();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* @Description 公鑰加密
* @Author msx
* @param publicKeyHex SM2十六進(jìn)制公鑰
* @param data 明文數(shù)據(jù)
* @return String
*/
public static String encrypt1(String publicKeyHex, String data) {
return encrypt(getECPublicKeyByPublicKeyHex(publicKeyHex), data, 1);
}
/**
* @Description 公鑰加密
* @Author msx
* @param data 明文數(shù)據(jù)
* @return String
*/
public static String encrypt(String data) {
return encrypt(getECPublicKeyByPublicKeyHex(publicKey), data, 1);
}
/**
* @Description 公鑰加密
* @Author msx
* @param publicKey SM2公鑰
* @param data 明文數(shù)據(jù)
* @param modeType 加密模式
* @return String
*/
public static String encrypt(BCECPublicKey publicKey, String data, int modeType) {
//加密模式
SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;
if (modeType != 1) {
mode = SM2Engine.Mode.C1C2C3;
}
//通過公鑰對象獲取公鑰的基本域參數(shù)。
ECParameterSpec ecParameterSpec = publicKey.getParameters();
ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
ecParameterSpec.getG(), ecParameterSpec.getN());
//通過公鑰值和公鑰基本參數(shù)創(chuàng)建公鑰參數(shù)對象
ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(publicKey.getQ(), ecDomainParameters);
//根據(jù)加密模式實(shí)例化SM2公鑰加密引擎
SM2Engine sm2Engine = new SM2Engine(mode);
//初始化加密引擎
sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));
byte[] arrayOfBytes = null;
try {
//將明文字符串轉(zhuǎn)換為指定編碼的字節(jié)串
byte[] in = data.getBytes("utf-8");
//通過加密引擎對字節(jié)數(shù)串行加密
arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
} catch (Exception e) {
System.out.println("SM2加密時(shí)出現(xiàn)異常:" + e.getMessage());
e.printStackTrace();
}
//將加密后的字節(jié)串轉(zhuǎn)換為十六進(jìn)制字符串
return Hex.toHexString(arrayOfBytes);
}
/**
* @Description 私鑰解密
* @Author msx
* @param privateKeyHex SM2十六進(jìn)制私鑰
* @param cipherData 密文數(shù)據(jù)
* @return String
*/
public static String decrypt1(String privateKeyHex, String cipherData) {
return decrypt(getBCECPrivateKeyByPrivateKeyHex(privateKeyHex), cipherData, 1);
}
/**
* @Description 私鑰解密
* @Author msx
* @param s
* @param cipherData 密文數(shù)據(jù)
* @return String
*/
public static String decrypt(String s, String cipherData) {
return decrypt(getBCECPrivateKeyByPrivateKeyHex(privateKey), cipherData, 1);
}
/**
* @Description 私鑰解密
* @Author msx
* @param privateKey SM私鑰
* @param cipherData 密文數(shù)據(jù)
* @param modeType 解密模式
* @return
*/
public static String decrypt(BCECPrivateKey privateKey, String cipherData, int modeType) {
//解密模式
SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;
if (modeType != 1) {
mode = SM2Engine.Mode.C1C2C3;
}
//將十六進(jìn)制字符串密文轉(zhuǎn)換為字節(jié)數(shù)組(需要與加密一致,加密是:加密后的字節(jié)數(shù)組轉(zhuǎn)換為了十六進(jìn)制字符串)
byte[] cipherDataByte = Hex.decode(cipherData);
//通過私鑰對象獲取私鑰的基本域參數(shù)。
ECParameterSpec ecParameterSpec = privateKey.getParameters();
ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
ecParameterSpec.getG(), ecParameterSpec.getN());
//通過私鑰值和私鑰鑰基本參數(shù)創(chuàng)建私鑰參數(shù)對象
ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(privateKey.getD(),
ecDomainParameters);
//通過解密模式創(chuàng)建解密引擎并初始化
SM2Engine sm2Engine = new SM2Engine(mode);
sm2Engine.init(false, ecPrivateKeyParameters);
String result = null;
try {
//通過解密引擎對密文字節(jié)串進(jìn)行解密
byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
//將解密后的字節(jié)串轉(zhuǎn)換為utf8字符編碼的字符串(需要與明文加密時(shí)字符串轉(zhuǎn)換成字節(jié)串所指定的字符編碼保持一致)
result = new String(arrayOfBytes, "utf-8");
} catch (Exception e) {
System.out.println("SM2解密時(shí)出現(xiàn)異常" + e.getMessage());
}
return result;
}
//橢圓曲線ECParameters ASN.1 結(jié)構(gòu)
private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
//橢圓曲線公鑰或私鑰的基本域參數(shù)。
private static ECParameterSpec ecDomainParameters = new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
/**
* @Description 公鑰字符串轉(zhuǎn)換為 BCECPublicKey 公鑰對象
* @Author msx
* @param pubKeyHex 64字節(jié)十六進(jìn)制公鑰字符串(如果公鑰字符串為65字節(jié)首個字節(jié)為0x04:表示該公鑰為非壓縮格式,操作時(shí)需要刪除)
* @return BCECPublicKey SM2公鑰對象
*/
public static BCECPublicKey getECPublicKeyByPublicKeyHex(String pubKeyHex) {
//截取64字節(jié)有效的SM2公鑰(如果公鑰首個字節(jié)為0x04)
if (pubKeyHex.length() > 128) {
pubKeyHex = pubKeyHex.substring(pubKeyHex.length() - 128);
}
//將公鑰拆分為x,y分量(各32字節(jié))
String stringX = pubKeyHex.substring(0, 64);
String stringY = pubKeyHex.substring(stringX.length());
//將公鑰x、y分量轉(zhuǎn)換為BigInteger類型
BigInteger x = new BigInteger(stringX, 16);
BigInteger y = new BigInteger(stringY, 16);
//通過公鑰x、y分量創(chuàng)建橢圓曲線公鑰規(guī)范
ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(x9ECParameters.getCurve().createPoint(x, y), ecDomainParameters);
//通過橢圓曲線公鑰規(guī)范,創(chuàng)建出橢圓曲線公鑰對象(可用于SM2加密及驗(yàn)簽)
return new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION);
}
/**
* @Description 私鑰字符串轉(zhuǎn)換為 BCECPrivateKey 私鑰對象
* @Author msx
* @param privateKeyHex 32字節(jié)十六進(jìn)制私鑰字符串
* @return BCECPrivateKey SM2私鑰對象
*/
public static BCECPrivateKey getBCECPrivateKeyByPrivateKeyHex(String privateKeyHex) {
//將十六進(jìn)制私鑰字符串轉(zhuǎn)換為BigInteger對象
BigInteger d = new BigInteger(privateKeyHex, 16);
//通過私鑰和私鑰域參數(shù)集創(chuàng)建橢圓曲線私鑰規(guī)范
ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(d, ecDomainParameters);
//通過橢圓曲線私鑰規(guī)范,創(chuàng)建出橢圓曲線私鑰對象(可用于SM2解密和簽名)
return new BCECPrivateKey("EC", ecPrivateKeySpec, BouncyCastleProvider.CONFIGURATION);
}
public static void main(String[] args) {
String publicKeyHex = null;
String privateKeyHex = null;
KeyPair keyPair = createECKeyPair();
PublicKey publicKey = keyPair.getPublic();
if (publicKey instanceof BCECPublicKey) {
//獲取65字節(jié)非壓縮縮的十六進(jìn)制公鑰串(0x04)
publicKeyHex = Hex.toHexString(((BCECPublicKey) publicKey).getQ().getEncoded(false));
System.out.println("---->SM2公鑰:" + publicKeyHex);
}
PrivateKey privateKey = keyPair.getPrivate();
if (privateKey instanceof BCECPrivateKey) {
//獲取32字節(jié)十六進(jìn)制私鑰串
privateKeyHex = ((BCECPrivateKey) privateKey).getD().toString(16);
System.out.println("---->SM2私鑰:" + privateKeyHex);
}
/**
* 公鑰加密
*/
String data = "=========待加密數(shù)據(jù)=========";
//將十六進(jìn)制公鑰串轉(zhuǎn)換為 BCECPublicKey 公鑰對象
String encryptData = encrypt1(publicKeyHex, data);
System.out.println("---->加密結(jié)果:" + encryptData);
/**
* 私鑰解密
*/
//將十六進(jìn)制私鑰串轉(zhuǎn)換為 BCECPrivateKey 私鑰對象
data = decrypt1(privateKeyHex, encryptData);
System.out.println("---->解密結(jié)果:" + data);
}
}最后,在后端接口中調(diào)用工具類的解密方法,就可以對前端傳來的加密數(shù)據(jù)進(jìn)行解密。
擴(kuò)展資料:
國密即國家密碼局認(rèn)定的國產(chǎn)密碼算法。主要有SM1,SM2,SM3,SM4。密鑰長度和分組長度均為128位。
(1)SM1 為對稱加密。其加密強(qiáng)度與AES相當(dāng)。該算法不公開,調(diào)用該算法時(shí),需要通過加密芯片的接口進(jìn)行調(diào)用。
(2)SM2為非對稱加密,基于ECC。該算法已公開。由于該算法基于ECC,故其簽名速度與秘鑰生成速度都快于RSA。ECC 256位(SM2采用的就是ECC 256位的一種)安全強(qiáng)度比RSA 2048位高,但運(yùn)算速度快于RSA。
(3)SM3 消息摘要。可以用MD5作為對比理解。該算法已公開。校驗(yàn)結(jié)果為256位。
(4)SM4 無線局域網(wǎng)標(biāo)準(zhǔn)的分組數(shù)據(jù)算法。對稱加密,密鑰長度和分組長度均為128位。
至此,??ヽ(°▽°)ノ?撒花。
總結(jié)
到此這篇關(guān)于三步輕松實(shí)現(xiàn)Java的SM2前端加密后端解密的文章就介紹到這了,更多相關(guān)Java SM2前端加密后端解密內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Boot集群管理工具KafkaAdminClient使用方法解析
這篇文章主要介紹了Spring Boot集群管理工具KafkaAdminClient使用方法解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02
詳解Java synchronized關(guān)鍵字的用法
在多線程編程中常常使用鎖機(jī)制來確保同一時(shí)刻只有一個線程能夠修改共享內(nèi)存,在Java中一般是使用synchronized作為鎖機(jī)制,下面就讓我們來學(xué)習(xí)一下如何使用synchronized實(shí)現(xiàn)線程安全吧2023-08-08
SpringBoot+Redis使用AOP防止重復(fù)提交的實(shí)現(xiàn)
本文主要介紹了SpringBoot+Redis使用AOP防止重復(fù)提交的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
mybatisplus?selectOne查詢,有數(shù)據(jù),但返回為null問題
這篇文章主要介紹了mybatisplus?selectOne查詢,有數(shù)據(jù),但返回為null問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11
MyBatis中resultMap和resultType的區(qū)別詳解
這篇文章主要介紹了MyBatis中resultMap和resultType的區(qū)別詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07

