SM2加密解密工具

import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * SM2加密工具
 *
 * @author zj
 * @since 2023-11-21
 */
public class SM2Util {
    /**
     * SM2默认曲线
     */
    public static final String SM2_DEFAULT_CURVE = "sm2p256v1";

    /**
     * 锁
     */
    protected static final Lock lock = new ReentrantLock();

    /**
     * 全局单例的 org.bouncycastle.jce.provider.BouncyCastleProvider 对象
     */
    public static final Provider PROVIDER;

    private static final SM2Engine ENGINE;

    private static final Digest DIGEST = new SM3Digest();

    static {
        PROVIDER = new BouncyCastleProvider();
        Security.addProvider(PROVIDER);
        ENGINE = new SM2Engine(DIGEST, SM2Engine.Mode.C1C3C2);
    }

    public static void main(String[] args) {
        KeyPair keyPair = SM2Util.generateKeyPairForSM2();

        String privateKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
        String publicKey  = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());

        System.out.println("privateKey====="+privateKey);
        System.out.println("publicKey====="+publicKey);

        // 业务报文加密
        String text = "{\"m_head\":{\"srvid\":\"TPChannelLogin\",\"req_time\":\"20231215160300\",\"req_seq\":\"69048390\",\"channel_id\":\"otherchannel\",\"channel_pwd\":\"019651970372c85bcff74ce0d3ac0352\",\"sessionid\":\"E2BCR9HLK9VDN12G7SU8SK0PR9BNCCZM\",\"usessionid\":\"DIHSCVEQYNNRKBW1Z1KDORW0HRE618IN\",\"version\":\"v1.0\",\"sign\":\"825c31fa21dc725528bafdbd8eaeab15\"},\"m_body\":{\"checknum\":\"230882\",\"password\":\"240822\",\"channel_id\":\"otherchannel\"}}";
//        String privateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgFH7DGMzfPhZIAHY7QKVeCkXGUYeHLkfosmnPKcOCStqgCgYIKoEcz1UBgi2hRANCAARkNRN4b237bLm9QhNytpytZCjx5buP9VfzvwUXleO6qUcKhL9UWhVDrG1odeyNLgqXmQIqbUxl8n6iHSnVTAJ7";
//        String publicKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEZDUTeG9t+2y5vUITcracrWQo8eW7j/VX878FF5XjuqlHCoS/VFoVQ6xtaHXsjS4Kl5kCKm1MZfJ+oh0p1UwCew==";
        String encrypt = encrypt(text, publicKey);

        System.out.println();
        System.out.println("加密数据======="+encrypt);
        System.out.println();
//        System.out.println("解密数据======="+decrypt(
//            "BLEi5Zn3lYbRSAAh+1ChM+lQOaNKPjrBLA30UbEiWJiqtWbCi5uQZ3J7S6XhlbmfEBqpaOX3UA/bDHEsobWJih7eiJu2jn8RecwB3l392gWjkKdbF97VqArHwQLUb1I0cPCspBOT4mHkNM/Vq/l1knn7/A/jI43XW1G1arkOC54AiJoC74EdeKe2LWUL60ohkHKv7IbIthzYp+p5WCTL/Vnpd/GBXRLdBU1Gve5zWcIYfhOeath2A+jKib8NqSpIqPaCJz2iTSbrMqybCFMJ/31np9PH0YZF53Xlbi9X1zqvf2gM3fDrL9abWiesIfGPaJ4AJowJZYDVpMc5XyX3BnIxGZV32e38cgMhFrMumJk6WnXEKEP6Y/HbcgKUtA8RTt6DN+3RRpr857YSporWBdhUHCKsR1BZW5bEMmPtFBW+CRch41s9HqtRCRQT9QsJUANqg/lQuiRDhoIVI0Gv58n3MScOSYdioSaU87iFaRpbFIRix7gzsPsHkg+ZAx4ZsiBTC7Dsy2mLR5G+2JsAvnesgdUNvuqMr6RVXXmcs8rW7ZUGsrbCcKP+1PyK3FKQBGjNGnRt16v0lDNb6URPKv1ZU6+i6KA+UGht06MepivQkXVWcbFKzLy7MKmemyiaUnTwLVl3nVza+RyZWSxaC8ONtgiUwdmScfvN6BfPSXsBaVzxVJFbKRX4q9o/S2kJqhz9qtcDypivaFR3mmnHzS2Uhsjm6LC92CgtE3On2bUwtpLHwQL32JIwjufbHVjMZ3z8ir7+mbFSR11Hcte5Cp6OCu+UW2HbLFmQjHs5QQ2lYDcKzXZ2gaJsP/Vf5cKi9wjj0pvOZljukV0zxk5NcvbfGh5h7ji8HGsueio3qDCV8EecsNzBH+sm8rq6oth+7uJS9Qxa22dtshc=",
//            privateKey));

