import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.*;
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.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.encoders.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
public class SM2Utils {
private static final LoggerLOGGER = LoggerFactory.getLogger(SM2Utils.class);
? ? /**
? ? * @Description 生成秘鑰對(duì)
? ? * @Author zd
? ? * @return KeyPair
*/
? ? public static KeyPaircreateECKeyPair() {
//使用標(biāo)準(zhǔn)名稱創(chuàng)建EC參數(shù)生成的參數(shù)規(guī)范
? ? ? ? final ECGenParameterSpec sm2Spec =new ECGenParameterSpec("sm2p256v1");
? ? ? ? // 獲取一個(gè)橢圓曲線類型的密鑰對(duì)生成器
? ? ? ? final KeyPairGenerator kpg;
? ? ? ? try {
kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
? ? ? ? ? ? // 使用SM2算法域參數(shù)集初始化密鑰生成器(默認(rèn)使用以最高優(yōu)先級(jí)安裝的提供者的 SecureRandom 的實(shí)現(xiàn)作為隨機(jī)源)
? ? ? ? ? ? // kpg.initialize(sm2Spec);
? ? ? ? ? ? // 使用SM2的算法域參數(shù)集和指定的隨機(jī)源初始化密鑰生成器
? ? ? ? ? ? kpg.initialize(sm2Spec, new SecureRandom());
? ? ? ? ? ? // 通過密鑰生成器生成密鑰對(duì)
? ? ? ? ? ? return kpg.generateKeyPair();
? ? ? ? }catch (Exception e) {
e.printStackTrace();
return null;
? ? ? ? }
}
/**
? ? * 生成SM2公私鑰對(duì)
? ? * @return
? ? */
? ? private static AsymmetricCipherKeyPairgenKeyPair0() {
//獲取一條SM2曲線參數(shù)
? ? ? ? X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
? ? ? ? //構(gòu)造domain參數(shù)
? ? ? ? ECDomainParameters domainParameters =new ECDomainParameters(sm2ECParameters.getCurve(),
? ? ? ? ? ? ? ? sm2ECParameters.getG(), sm2ECParameters.getN());
? ? ? ? //1.創(chuàng)建密鑰生成器
? ? ? ? ECKeyPairGenerator keyPairGenerator =new ECKeyPairGenerator();
? ? ? ? //2.初始化生成器,帶上隨機(jī)數(shù)
? ? ? ? try {
keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));
? ? ? ? }catch (NoSuchAlgorithmException e) {
LOGGER.error("生成公私鑰對(duì)時(shí)出現(xiàn)異常:", e);
//? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
//3.生成密鑰對(duì)
? ? ? ? AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();
? ? ? ? return asymmetricCipherKeyPair;
? ? }
/**
? ? * @Description 公鑰加密
? ? * @Author zd
? ? * @param publicKeyHex SM2十六進(jìn)制公鑰
? ? * @param data? ? ? ? 明文數(shù)據(jù)
? ? * @return String
*/
? ? public static Stringencrypt(String publicKeyHex, String data) {
return encrypt(getECPublicKeyByPublicKeyHex(publicKeyHex), data, 1);
? ? }
/**
? ? * @Description 公鑰加密
? ? * @Author zd
? ? * @param publicKey SM2公鑰
? ? * @param data? ? ? 明文數(shù)據(jù)
? ? * @param modeType? 加密模式
? ? * @return String
*/
? ? public static Stringencrypt(BCECPublicKey publicKey, String data, int modeType) {
//加密模式
? ? ? ? SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;
? ? ? ? if (modeType !=1) {
mode = SM2Engine.Mode.C1C2C3;
? ? ? ? }
//通過公鑰對(duì)象獲取公鑰的基本域參數(shù)盒粮。
? ? ? ? ECParameterSpec ecParameterSpec = publicKey.getParameters();
? ? ? ? ECDomainParameters ecDomainParameters =new ECDomainParameters(ecParameterSpec.getCurve(),
? ? ? ? ? ? ? ? ecParameterSpec.getG(), ecParameterSpec.getN());
? ? ? ? //通過公鑰值和公鑰基本參數(shù)創(chuàng)建公鑰參數(shù)對(duì)象
? ? ? ? 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");
? ? ? ? ? ? //通過加密引擎對(duì)字節(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 zd
? ? * @param privateKeyHex SM2十六進(jìn)制私鑰
? ? * @param cipherData? ? 密文數(shù)據(jù)
? ? * @return String
*/
? ? public static Stringdecrypt(String privateKeyHex, String cipherData) {
return decrypt(getBCECPrivateKeyByPrivateKeyHex(privateKeyHex), cipherData, 1);
? ? }
/**
? ? * @Description 私鑰解密
? ? * @Author zd
? ? * @param privateKey SM私鑰
? ? * @param cipherData 密文數(shù)據(jù)
? ? * @param modeType? 解密模式
? ? * @return
? ? */
? ? public static Stringdecrypt(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);
? ? ? ? //通過私鑰對(duì)象獲取私鑰的基本域參數(shù)矗蕊。
? ? ? ? ECParameterSpec ecParameterSpec = privateKey.getParameters();
? ? ? ? ECDomainParameters ecDomainParameters =new ECDomainParameters(ecParameterSpec.getCurve(),
? ? ? ? ? ? ? ? ecParameterSpec.getG(), ecParameterSpec.getN());
? ? ? ? //通過私鑰值和私鑰鑰基本參數(shù)創(chuàng)建私鑰參數(shù)對(duì)象
? ? ? ? ECPrivateKeyParameters ecPrivateKeyParameters =new ECPrivateKeyParameters(privateKey.getD(),
? ? ? ? ? ? ? ? ecDomainParameters);
? ? ? ? //通過解密模式創(chuàng)建解密引擎并初始化
? ? ? ? SM2Engine sm2Engine =new SM2Engine(mode);
? ? ? ? sm2Engine.init(false, ecPrivateKeyParameters);
? ? ? ? String result =null;
? ? ? ? try {
//通過解密引擎對(duì)密文字節(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 X9ECParametersx9ECParameters = GMNamedCurves.getByName("sm2p256v1");
? ? //橢圓曲線公鑰或私鑰的基本域參數(shù)丁存。
? ? private static ECParameterSpececDomainParameters =new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
? ? /**
? ? * @Description 公鑰字符串轉(zhuǎn)換為 BCECPublicKey 公鑰對(duì)象
? ? * @Author zd
? ? * @param pubKeyHex 64字節(jié)十六進(jìn)制公鑰字符串(如果公鑰字符串為65字節(jié)首個(gè)字節(jié)為0x04:表示該公鑰為非壓縮格式,操作時(shí)需要?jiǎng)h除)
? ? * @return BCECPublicKey SM2公鑰對(duì)象
? ? */
? ? public static BCECPublicKeygetECPublicKeyByPublicKeyHex(String pubKeyHex) {
//截取64字節(jié)有效的SM2公鑰(如果公鑰首個(gè)字節(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)建出橢圓曲線公鑰對(duì)象(可用于SM2加密及驗(yàn)簽)
? ? ? ? return new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION);
? ? }
/**
? ? * @Description 私鑰字符串轉(zhuǎn)換為 BCECPrivateKey 私鑰對(duì)象
? ? * @Author zd
? ? * @param privateKeyHex 32字節(jié)十六進(jìn)制私鑰字符串
? ? * @return BCECPrivateKey SM2私鑰對(duì)象
? ? */
? ? public static BCECPrivateKeygetBCECPrivateKeyByPrivateKeyHex(String privateKeyHex) {
//將十六進(jìn)制私鑰字符串轉(zhuǎn)換為BigInteger對(duì)象
? ? ? ? BigInteger d =new BigInteger(privateKeyHex, 16);
? ? ? ? //通過私鑰和私鑰域參數(shù)集創(chuàng)建橢圓曲線私鑰規(guī)范
? ? ? ? ECPrivateKeySpec ecPrivateKeySpec =new ECPrivateKeySpec(d, ecDomainParameters);
? ? ? ? //通過橢圓曲線私鑰規(guī)范茂浮,創(chuàng)建出橢圓曲線私鑰對(duì)象(可用于SM2解密和簽名)
? ? ? ? return new BCECPrivateKey("EC", ecPrivateKeySpec, BouncyCastleProvider.CONFIGURATION);
? ? }
/**
? ? * 私鑰簽名
? ? * @param privateKey? ? 私鑰
? ? * @param content? ? ? 待簽名內(nèi)容
? ? * @return
? ? */
? ? public static Stringsign(String privateKey, String content)throws CryptoException {
//待簽名內(nèi)容轉(zhuǎn)為字節(jié)數(shù)組
? ? ? ? byte[] message = Hex.decode(content);
? ? ? ? //獲取一條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(privateKey, 16);
? ? ? ? ECPrivateKeyParameters privateKeyParameters =new ECPrivateKeyParameters(privateKeyD, domainParameters);
? ? ? ? //創(chuàng)建簽名實(shí)例
? ? ? ? SM2Signer sm2Signer =new SM2Signer();
? ? ? ? //初始化簽名實(shí)例,帶上ID,國密的要求,ID默認(rèn)值:1234567812345678
? ? ? ? try {
sm2Signer.init(true, new ParametersWithID(new ParametersWithRandom(privateKeyParameters, SecureRandom.getInstance("SHA1PRNG")), Strings.toByteArray("1234567812345678")));
? ? ? ? }catch (NoSuchAlgorithmException e) {
log.error("簽名時(shí)出現(xiàn)異常:", e);
? ? ? ? }
sm2Signer.update(message, 0, message.length);
? ? ? ? //生成簽名,簽名分為兩部分r和s,分別對(duì)應(yīng)索引0和1的數(shù)組
? ? ? ? byte[] signBytes = sm2Signer.generateSignature();
? ? ? ? String sign = Hex.toHexString(signBytes);
? ? ? ? return sign;
? ? }
/**
? ? * 驗(yàn)證簽名
? ? * @param publicKey? ? 公鑰
? ? * @param content? ? ? 待簽名內(nèi)容
? ? * @param sign? ? ? ? ? 簽名值
? ? * @return
? ? */
? ? public static boolean verify(String publicKey, String content, String sign) {
//待簽名內(nèi)容
? ? ? ? byte[] message = Hex.decode(content);
? ? ? ? byte[] signData = Hex.decode(sign);
? ? ? ? // 獲取一條SM2曲線參數(shù)
? ? ? ? X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
? ? ? ? // 構(gòu)造domain參數(shù)
? ? ? ? ECDomainParameters domainParameters =new ECDomainParameters(sm2ECParameters.getCurve(),
? ? ? ? ? ? ? ? sm2ECParameters.getG(),
? ? ? ? ? ? ? ? sm2ECParameters.getN());
? ? ? ? //提取公鑰點(diǎn)
? ? ? ? ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKey));
? ? ? ? // 公鑰前面的02或者03表示是壓縮公鑰,04表示未壓縮公鑰, 04的時(shí)候壳咕,可以去掉前面的04
? ? ? ? ECPublicKeyParameters publicKeyParameters =new ECPublicKeyParameters(pukPoint, domainParameters);
? ? ? ? //創(chuàng)建簽名實(shí)例
? ? ? ? SM2Signer sm2Signer =new SM2Signer();
? ? ? ? ParametersWithID parametersWithID =new ParametersWithID(publicKeyParameters, Strings.toByteArray("1234567812345678"));
? ? ? ? sm2Signer.init(false, parametersWithID);
? ? ? ? sm2Signer.update(message, 0, message.length);
? ? ? ? //驗(yàn)證簽名結(jié)果
? ? ? ? boolean verify = sm2Signer.verifySignature(signData);
? ? ? ? return verify;
? ? }
public static void main(String[] args)throws UnsupportedEncodingException, CryptoException, NoSuchAlgorithmException, InvalidAlgorithmParameterException {
String data ="{\"head\":{\"reqCode\":\"1001\",\"reqDate\":\"20220321\",\"reqTime\":\"231815\",\"reqId\":\"2022\",\"bbkNbr\":\"379\"},\"body\":{\"regCode\":\"8238003300209049600\"}}";
? ? ? ? String timestamp = String.valueOf(System.currentTimeMillis());
? ? ? ? AsymmetricCipherKeyPair asymmetricCipherKeyPair =genKeyPair0();
? ? ? ? //提取公鑰點(diǎn)
? ? ? ? ECPoint ecPoint = ((ECPublicKeyParameters) asymmetricCipherKeyPair.getPublic()).getQ();
? ? ? ? //公鑰前面的02或者03表示是壓縮公鑰,04表示未壓縮公鑰,04的時(shí)候,可以去掉前面的04
? ? ? ? String publicStr = Hex.toHexString(ecPoint.getEncoded(false));
? ? ? ? BigInteger privatekey = ((ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate()).getD();
? ? ? ? String privateStr = privatekey.toString(16);
? ? ? ? System.out.println("---->生成公鑰:" + publicStr);
? ? ? ? System.out.println("---->生成私鑰:" + privateStr);
? ? ? ? String encryptData =encrypt(publicStr, data);
? ? ? ? System.out.println("---->加密結(jié)果:" + encryptData);
? ? ? ? String decryptData =decrypt(privateStr,encryptData);
? ? ? ? System.out.println("---->解密結(jié)果:" + decryptData);
? ? ? ? String sign = SM2Utils.sign(privateStr,Hex.toHexString((data + timestamp).getBytes()));
? ? ? ? System.out.println("---->簽名結(jié)果:" + sign);
? ? ? ? boolean verify = SM2Utils.verify(publicStr, Hex.toHexString((data + timestamp).getBytes()), sign);
? ? ? ? System.out.println("---->驗(yàn)簽結(jié)果:" + verify);
? ? }
}