首页 > 编程语言 >【Java】SM2Utils(国密 SM2 工具类)

【Java】SM2Utils(国密 SM2 工具类)

时间:2023-08-25 10:47:57浏览次数:47  
标签:return String import 国密 static SM2Utils new Java public

基于 bouncycastle 实现 国密 SM2

<!-- 引入 bouncycastle -->
<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);
        }
    }

    // region generateKeyPair

    /**
     * 获取sm2密钥对
     * BC库使用的公钥=64个字节+1个字节(04标志位),BC库使用的私钥=32个字节
     * SM2秘钥的组成部分有 私钥D 、公钥X 、 公钥Y , 他们都可以用长度为64的16进制的HEX串表示,
     * <br/>SM2公钥并不是直接由X+Y表示 , 而是额外添加了一个头
     *
     * @return
     */
    public static SM2KeyPair<byte[], BigInteger> genKeyPair() {
        return genKeyPair(false);
    }

    /**
     * 获取sm2密钥对
     * BC库使用的公钥=64个字节+1个字节(04标志位),BC库使用的私钥=32个字节
     * SM2秘钥的组成部分有 私钥D 、公钥X 、 公钥Y , 他们都可以用长度为64的16进制的HEX串表示,
     * <br/>SM2公钥并不是直接由X+Y表示 , 而是额外添加了一个头,当启用压缩时:公钥=有头+公钥X ,即省略了公钥Y的部分
     *
     * @param compressed 是否压缩公钥(加密解密都使用BC库才能使用压缩)
     * @return
     */
    @SneakyThrows
    public static SM2KeyPair<byte[], BigInteger> genKeyPair(boolean compressed) {
        //1.创建密钥生成器
        KeyPairGeneratorSpi.EC spi = new KeyPairGeneratorSpi.EC();
        //获取一条SM2曲线参数
        X9ECParameters parameters = GMNamedCurves.getByOID(GMObjectIdentifiers.sm2p256v1);
        //构造spec参数
        ECParameterSpec parameterSpec = new ECParameterSpec(parameters.getCurve(), parameters.getG(), parameters.getN());
        // SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG", "SUN");
        SecureRandom secureRandom = new SecureRandom();
        //2.初始化生成器,带上随机数
        spi.initialize(parameterSpec, secureRandom);
        //3.生成密钥对
        KeyPair asymmetricCipherKeyPair = spi.generateKeyPair();
        // 把公钥放入map中,默认压缩公钥
        // 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥,04的时候,可以去掉前面的04
        BCECPublicKey publicKeyParameters = (BCECPublicKey) asymmetricCipherKeyPair.getPublic();
        ECPoint ecPoint = publicKeyParameters.getQ();
        byte[] publicKey = ecPoint.getEncoded(compressed);
        // 把私钥放入map中
        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())
        );
    }

    // endregion generateKeyPair

    // region encrypt

    /**
     * SM2加密算法
     *
     * @param publicKey 公钥
     * @param data      待加密的数据
     * @return 密文,BC库产生的密文带由04标识符,与非BC库对接时需要去掉开头的04
     */
    public static byte[] encrypt(byte[] publicKey, byte[] data) {
        // 按国密排序标准加密
        return encrypt(publicKey, data, SM2Engine.Mode.C1C3C2);
    }

    /**
     * SM2加密算法
     *
     * @param publicKey 公钥
     * @param data      待加密的数据
     * @param mode      密文排列方式
     * @return 密文,BC库产生的密文带由04标识符,与非BC库对接时需要去掉开头的04
     */
    @SneakyThrows
    public static byte[] encrypt(byte[] publicKey, byte[] data, SM2Engine.Mode mode) {
        final ASN1ObjectIdentifier sm2p256v1 = GMObjectIdentifiers.sm2p256v1;
        // 获取一条SM2曲线参数
        X9ECParameters parameters = GMNamedCurves.getByOID(sm2p256v1);
        // 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数N
        ECNamedDomainParameters namedDomainParameters = new ECNamedDomainParameters(
                sm2p256v1, parameters.getCurve(), parameters.getG(), parameters.getN());
        //提取公钥点
        ECPoint pukPoint = parameters.getCurve().decodePoint(publicKey);
        // 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
        ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, namedDomainParameters);
        SM2Engine sm2Engine = new SM2Engine(mode);
        SecureRandom secureRandom = new SecureRandom();
        // 设置sm2为加密模式
        sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, secureRandom));
        final byte[] encrypt = sm2Engine.processBlock(data, 0, data.length);
