Java?SM2加密相關(guān)實(shí)現(xiàn)與簡(jiǎn)單原理詳解
首先我們應(yīng)該了解SM2加密的主要用途:數(shù)字簽名、密鑰交換和公鑰加密等應(yīng)用。
以下為SM2加密的簡(jiǎn)單原理:
密鑰生成:首先,生成一對(duì)公鑰和私鑰。公鑰用于加密和驗(yàn)證簽名,私鑰用于解密和生成簽名。
加密過(guò)程:
- 隨機(jī)選擇一個(gè)臨時(shí)的非零整數(shù)k,計(jì)算橢圓曲線點(diǎn)C = k * G,其中G是曲線上的基點(diǎn)。
- 將明文數(shù)據(jù)轉(zhuǎn)換為橢圓曲線上的點(diǎn)M。
- 計(jì)算橢圓曲線點(diǎn)C1 = k * G。
- 計(jì)算橢圓曲線點(diǎn)S = (h + x) * C1,其中h是哈希值,x是私鑰。
- 將明文數(shù)據(jù)與S進(jìn)行異或運(yùn)算,得到密文C2。
- 將C1和C2組合在一起作為最終的加密結(jié)果。
解密過(guò)程:
- 使用私鑰x計(jì)算橢圓曲線點(diǎn)C1' = x * C1。
- 從C1'中提取出明文數(shù)據(jù)M'。
- 將M'與密文C2進(jìn)行異或運(yùn)算,得到原始的明文數(shù)據(jù)。
我們要了解對(duì)于同一個(gè)公鑰和同一個(gè)數(shù)據(jù),多次加密的密文是不相同的?。?!
import cn.hutool.json.JSONObject;
import com.alibaba.fastjson.JSON;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.CipherParameters;
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.crypto.signers.SM2Signer;
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.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.Base64;
/**
* SM2工具類(lèi)
* @author van
*/
public class SM2Util {
/**
* 生成 SM2 公私鑰對(duì)
*
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidAlgorithmParameterException
*/
public static KeyPair geneSM2KeyPair() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
// 獲取一個(gè)橢圓曲線類(lèi)型的密鑰對(duì)生成器
final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
// 產(chǎn)生隨機(jī)數(shù)
SecureRandom secureRandom = new SecureRandom();
// 使用SM2參數(shù)初始化生成器
kpg.initialize(sm2Spec, secureRandom);
// 獲取密鑰對(duì)
KeyPair keyPair = kpg.generateKeyPair();
return keyPair;
}
/**
* 生產(chǎn)hex秘鑰對(duì)
*/
public static void geneSM2HexKeyPair(){
try {
KeyPair keyPair = geneSM2KeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
System.out.println("======== EC X Y keyPair ========");
System.out.println(privateKey);
System.out.println(publicKey);
System.out.println("======== hex keyPair ========");
System.out.println("hex priKey: " + getPriKeyHexString(privateKey));
System.out.println("hex pubKey: " + getPubKeyHexString(publicKey));
System.out.println("======== base64 keyPair ========");
System.out.println("base64 priKey: " + new String(Base64.getEncoder().encode(privateKey.getEncoded())));
System.out.println("base64 pubKey: " + new String(Base64.getEncoder().encode(publicKey.getEncoded())));
System.out.println("======== generate finished ========");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 獲取私鑰(16進(jìn)制字符串,頭部不帶00長(zhǎng)度共64)
*
* @param privateKey 私鑰PrivateKey型
* @return
*/
public static String getPriKeyHexString(PrivateKey privateKey) {
// OK
// BCECPrivateKey s=(BCECPrivateKey)privateKey;
// String priKeyHexString = Hex.toHexString(s.getD().toByteArray());
// if(null!= priKeyHexString && priKeyHexString.length()==66 && "00".equals(priKeyHexString.substring(0,2))){
// return priKeyHexString.substring(2);
// }
// OK
BCECPrivateKey key = (BCECPrivateKey) privateKey;
BigInteger intPrivateKey = key.getD();
String priKeyHexString = intPrivateKey.toString(16);
return priKeyHexString;
}
/**
* 獲取私鑰 base64字符串
*
* @param privateKey 私鑰PrivateKey型
* @return
*/
public static String getPriKeyBase64String(PrivateKey privateKey) {
return new String(Base64.getEncoder().encode(privateKey.getEncoded()));
}
/**
* 獲取公鑰(16進(jìn)制字符串,頭部帶04長(zhǎng)度共130)
*
* @param publicKey 公鑰PublicKey型
* @return
*/
public static String getPubKeyHexString(PublicKey publicKey) {
BCECPublicKey key = (BCECPublicKey) publicKey;
return Hex.toHexString(key.getQ().getEncoded(false));
}
/**
* 獲取公鑰 base64字符串
*
* @param publicKey 公鑰PublicKey型
* @return
*/
public static String getPubKeyBase64String(PublicKey publicKey) {
return new String(Base64.getEncoder().encode(publicKey.getEncoded()));
}
/**
* SM2加密算法
*
* @param publicKey 公鑰
* @param data 明文數(shù)據(jù)
* @return
*/
public static String encrypt(String data, PublicKey publicKey) {
return encrypt(data.getBytes(StandardCharsets.UTF_8), publicKey);
}
public static String encrypt(byte[] data, PublicKey publicKey) {
BCECPublicKey key = (BCECPublicKey) publicKey;
return encrypt(data, Hex.toHexString(key.getQ().getEncoded(false)));
}
public static String encrypt(String data, String pubKeyHexString) {
return encrypt(data.getBytes(StandardCharsets.UTF_8), pubKeyHexString);
}
/**
* SM2加密算法
*
* @param pubKeyHexString 公鑰(16進(jìn)制字符串)
* @param data 明文數(shù)據(jù)
* @return hex字符串
*/
public static String encrypt(byte[] data, String pubKeyHexString) {
// 獲取一條SM2曲線參數(shù)
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
// 構(gòu)造ECC算法參數(shù),曲線方程、橢圓曲線G點(diǎn)、大整數(shù)N
ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
//提取公鑰點(diǎn)
ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(pubKeyHexString));
// 公鑰前面的02或者03表示是壓縮公鑰,04表示未壓縮公鑰, 04的時(shí)候,可以去掉前面的04
ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
// 設(shè)置sm2為加密模式
sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
byte[] arrayOfBytes = null;
try {
arrayOfBytes = sm2Engine.processBlock(data, 0, data.length);
} catch (Exception e) {
System.out.println("SM2加密時(shí)出現(xiàn)異常:" + e.getMessage());
}
return Hex.toHexString(arrayOfBytes);
}
/**
* SM2解密算法
* @param cipherData hex格式密文
* @param privateKey 密鑰PrivateKey型
* @return 明文
*/
public static String decrypt(String cipherData, PrivateKey privateKey) {
return decrypt(Hex.decode(cipherData), privateKey);
}
public static String decrypt(byte[] cipherData, PrivateKey privateKey) {
BCECPrivateKey key = (BCECPrivateKey) privateKey;
return decrypt(cipherData, Hex.toHexString(key.getD().toByteArray()));
}
public static String decrypt(String cipherData, String priKeyHexString) {
// 使用BC庫(kù)加解密時(shí)密文以04開(kāi)頭,傳入的密文前面沒(méi)有04則補(bǔ)上
if (!cipherData.startsWith("04")) {
cipherData = "04" + cipherData;
}
return decrypt(Hex.decode(cipherData), priKeyHexString);
}
/**
* SM2解密算法
*
* @param cipherData 密文數(shù)據(jù)
* @param priKeyHexString 私鑰(16進(jìn)制字符串)
* @return
*/
public static String decrypt(byte[] cipherData, String priKeyHexString) {
//獲取一條SM2曲線參數(shù)
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
//構(gòu)造domain參數(shù)
ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
BigInteger privateKeyD = new BigInteger(priKeyHexString, 16);
ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);
SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
// 設(shè)置sm2為解密模式
sm2Engine.init(false, privateKeyParameters);
String result = "";
try {
byte[] arrayOfBytes = sm2Engine.processBlock(cipherData, 0, cipherData.length);
return new String(arrayOfBytes);
} catch (Exception e) {
System.out.println("SM2解密時(shí)出現(xiàn)異常:" + e.getMessage());
}
return result;
}
/**
* @param data
* @param priKeyHexString hex私鑰,長(zhǎng)度64
* @return hex格式簽名值
* @throws Exception
*/
public static String sign(String data, String priKeyHexString) throws Exception {
return sign(data.getBytes(StandardCharsets.UTF_8), priKeyHexString);
}
/**
* 簽名
* @param data 原始數(shù)據(jù),字節(jié)數(shù)組
* @param priKeyHexString hex私鑰,64長(zhǎng)度
* @return Hex字符串
* @throws Exception
*/
public static String sign(byte[] data, String priKeyHexString) throws Exception {
String signValue = null;
SM2Signer signer = new SM2Signer();
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
//構(gòu)造domain參數(shù)
ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
CipherParameters param = new ParametersWithRandom(new ECPrivateKeyParameters(new BigInteger(priKeyHexString, 16), domainParameters));
signer.init(true, param);
signer.update(data, 0, data.length);
signValue = Hex.toHexString(signer.generateSignature());
return signValue;
}
/**
* 驗(yàn)簽
* @param data 原始數(shù)據(jù)
* @param signValue 原始簽名值(hex型)
* @param publicKeyHexString hex130長(zhǎng)度公鑰
* @return ture or false
* @throws Exception
*/
public static boolean verify(String data, String signValue, String publicKeyHexString) throws Exception {
return verify(data.getBytes(StandardCharsets.UTF_8), Hex.decode(signValue), publicKeyHexString);
}
/**
* 驗(yàn)簽
* @param data 原始數(shù)據(jù)字節(jié)數(shù)組
* @param sign 字節(jié)數(shù)組()
* @param publicKeyHexString hex130長(zhǎng)度公鑰
* @return true or false
* @throws Exception
*/
public static boolean verify(byte[] data, byte[] sign, String publicKeyHexString) throws Exception {
SM2Signer signer = new SM2Signer();
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
//構(gòu)造domain參數(shù)
ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
if (publicKeyHexString.length() == 128) {
publicKeyHexString = "04" + publicKeyHexString;
}
ECPoint ecPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKeyHexString));
CipherParameters param = new ECPublicKeyParameters(ecPoint, domainParameters);
signer.init(false, param);
signer.update(data, 0, data.length);
return signer.verifySignature(sign);
}
/**
* 私鑰生成公鑰
* @param priKeyHexString 私鑰Hex格式,必須64位
* @return 公鑰Hex格式,04開(kāi)頭,130位
* @throws Exception 例如:
* 04181db7fe400641115c0dec08e23d8ddb94c5999f2fb6efd03030780142e077a63eb4d47947ef5baee7f40fec2c29181d2a714d9c6cba87b582f252a4e3e9a9f8
* 11d0a44d47449d48d614f753ded6b06af76033b9c3a2af2b8b2239374ccbce3a
*/
public static String getPubKeyByPriKey(String priKeyHexString) throws Exception {
if (priKeyHexString == null || priKeyHexString.length() != 64) {
System.err.println("priKey 必須是Hex 64位格式,例如:11d0a44d47449d48d614f753ded6b06af76033b9c3a2af2b8b2239374ccbce3a");
return "";
}
String pubKeyHexString = null;
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
//構(gòu)造domain參數(shù)
BigInteger privateKeyD = new BigInteger(priKeyHexString, 16);
ECParameterSpec ecParameterSpec = new ECParameterSpec(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(privateKeyD, ecParameterSpec);
PrivateKey privateKey = null;
privateKey = KeyFactory.getInstance("EC", new BouncyCastleProvider()).generatePrivate(ecPrivateKeySpec);
// 臨時(shí)解決辦法
String pointString = privateKey.toString();
// System.out.println(pointString);
String pointString_X = pointString.substring(pointString.indexOf("X: ") + "X: ".length(), pointString.indexOf("Y: ")).trim();
String pointString_Y = pointString.substring(pointString.indexOf("Y: ") + "Y: ".length()).trim();
// System.out.println(pointString_X);
// System.out.println(pointString_Y);
pubKeyHexString = "04" + pointString_X + pointString_Y;
return pubKeyHexString;
}
// 將字符串轉(zhuǎn)換為十六進(jìn)制字符串
public static String stringToHex(String str) {
StringBuilder hexString = new StringBuilder();
for (char ch : str.toCharArray()) {
hexString.append(Integer.toHexString((int) ch));
}
return hexString.toString();
}
public static void main(String[] args) throws Exception {
System.out.println("====== sm2utils test ======");
JSONObject jsonObject = new JSONObject();
jsonObject.put("username","jsdxzx");
jsonObject.put("password","1");
jsonObject.put("clientId","jsdxzx");
jsonObject.put("grantType","password_credentials");
jsonObject.put("passwordEncode",true);
String jsonString = JSON.toJSONString(jsonObject);
String M = jsonString;
System.out.println("mingwen\t" + M);
System.out.println("begin 開(kāi)始生成密鑰對(duì)>>>");
KeyPair keyPair = geneSM2KeyPair();
PublicKey publicKey = keyPair.getPublic();
String pubKeyHexString = getPubKeyHexString(publicKey);
System.out.println("publicKey\t" + pubKeyHexString);
PrivateKey privateKey = keyPair.getPrivate();
String priKeyHexString = getPriKeyHexString(privateKey);
System.out.println("privateKey\t" + priKeyHexString);
System.out.println("end 結(jié)束生成密鑰對(duì)>>>");
priKeyHexString="4f0341e5977b175e0ec0e1fdefd2799b13dd25c51716133ef42ba76c0edd973d"; //1
pubKeyHexString="04e523210b3407464839723558e0d82765b9e2cac9491bd86c99c89b9fc43fbe9a94395ee3138dbc4ae43daa8fe01fd512de8568102e34c66989eb2b306611b518"; //1
System.out.println("publicKey\t" + pubKeyHexString);
String cipherData = encrypt(M, pubKeyHexString);
System.out.println("miwen\t" + cipherData);
String text = decrypt(cipherData, priKeyHexString);
System.out.println("jiemi\t" + text);
String sign = sign(M, priKeyHexString);
System.out.println("signvalue\t" + sign);
sign="304402204bbd4b026f58f1e072c2ab1f736a730ed5c2f6773ef4855df5e87f9ea54f14be02205e9b6146b5273e6f37fe6d9d8f059bc46f7042a10da224130a4e0ba8619d967c";
boolean verifyResult = verify(M, sign, pubKeyHexString);
System.out.println("verifyResult\t" + verifyResult);
}
}總結(jié)
到此這篇關(guān)于Java SM2加密相關(guān)實(shí)現(xiàn)與簡(jiǎn)單原理的文章就介紹到這了,更多相關(guān)Java SM2加密實(shí)現(xiàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
通過(guò)Java實(shí)現(xiàn)zip文件與rar文件解壓縮的詳細(xì)步驟
這篇文章主要給大家介紹了如何通過(guò)?Java?來(lái)完成?zip?文件與?rar?文件的解壓縮,文中通過(guò)代碼示例講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-07-07
SpringBoot 創(chuàng)建容器的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot 創(chuàng)建容器的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
SpringBoot項(xiàng)目打包發(fā)布到外部tomcat(出現(xiàn)各種異常的解決)
這篇文章主要介紹了SpringBoot項(xiàng)目打包發(fā)布到外部tomcat(出現(xiàn)各種異常的解決),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
Spring Security單項(xiàng)目權(quán)限設(shè)計(jì)過(guò)程解析
這篇文章主要介紹了Spring Security單項(xiàng)目權(quán)限設(shè)計(jì)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
SpringSecurity集成第三方登錄過(guò)程詳解(最新推薦)
在ThirdAuthenticationFilter 類(lèi)的attemptAuthentication()方法中,我們通過(guò)authType類(lèi)型,然后創(chuàng)建對(duì)應(yīng)的Authentication實(shí)現(xiàn)來(lái)實(shí)現(xiàn)不同方式的登錄,下面給大家分享SpringSecurity集成第三方登錄過(guò)程,感興趣的朋友一起看看吧2024-05-05

