Sm2Util.java
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sandy.ecp.framework.util;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.ECGenParameterSpec;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.DatatypeConverter;
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Base64Utils;
/**
* @author Sandy
* @date 2023-05-23 12:12:12
*/
public class Sm2Util {
protected static final Logger logger = LoggerFactory.getLogger(Sm2KeyGenerator.class);
private static Map<String, String> keys = new HashMap<String, String>();
static {
Security.addProvider(new BouncyCastleProvider());
try {
keyGenerator();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Map<String, String> getKeys() {
return keys;
}
public static Map<String, String> keyGenerator1() throws Exception {
if (null != keys.get("privatekey")) {
return keys;
}
final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
// 获取一个椭圆曲线类型的密钥对生成器 new BouncyCastleProvider();
final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
SecureRandom random = new SecureRandom();
// 使用SM2的算法区域初始化密钥生成器
kpg.initialize(sm2Spec, random);
// 获取密钥对
KeyPair keyPair = kpg.generateKeyPair();
if (logger.isDebugEnabled()) {
logger.debug("PrivateKey = {}", new String(Base64Utils.encode(keyPair.getPrivate().getEncoded())));
logger.debug("PublicKey = {}", new String(Base64Utils.encode(keyPair.getPublic().getEncoded())));
}
keys.put("privatekey", new String(Base64Utils.encode(keyPair.getPrivate().getEncoded())));
keys.put("privatekeyHex", new String(org.apache.commons.codec.binary.Hex.encodeHex(keyPair.getPrivate().getEncoded())));
keys.put("publickey", new String(Base64Utils.encode(keyPair.getPublic().getEncoded())));
keys.put("publickeyHex", new String(org.apache.commons.codec.binary.Hex.encodeHex((keyPair.getPublic().getEncoded()))));
return keys;
}
public static Map<String, String> keyGenerator2() throws Exception {
if (null != keys.get("privatekey")) {
return keys;
}
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
//生成密钥对
ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
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);
HexBinaryAdapter fdsf = new HexBinaryAdapter();
String pkey = DatatypeConverter.printBase64Binary(fdsf.unmarshal(privateKeyHex));
if (logger.isDebugEnabled()) {
logger.debug("privateKeyHex={}", privateKeyHex);
logger.debug("privateKeyBase64={}", pkey);
}
keys.put("privatekey", privateKeyHex);
//公钥,16进制格式,发给前端,格式如04813d4d97ad31bd9d18d785f337f683233099d5abed09cb397152d50ac28cc0ba43711960e811d90453db5f5a9518d660858a8d0c57e359a8bf83427760ebcbba
ECPoint publicEcPoint = ((ECPublicKeyParameters) asymmetricCipherKeyPair.getPublic()).getQ();
String publicKeyHex = Hex.toHexString(publicEcPoint.getEncoded(false));
if (logger.isDebugEnabled()) {
logger.debug("publicKeyHex={}", publicKeyHex);
String base64 = org.apache.commons.codec.binary.Base64.encodeBase64String(publicEcPoint.getEncoded(false));
logger.debug("publicKeyBase64={}", base64);
}
keys.put("publickey", publicKeyHex);
return keys;
}
public static Map<String, String> keyGenerator() throws Exception {
if (null != keys.get("privatekey")) {
return keys;
}
//生成密钥对
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
try {
keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));
} catch (NoSuchAlgorithmException e) {
logger.error("NoSuchAlgorithmException", e);
}
AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();
//私钥,16进制格式
BigInteger privateKey = ((ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate()).getD();
String privateKeyHex = privateKey.toString(16);
keys.put("privatekey", privateKeyHex);
//公钥,16进制格式
ECPoint ecPoint = ((ECPublicKeyParameters) asymmetricCipherKeyPair.getPublic()).getQ();
String publicKeyHex = Hex.toHexString(ecPoint.getEncoded(false));
// logger.info("privateKeyHex {}", privateKeyHex);
// logger.info("publicKeyHex {}", publicKeyHex);
keys.put("publickey", publicKeyHex);
return keys;
}
/**
* SM2加密算法
* @param publicKey 公钥
* @param data 待加密的数据
* @param cipherMode 密文排列方式0-C1C2C3;1-C1C3C2;
* @return 密文,BC库产生的密文带由04标识符,与非BC库对接时需要去掉开头的04
*/
public static String encrypt(String publicKey, String data, int cipherMode){
// 获取一条SM2曲线参数
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
// 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数N
ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
//提取公钥点
ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKey));
// 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
SM2EngineExtend sm2Engine = new SM2EngineExtend();
// 设置sm2为加密模式
sm2Engine.init(true, cipherMode, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
byte[] arrayOfBytes = null;
try {
byte[] in = data.getBytes();
arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
} catch (Exception e) {
logger.error("SM2加密时出现异常:{}", e.getMessage(), e);
}
return Hex.toHexString(arrayOfBytes);
}
public static String decrypt(String cipherData, String privateKey) throws Exception {
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
// 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上
if (!cipherData.startsWith("04")) {
cipherData = "04" + cipherData;
}
byte[] cipherDataByte = Hex.decode(cipherData);
//刚才的私钥Hex,先还原私钥
if (null == privateKey || privateKey.isEmpty()) {
privateKey = keys.get("privatekey");
}
BigInteger privateKeyD = new BigInteger(privateKey, 16);
ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);
//用私钥解密
SM2EngineExtend sm2Engine = new SM2EngineExtend();
sm2Engine.init(false, 0, privateKeyParameters);
byte [] declode = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
//processBlock得到Base64格式,记得解码
//byte[] arrayOfBytes = java.util.Base64.getDecoder().decode(declode);
String data = new String(declode, StandardCharsets.UTF_8);
return data;
}
}