//        if (encrypt[0] == 0x04) {
//            return Arrays.copyOfRange(encrypt, 1, encrypt.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);
    }

    // endregion encrypt

    // region decrypt

    /**
     * SM2解密算法
     *
     * @param privateKey 私钥
     * @param cipherData 密文数据
     * @return
     */
    public static byte[] decrypt(BigInteger privateKey, byte[] cipherData) {
        // 按国密排序标准解密
        return decrypt(privateKey, cipherData, SM2Engine.Mode.C1C3C2);
    }

    /**
     * SM2解密算法
     *
     * @param privateKey 私钥
     * @param cipherData 密文数据
     * @param mode       密文排列方式
     * @return
     */
    @SneakyThrows
    public static byte[] decrypt(BigInteger privateKey, byte[] cipherData, SM2Engine.Mode mode) {
        final ASN1ObjectIdentifier sm2p256v1 = GMObjectIdentifiers.sm2p256v1;
        //获取一条SM2曲线参数
        X9ECParameters parameters = GMNamedCurves.getByOID(sm2p256v1);
        // 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数N
        ECNamedDomainParameters namedDomainParameters = new ECNamedDomainParameters(
                sm2p256v1, parameters.getCurve(), parameters.getG(), parameters.getN());
        ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKey, namedDomainParameters);
        SM2Engine sm2Engine = new SM2Engine(mode);
        // 设置sm2为解密模式
        sm2Engine.init(false, privateKeyParameters);
        // 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上
        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);
    }

    // endregion decrypt

    // region sign & cert

    /**
     * 签名
     *
     * @param plainText  待签名文本
     * @param privateKey 私钥
     * @return
     * @throws GeneralSecurityException
     */
    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());
    }

    /**
     * 验签
     *
     * @param plainText 待签名文本
     * @param signText
     * @param publicKey 公钥
     * @return
     * @throws GeneralSecurityException
     */
    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));
    }

    /**
     * 证书验签
     *
     * @param certText  证书串
     * @param plainText 签名原文
     * @param signText  签名产生签名值 此处的签名值实际上就是 R和S的sequence
     * @return
     * @throws GeneralSecurityException
     */
    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));
    }

    // endregion sign & cert

    public static void main(String[] args) {
//        final String plainText = "123456";

//        final SM2KeyPair<String, String> keys = genKeyPairAsBase64();
//        log.debug("\n公钥 : {}\n私钥 : {}", keys.getPublic(), keys.getPrivate());

//        String pubKey = "04a445fa8aa9318a2e4f2d0fd718fafc6443f408c805e51979679840907c6ae56e4e3378382f627165bbbb2566dd301d6695b0c7d6192177b5ef8b7561547d7cc5";
//        String priKey = "f2ad7ce861f362caf026725b3e9558c5477e7e0f55a476b1a2200d43425a0e1b";

//        String pubKey = "BPaIW/Bdy1brZeCvaXU95SYRbvT8O/A3cC67Nm8v2ukSikG6ToBJ8yX3rDzg48+R0qimVnN3QVgiAhS2aPprHNA=";
//        String priKey = "T768XF7KJXwKdeHRetcmBwiDczSgxIDBj3ioP9ozWG4=";

//        String pubKey = "BDIVcRKDxr0eTLHs1kjRw5UXcGtVZAQJPJ7H+dwiQt/Rfywi+GKkl7YtJgZvjOpQd9WuoIqfclgfnMxJV2R6Wlk=";
//        String priKey = "eb5wex++fwJ252auYDmDyCtGDHf+CaSXVXvX1uid4AY=";

//        String encrypt = encryptBase64(pubKey, plainText);
//        String decrypt = decryptBase64(priKey, encrypt);
//        log.debug("\n加密 : {}\n解密 : {}", encrypt, decrypt);

//        try {
//            String sign = sign(plainText, new BigInteger(BASE64_DECODER.decode(priKey)));
//            boolean verify = verify(plainText, sign, BASE64_DECODER.decode(pubKey));
//            log.debug("\n签名 : {}\n验签 : {}", sign, verify);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
    }
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;
    }
}