        System.out.println("解密数据======="+decrypt(encrypt, privateKey));

    }

    /**
     * 解密
     *
     * @param encryptData SM2密文
     * @param privateKey 私钥参数
     * @return 加密后的bytes
     */
    public static String decrypt(String encryptData, String privateKey) {
        ECPrivateKeyParameters privateKeyParameters = generatePrivateKey(Base64.getDecoder().decode(privateKey));
        byte[] data = Base64.getDecoder().decode(encryptData);
        lock.lock();
        try {
            DIGEST.reset();
            ENGINE.init(false, privateKeyParameters);
            return new String(ENGINE.processBlock(data, 0, data.length), StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 加密,SM2非对称加密的结果由C1,C2,C3三部分组成,其中:
     *
     * <pre>
     * C1 生成随机数的计算出的椭圆曲线点
     * C2 密文数据
     * C3 SM3的摘要值
     * </pre>
     *
     * @param data 被加密的str
     * @param publicKey 公钥参数
     * @return 加密后的字符串
     */
    public static String encrypt(String data, String publicKey) {
        ECPublicKeyParameters pubKeyParameters = generatePublicKey(Base64.getDecoder().decode(publicKey));
        lock.lock();
        byte[] bytes;
        try {
            DIGEST.reset();
            ENGINE.init(true, new ParametersWithRandom(pubKeyParameters));
            byte[] preBytes = data.getBytes(StandardCharsets.UTF_8);
            bytes = ENGINE.processBlock(preBytes, 0, preBytes.length);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
        return Base64.getEncoder().encodeToString(bytes);
    }

    /**
     * 生成用于非对称加密的公钥和私钥
     *
     * @return {@link KeyPair}
     */
    public static KeyPair generateKeyPairForSM2() {
        // SM2算法需要单独定义其曲线生成
        final ECGenParameterSpec sm2p256v1 = new ECGenParameterSpec(SM2_DEFAULT_CURVE);
        return generateKeyPair("EC", 256, new SecureRandom(), sm2p256v1);
    }

    /**
     * 生成用于非对称加密的公钥和私钥
     *
     * @param algorithm 算法
     * @param keySize 密钥模(modulus )长度(单位bit)
     * @param random {@link SecureRandom} 对象,创建时可选传入seed
     * @param params {@link AlgorithmParameterSpec}
     * @return {@link KeyPair}
     */
    public static KeyPair generateKeyPair(String algorithm, int keySize, SecureRandom random,
        AlgorithmParameterSpec... params) {
        final KeyPairGenerator keyPairGen;
        try {
            keyPairGen = KeyPairGenerator.getInstance(algorithm, PROVIDER);//
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        // 密钥模(modulus )长度初始化定义
        if (keySize > 0) {
            if (null != random) {
                keyPairGen.initialize(keySize, random);
            } else {
                keyPairGen.initialize(keySize);
            }
        }
        // 自定义初始化参数
        if (params != null) {
            for (AlgorithmParameterSpec param : params) {
                if (null == param) {
                    continue;
                }
                try {
                    if (null != random) {
                        keyPairGen.initialize(param, random);
                    } else {
                        keyPairGen.initialize(param);
                    }
                } catch (InvalidAlgorithmParameterException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return keyPairGen.generateKeyPair();
    }

    /**
     * 生成公钥,仅用于非对称加密<br>
     * 采用X509证书规范<br>
     *
     * @param key 密钥,必须为DER编码存储
     * @return 公钥 {@link ECPublicKeyParameters}
     */
    private static ECPublicKeyParameters generatePublicKey(byte[] key) {
        if (null == key) {
            return null;
        }
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(key);
        try {
            PublicKey publicKey = KeyFactory.getInstance("EC", PROVIDER).generatePublic(x509EncodedKeySpec);
            return (ECPublicKeyParameters) ECUtil.generatePublicKeyParameter(publicKey);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 生成私钥,仅用于非对称加密<br>
     * 采用PKCS#8规范,此规范定义了私钥信息语法和加密私钥语法<br>
     *
     * @param key 密钥,PKCS#8格式
     * @return 私钥 {@link PrivateKey}
     */
    private static ECPrivateKeyParameters generatePrivateKey(byte[] key) {
        if (null == key) {
            return null;
        }
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(key);
        try {
            PrivateKey privateKey = KeyFactory.getInstance("EC", PROVIDER).generatePrivate(pkcs8EncodedKeySpec);
            return (ECPrivateKeyParameters) ECUtil.generatePrivateKeyParameter(privateKey);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}