基于 bouncycastle 实现 国密 SM2
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECNamedDomainParameters;
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.jcajce.provider.asymmetric.ec.KeyPairGeneratorSpi;
import org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory;
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.encoders.Hex;
import java.io.ByteArrayInputStream;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.Base64;
import static java.util.Objects.isNull;
@Slf4j
public class SM2Utils {
private static final String EC = "EC";
private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder();
private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder();
private static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider();
static {
if (isNull(Security.getProvider(BouncyCastleProvider.PROVIDER_NAME))) {
Security.addProvider(PROVIDER);
}
}
public static SM2KeyPair<byte[], BigInteger> genKeyPair() {
return genKeyPair(false);
}
@SneakyThrows
public static SM2KeyPair<byte[], BigInteger> genKeyPair(boolean compressed) {
KeyPairGeneratorSpi.EC spi = new KeyPairGeneratorSpi.EC();
X9ECParameters parameters = GMNamedCurves.getByOID(GMObjectIdentifiers.sm2p256v1);
ECParameterSpec parameterSpec = new ECParameterSpec(parameters.getCurve(), parameters.getG(), parameters.getN());
SecureRandom secureRandom = new SecureRandom();
spi.initialize(parameterSpec, secureRandom);
KeyPair asymmetricCipherKeyPair = spi.generateKeyPair();
BCECPublicKey publicKeyParameters = (BCECPublicKey) asymmetricCipherKeyPair.getPublic();
ECPoint ecPoint = publicKeyParameters.getQ();
byte[] publicKey = ecPoint.getEncoded(compressed);
BCECPrivateKey privateKeyParameters = (BCECPrivateKey) asymmetricCipherKeyPair.getPrivate();
BigInteger intPrivateKey = privateKeyParameters.getD();
return new SM2KeyPair<>(publicKey, intPrivateKey);
}
public static SM2KeyPair<String, String> genKeyPairAsHex() {
return genKeyPairAsHex(false);
}
public static SM2KeyPair<String, String> genKeyPairAsHex(boolean compressed) {
final SM2KeyPair<byte[], BigInteger> pair = genKeyPair(compressed);
return new SM2KeyPair<>(
Hex.toHexString(pair.getPublic()),
pair.getPrivate().toString(16)
);
}
public static SM2KeyPair<String, String> genKeyPairAsBase64() {
return genKeyPairAsBase64(false);
}
public static SM2KeyPair<String, String> genKeyPairAsBase64(boolean compressed) {
final SM2KeyPair<byte[], BigInteger> pair = genKeyPair(compressed);
return new SM2KeyPair<>(
BASE64_ENCODER.encodeToString(pair.getPublic()),
BASE64_ENCODER.encodeToString(pair.getPrivate().toByteArray())
);
}
public static byte[] encrypt(byte[] publicKey, byte[] data) {
return encrypt(publicKey, data, SM2Engine.Mode.C1C3C2);
}
@SneakyThrows
public static byte[] encrypt(byte[] publicKey, byte[] data, SM2Engine.Mode mode) {
final ASN1ObjectIdentifier sm2p256v1 = GMObjectIdentifiers.sm2p256v1;
X9ECParameters parameters = GMNamedCurves.getByOID(sm2p256v1);
ECNamedDomainParameters namedDomainParameters = new ECNamedDomainParameters(
sm2p256v1, parameters.getCurve(), parameters.getG(), parameters.getN());
ECPoint pukPoint = parameters.getCurve().decodePoint(publicKey);
ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, namedDomainParameters);
SM2Engine sm2Engine = new SM2Engine(mode);
SecureRandom secureRandom = new SecureRandom();
sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, secureRandom));
final byte[] encrypt = sm2Engine.processBlock(data, 0, data.length);
return encrypt;
}
public static String encryptHex(String publicKey, String data) {
return encryptHex(publicKey, data, SM2Engine.Mode.C1C3C2);
}
public static String encryptHex(String publicKey, String data, SM2Engine.Mode mode) {
final byte[] key = Hex.decode(publicKey);
byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
final byte[] encrypt = encrypt(key, bytes, mode);
return Hex.toHexString(encrypt);
}
public static String encryptBase64(String publicKey, String data) {
return encryptBase64(publicKey, data, SM2Engine.Mode.C1C3C2);
}
public static String encryptBase64(String publicKey, String data, SM2Engine.Mode mode) {
final byte[] key = BASE64_DECODER.decode(publicKey);
byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
final byte[] encrypt = encrypt(key, bytes, mode);
return BASE64_ENCODER.encodeToString(encrypt);
}
public static byte[] decrypt(BigInteger privateKey, byte[] cipherData) {
return decrypt(privateKey, cipherData, SM2Engine.Mode.C1C3C2);
}
@SneakyThrows
public static byte[] decrypt(BigInteger privateKey, byte[] cipherData, SM2Engine.Mode mode) {
final ASN1ObjectIdentifier sm2p256v1 = GMObjectIdentifiers.sm2p256v1;
X9ECParameters parameters = GMNamedCurves.getByOID(sm2p256v1);
ECNamedDomainParameters namedDomainParameters = new ECNamedDomainParameters(
sm2p256v1, parameters.getCurve(), parameters.getG(), parameters.getN());
ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKey, namedDomainParameters);
SM2Engine sm2Engine = new SM2Engine(mode);
sm2Engine.init(false, privateKeyParameters);
if (cipherData[0] == 0x04) {
return sm2Engine.processBlock(cipherData, 0, cipherData.length);
} else {
byte[] bytes = new byte[cipherData.length + 1];
bytes[0] = 0x04;
System.arraycopy(cipherData, 0, bytes, 1, cipherData.length);
return sm2Engine.processBlock(bytes, 0, bytes.length);
}
}
public static String decryptHex(String privateKey, String cipherData) {
return decryptHex(privateKey, cipherData, SM2Engine.Mode.C1C3C2);
}
public static String decryptHex(String privateKey, String cipherData, SM2Engine.Mode mode) {
final BigInteger key = new BigInteger(privateKey, 16);
final byte[] decrypt = decrypt(key, Hex.decode(cipherData), mode);
return new String(decrypt, StandardCharsets.UTF_8);
}
public static String decryptBase64(String privateKey, String cipherData) {
return decryptBase64(privateKey, cipherData, SM2Engine.Mode.C1C3C2);
}
public static String decryptBase64(String privateKey, String cipherData, SM2Engine.Mode mode) {
final BigInteger key = new BigInteger(BASE64_DECODER.decode(privateKey));
final byte[] decrypt = decrypt(key, BASE64_DECODER.decode(cipherData), mode);
return new String(decrypt, StandardCharsets.UTF_8);
}
public static String sign(String plainText, BigInteger privateKey) throws GeneralSecurityException {
X9ECParameters parameters = GMNamedCurves.getByOID(GMObjectIdentifiers.sm2p256v1);
ECParameterSpec parameterSpec = new ECParameterSpec(parameters.getCurve(), parameters.getG(), parameters.getN());
ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(privateKey, parameterSpec);
PrivateKey bcecPrivateKey = new BCECPrivateKey(EC, privateKeySpec, BouncyCastleProvider.CONFIGURATION);
Signature signature = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), PROVIDER);
signature.initSign(bcecPrivateKey);
signature.update(plainText.getBytes(StandardCharsets.UTF_8));
return BASE64_ENCODER.encodeToString(signature.sign());
}
public static boolean verify(String plainText, String signText, byte[] publicKey) throws GeneralSecurityException {
X9ECParameters parameters = GMNamedCurves.getByOID(GMObjectIdentifiers.sm2p256v1);
ECParameterSpec parameterSpec = new ECParameterSpec(parameters.getCurve(), parameters.getG(), parameters.getN());
ECPoint ecPoint = parameters.getCurve().decodePoint(publicKey);
ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(ecPoint, parameterSpec);
PublicKey bcecPublicKey = new BCECPublicKey(EC, publicKeySpec, BouncyCastleProvider.CONFIGURATION);
Signature signature = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), PROVIDER);
signature.initVerify(bcecPublicKey);
signature.update(plainText.getBytes(StandardCharsets.UTF_8));
return signature.verify(BASE64_DECODER.decode(signText));
}
public static boolean certVerify(String certText, String plainText, String signText) throws GeneralSecurityException {
CertificateFactory factory = new CertificateFactory();
X509Certificate certificate = (X509Certificate) factory.engineGenerateCertificate(
new ByteArrayInputStream(BASE64_DECODER.decode(certText)));
Signature signature = Signature.getInstance(certificate.getSigAlgName(), PROVIDER);
signature.initVerify(certificate);
signature.update(plainText.getBytes(StandardCharsets.UTF_8));
return signature.verify(BASE64_DECODER.decode(signText));
}
public static void main(String[] args) {
}
public class SM2KeyPair<U, V> {
protected V privateKey;
protected U publicKey;
public SM2KeyPair(U publicKey, V privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
public U getPublic() {
return publicKey;
}
public V getPrivate() {
return privateKey;
}
}