标签:return,String,import,国密,static,SM2Utils,new,Java,public
From: https://www.cnblogs.com/zhuzhongxing/p/17656227.html

相关文章

  • 解放双手!ChatGPT助力编写JAVA框架
    亲爱的Javaer们,在平时编码的过程中,你是否曾想过编写一个Java框架去为开发提效?但是要么编写框架时感觉无从下手,不知道从哪开始。要么有思路了后对某个功能实现的技术细节不了解,空有想法而无法实现。如果你遇到了这些问题,看完这篇文章你也能用ChatGPT编写一个简单的JAVA框架。构思......
  • java基础
    一、数据类型1、数据类型 2、数据类型的转换自动转换:取值范围小的类型  自动提升为  取值范围大的类型。    三元运算符 ......
  • java Object转String的4种实现方式
    1.情景展示在实际开发过程中,经常会遇到将Object类型强转成String类型。Object转String一共有几种实现方式?2.具体分析共有三种实现方式。3.解决方案以Map<String,Object>进行举例说明。Map<String,Object>map=newHashMap<>(3);map.put("k1","v1");map.put("k2",......
  • 【校招VIP】java语言考点之双亲委派模型
    考点介绍:双亲委派是校招面试中的高频考点之一。双亲委派机制定义:当一个类加载器收到了类加载的请求的时候,他不会直接去加载指定的类,而是把这个请求委托给自己的父加载器去加载。只有父加载器无法加载这个类的时候,才会由当前这个加载器来负责类的加载......一、考点题目1、为什......
  • IDEA 生成的 JAVA 接口允许跨域访问的配置
    1.新建一个基类,在类上方添加 @CrossOrigin属性importorg.springframework.web.bind.annotation.CrossOrigin;@CrossOriginpublicclassBaseController{}2.在其他controller中继承这个基类,其他接口就可以跨域访问了publicclassElseControllerextendsBaseContr......
  • 5.JAVA数组
     数组声明创建 内存的使用 定义数组时数组并不存在;只有创建数组是才存在           ......
  • 【Java】SM4Utils(国密 SM4 工具类)
    基于bouncycastle实现国密SM4<!--引入bouncycastle--><dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.70</version></dependency>importlom......
  • LeetCode-21. 合并两个有序链表(Java)
    这是我在51CTO博客开启的写作之路,第一次正式写博客记录我在LeetCode的刷题日,希望能帮助更多的小伙伴攻面自己心仪的公司offer。如下对于 LeetCode-21.合并两个有序链表,进行全面解析并小结解题思路,同学们请参考:1.题目描述将两个升序链表合并为一个新的 升序 链表并返回。新链表......
  • C# 实现 国密SM4/ECB/PKCS7Padding对称加密解密
    C# 实现国密SM4/ECB/PKCS7Padding对称加密解密,为了演示方便本问使用的是VisualStudio2022来构建代码的1、新建项目,之后选择项目鼠标右键选择 管理NuGet程序包管理,输入 BouncyCastle回车添加BouncyCastle程序包 2、代码如下:CBC模式byt......
  • Java设计模式
    装饰器模式:装饰器模式是指在不改变现有对象结构的情况下,动态的给改对象增加一些职责(即增加其额外功能)的模式。装饰器模式通常在以下几种情况使用。当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生......