packagecom.pcidata.common.tools.encrypt;importcom.pcidata.common.tools.CommonUtils;importcom.pcidata.common.tools.CustomStringUtils;importcom.pcidata.modules.key.modelvo.response.PCIKeyPair;importlombok.extern.slf4j.Slf4j;importorg.bouncycastle.asn1.gm.GMNamedCurves;importorg.bouncycastle.asn1.x9.X9ECParameters;importorg.bouncycastle.crypto.AsymmetricCipherKeyPair;importorg.bouncycastle.crypto.engines.SM2Engine;importorg.bouncycastle.crypto.generators.ECKeyPairGenerator;import org.bouncycastle.crypto.params.*;importorg.bouncycastle.crypto.signers.SM2Signer;importorg.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;importorg.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;importorg.bouncycastle.jce.spec.ECParameterSpec;importorg.bouncycastle.math.ec.ECPoint;importorg.bouncycastle.pqc.math.linearalgebra.ByteUtils;importorg.bouncycastle.util.Strings;importorg.bouncycastle.util.encoders.Hex;importjava.math.BigInteger;importjava.security.NoSuchAlgorithmException;importjava.security.PrivateKey;importjava.security.PublicKey;importjava.security.SecureRandom;/*** @Author: dzy
* @Date: 2018/9/28 15:53
* @Describe: SM2工具类*/@Slf4jpublic classSM2Util {/*** 生成SM2公私钥对
*@return
*/
private staticAsymmetricCipherKeyPair genKeyPair0() {//获取一条SM2曲线参数
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");//构造domain参数
ECDomainParameters domainParameters = newECDomainParameters(sm2ECParameters.getCurve(),
sm2ECParameters.getG(), sm2ECParameters.getN());//1.创建密钥生成器
ECKeyPairGenerator keyPairGenerator = newECKeyPairGenerator();//2.初始化生成器,带上随机数
try{
keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));
}catch(NoSuchAlgorithmException e) {
log.error("生成公私钥对时出现异常:", e);//e.printStackTrace();
}//3.生成密钥对
AsymmetricCipherKeyPair asymmetricCipherKeyPair =keyPairGenerator.generateKeyPair();returnasymmetricCipherKeyPair;
}/*** 生成公私钥对(默认压缩公钥)
*@return
*/
public staticPCIKeyPair genKeyPair() {return genKeyPair(true);
}/*** 生成公私钥对
*@paramcompressedPubKey 是否压缩公钥
*@return
*/
public static PCIKeyPair genKeyPair(booleancompressedPubKey) {
AsymmetricCipherKeyPair asymmetricCipherKeyPair=genKeyPair0();//提取公钥点
ECPoint ecPoint =((ECPublicKeyParameters) asymmetricCipherKeyPair.getPublic()).getQ();//公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥,04的时候,可以去掉前面的04
String pubKey =Hex.toHexString(ecPoint.getEncoded(compressedPubKey));
BigInteger privatekey=((ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate()).getD();
String priKey= privatekey.toString(16);
PCIKeyPair keyPair= newPCIKeyPair(priKey, pubKey);returnkeyPair;
}/*** 私钥签名
*@paramprivateKey 私钥
*@paramcontent 待签名内容
*@return
*/
public staticString sign(String privateKey, String content) {//待签名内容转为字节数组
byte[] message =Hex.decode(content);//获取一条SM2曲线参数
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");//构造domain参数
ECDomainParameters domainParameters = newECDomainParameters(sm2ECParameters.getCurve(),
sm2ECParameters.getG(), sm2ECParameters.getN());
BigInteger privateKeyD= new BigInteger(privateKey, 16);
ECPrivateKeyParameters privateKeyParameters= newECPrivateKeyParameters(privateKeyD, domainParameters);//创建签名实例
SM2Signer sm2Signer = newSM2Signer();//初始化签名实例,带上ID,国密的要求,ID默认值:1234567812345678
try{
sm2Signer.init(true, new ParametersWithID(new ParametersWithRandom(privateKeyParameters, SecureRandom.getInstance("SHA1PRNG")), Strings.toByteArray("1234567812345678")));
}catch(NoSuchAlgorithmException e) {
log.error("签名时出现异常:", e);
}//生成签名,签名分为两部分r和s,分别对应索引0和1的数组
BigInteger[] bigIntegers =sm2Signer.generateSignature(message);byte[] rBytes = modifyRSFixedBytes(bigIntegers[0].toByteArray());byte[] sBytes = modifyRSFixedBytes(bigIntegers[1].toByteArray());byte[] signBytes =ByteUtils.concatenate(rBytes, sBytes);
String sign=Hex.toHexString(signBytes);returnsign;
}/*** 将R或者S修正为固定字节数
*@paramrs
*@return
*/
private static byte[] modifyRSFixedBytes(byte[] rs) {int length =rs.length;int fixedLength = 32;byte[] result = new byte[fixedLength];if (length < 32) {
System.arraycopy(rs,0, result, fixedLength -length, length);
}else{
System.arraycopy(rs, length- fixedLength, result, 0, fixedLength);
}returnresult;
}/*** 验证签名
*@parampublicKey 公钥
*@paramcontent 待签名内容
*@paramsign 签名值
*@return
*/
public static booleanverify(String publicKey, String content, String sign) {//待签名内容
byte[] message =Hex.decode(content);byte[] signData =Hex.decode(sign);//获取一条SM2曲线参数
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");//构造domain参数
ECDomainParameters domainParameters = newECDomainParameters(sm2ECParameters.getCurve(),
sm2ECParameters.getG(),
sm2ECParameters.getN());//提取公钥点
ECPoint pukPoint =sm2ECParameters.getCurve().decodePoint(CommonUtils.hexString2byte(publicKey));//公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
ECPublicKeyParameters publicKeyParameters = newECPublicKeyParameters(pukPoint, domainParameters);//获取签名
BigInteger R = null;
BigInteger S= null;byte[] rBy = new byte[33];
System.arraycopy(signData,0, rBy, 1, 32);
rBy[0] = 0x00;byte[] sBy = new byte[33];
System.arraycopy(signData,32, sBy, 1, 32);
sBy[0] = 0x00;
R= newBigInteger(rBy);
S= newBigInteger(sBy);//创建签名实例
SM2Signer sm2Signer = newSM2Signer();
ParametersWithID parametersWithID= new ParametersWithID(publicKeyParameters, Strings.toByteArray("1234567812345678"));
sm2Signer.init(false, parametersWithID);//验证签名结果
boolean verify =sm2Signer.verifySignature(message, R, S);returnverify;
}/*** SM2加密算法
*@parampublicKey 公钥
*@paramdata 数据
*@return
*/
public staticString encrypt(String publicKey, String data){//获取一条SM2曲线参数
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");//构造domain参数
ECDomainParameters domainParameters = newECDomainParameters(sm2ECParameters.getCurve(),
sm2ECParameters.getG(),
sm2ECParameters.getN());//提取公钥点
ECPoint pukPoint =sm2ECParameters.getCurve().decodePoint(CommonUtils.hexString2byte(publicKey));//公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
ECPublicKeyParameters publicKeyParameters = newECPublicKeyParameters(pukPoint, domainParameters);
SM2Engine sm2Engine= newSM2Engine();
sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, newSecureRandom()));byte[] arrayOfBytes = null;try{byte[] in = data.getBytes("utf-8");
arrayOfBytes= sm2Engine.processBlock(in, 0, in.length);
}catch(Exception e) {
log.error("SM2加密时出现异常:", e);
}returnHex.toHexString(arrayOfBytes);
}/*** SM2加密算法
*@parampublicKey 公钥
*@paramdata 明文数据
*@return
*/
public staticString encrypt(PublicKey publicKey, String data) {
ECPublicKeyParameters ecPublicKeyParameters= null;if (publicKey instanceofBCECPublicKey) {
BCECPublicKey bcecPublicKey=(BCECPublicKey) publicKey;
ECParameterSpec ecParameterSpec=bcecPublicKey.getParameters();
ECDomainParameters ecDomainParameters= newECDomainParameters(ecParameterSpec.getCurve(),
ecParameterSpec.getG(), ecParameterSpec.getN());
ecPublicKeyParameters= newECPublicKeyParameters(bcecPublicKey.getQ(), ecDomainParameters);
}
SM2Engine sm2Engine= newSM2Engine();
sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, newSecureRandom()));byte[] arrayOfBytes = null;try{byte[] in = data.getBytes("utf-8");
arrayOfBytes= sm2Engine.processBlock(in,0, in.length);
}catch(Exception e) {
log.error("SM2加密时出现异常:", e);
}returnHex.toHexString(arrayOfBytes);
}/*** SM2解密算法
*@paramprivateKey 私钥
*@paramcipherData 密文数据
*@return
*/
public staticString decrypt(String privateKey, String cipherData) {byte[] cipherDataByte =Hex.decode(cipherData);//获取一条SM2曲线参数
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");//构造domain参数
ECDomainParameters domainParameters = newECDomainParameters(sm2ECParameters.getCurve(),
sm2ECParameters.getG(), sm2ECParameters.getN());
BigInteger privateKeyD= new BigInteger(privateKey, 16);
ECPrivateKeyParameters privateKeyParameters= newECPrivateKeyParameters(privateKeyD, domainParameters);
SM2Engine sm2Engine= newSM2Engine();
sm2Engine.init(false, privateKeyParameters);
String result= null;try{byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);return new String(arrayOfBytes, "utf-8");
}catch(Exception e) {
log.error("SM2解密时出现异常:", e);
}returnresult;
}/*** SM2解密算法
*@paramprivateKey 私钥
*@paramcipherData 密文数据
*@return
*/
public staticString decrypt(PrivateKey privateKey, String cipherData) {byte[] cipherDataByte =Hex.decode(cipherData);
BCECPrivateKey bcecPrivateKey=(BCECPrivateKey) privateKey;
ECParameterSpec ecParameterSpec=bcecPrivateKey.getParameters();
ECDomainParameters ecDomainParameters= newECDomainParameters(ecParameterSpec.getCurve(),
ecParameterSpec.getG(), ecParameterSpec.getN());
ECPrivateKeyParameters ecPrivateKeyParameters= newECPrivateKeyParameters(bcecPrivateKey.getD(),
ecDomainParameters);
SM2Engine sm2Engine= newSM2Engine();
sm2Engine.init(false, ecPrivateKeyParameters);
String result= null;try{byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);return new String(arrayOfBytes, "utf-8");
}catch(Exception e) {
log.error("SM2解密时出现异常:", e);
}returnresult;
}/*** 将未压缩公钥压缩成压缩公钥
*@parampubKey 未压缩公钥(16进制,不要带头部04)
*@return
*/
public staticString compressPubKey(String pubKey) {
pubKey= CustomStringUtils.append("04", pubKey); //将未压缩公钥加上未压缩标识.//获取一条SM2曲线参数
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");//构造domain参数
ECDomainParameters domainParameters = newECDomainParameters(sm2ECParameters.getCurve(),
sm2ECParameters.getG(),
sm2ECParameters.getN());//提取公钥点
ECPoint pukPoint =sm2ECParameters.getCurve().decodePoint(CommonUtils.hexString2byte(pubKey));//公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04//ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
String compressPubKey=Hex.toHexString(pukPoint.getEncoded(Boolean.TRUE));returncompressPubKey;
}/*** 将压缩的公钥解压为非压缩公钥
*@paramcompressKey 压缩公钥
*@return
*/
public staticString unCompressPubKey(String compressKey) {//获取一条SM2曲线参数
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");//构造domain参数
ECDomainParameters domainParameters = newECDomainParameters(sm2ECParameters.getCurve(),
sm2ECParameters.getG(),
sm2ECParameters.getN());//提取公钥点
ECPoint pukPoint =sm2ECParameters.getCurve().decodePoint(CommonUtils.hexString2byte(compressKey));//公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04//ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
String pubKey=Hex.toHexString(pukPoint.getEncoded(Boolean.FALSE));
pubKey= pubKey.substring(2); //去掉前面的04 (04的时候,可以去掉前面的04)
returnpubKey;
}
}