SM2国密非对称加密算法工具类


前言

在本文中,我们将探讨用于 SM2国密算法的实用工具:Sm2Util,这个工具类可以生成 SM2 加密密钥对(公钥、私钥)、使用 SM2 算法进行加密和解密。


一、SM2 国密算法简介

SM2 国密SM2算法是中国国家密码管理局(CNCA)发布的一种非对称加密算法。它采用椭圆曲线密码体系(Elliptic Curve Cryptography,ECC)进行密钥交换、数字签名和公钥加密等操作。SM2算法和RSA算法都是公钥密码算法,SM2算法是一种更先进安全的算法,在我们国家商用密码体系中被用来替换RSA算法。

二、工具类的使用

1.引入Maven依赖

代码如下(示例):

<dependency>
   <groupId>org.bouncycastle</groupId>
   <artifactId>bcprov-jdk18on</artifactId>
   <version>1.77</version>
</dependency>
<dependency>
   <groupId>org.bouncycastle</groupId>
   <artifactId>bcpkix-jdk18on</artifactId>
   <version>1.77</version>
</dependency>

2.Sm2Util工具类

代码如下(示例):

package com.tg.dp.user.auth.server.util;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
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.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.util.Assert;

import java.math.BigInteger;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.LinkedHashMap;
import java.util.Map;


/**
 * SM2国密加密解密
 * @author Administrator
 */
@Slf4j
public class Sm2Util {

    public static final String PUBLIC_KEY = "0413b03efd15f560fc6d73965febe39ccc47596088c3653b8efe91e008194c0c50ca8ffeddcddbf48bd05506510af59075edeb651de626765527b217e8a3b6ce10";
    public static final String PRIVATE_KEY = "026b435ef1124d82e7ed5521dcf9572ba81c56afa57d8e978a95f9239dfbd31c";
    public static final String PREFIX = "04";
    /**
     * SM2加密算法
     *
     * @param publicKey 公钥
     * @param data      明文数据
     * @return
     */
    public static String encrypt(String publicKey, String data) {
        String result = "";
        if (StrUtil.isEmpty(data)) {
            return data;
        }
        Assert.hasText(publicKey, "publicKey不能为空!");
        try {
            // 获取一条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);

            SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
            // 设置sm2为加密模式
            sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
            byte[] in = data.getBytes();
            byte[] arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
            result = Hex.toHexString(arrayOfBytes);
        } catch (Exception e) {
            log.error("SM2加密时出现异常:{}", e.getMessage(), e);
        }
        return result;
    }

    /**
     * SM2解密算法
     *
     * @param privateKey 私钥
     * @param cipherData 密文数据
     * @return
     */
    public static String decrypt(String privateKey, String cipherData) {
        String result = "";
        if (StrUtil.isEmpty(cipherData)) {
            return cipherData;
        }
        Assert.hasText(privateKey, "privateKey不能为空!");
        // 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上
        try {
            if (!cipherData.startsWith(PREFIX)) {
                cipherData = PREFIX + cipherData;
            }
            byte[] cipherDataByte = Hex.decode(cipherData);
            BigInteger privateKeyD = new BigInteger(privateKey, 16);
            //获取一条SM2曲线参数
            X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
            //构造domain参数
            ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
            ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);

            SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
            // 设置sm2为解密模式
            sm2Engine.init(false, privateKeyParameters);
            byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
            return new String(arrayOfBytes);
        } catch (Exception e) {
            log.error("SM2解密时出现异常:{}", e.getMessage(), e);
        }
        return result;
    }

    public static void main(String[] args) {
//        //获取公钥、私钥
//        Map<String, String> map = generateKey();
//        System.out.println("公钥:" + map.get("publicKey"));
//        System.out.println("私钥:" + map.get("privateKey"));

        String encrypt = Sm2Util.encrypt(PUBLIC_KEY, "123456");
        System.out.println("加密结果:");
        System.out.println(encrypt);
        String decrypt = Sm2Util.decrypt(PRIVATE_KEY, encrypt);
        System.out.println("解密结果:");
        System.out.println(decrypt);
    }

    /**
     * 获取密钥对
     */
    public static Map<String, String> generateKey() {
        try {
            Map<String, String> map = new LinkedHashMap<>();
            ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
            // 获取一个椭圆曲线类型的密钥对生成器
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
            // 使用SM2参数初始化生成器
            kpg.initialize(sm2Spec);
            // 获取密钥对
            KeyPair keyPair = kpg.generateKeyPair();

            //公钥
            PublicKey publicKey = keyPair.getPublic();
            BCECPublicKey p = (BCECPublicKey) publicKey;
            String publicKeyStr = Hex.toHexString(p.getQ().getEncoded(false));
            log.info("generate-publicKey:{}", publicKeyStr);

            //私钥
            PrivateKey privateKey = keyPair.getPrivate();
            BCECPrivateKey s = (BCECPrivateKey) privateKey;
            String privateKeyStr = Hex.toHexString(s.getD().toByteArray());
            log.info("generate-privateKey:{}", privateKeyStr);

            map.put("publicKey", publicKeyStr);
            map.put("privateKey", privateKeyStr);
            return map;
        } catch (Exception e) {
            e.getLocalizedMessage();
            log.error("SM2Util生成密钥异常!{}", e.getLocalizedMessage(), e);
        }
        return null;
    }

}

其中,encrypt()方法加密,decrypt()方法解密,generateKey()方法获取加解密密码对。
运行generateKey()方法获取SM2公私钥密码对:
在这里插入图片描述

运行main()方法调用加密、解密算法进行加解密:
在这里插入图片描述


总结

通过使用 Sm2Util工具类,可以轻松生成 SM2 密钥对,并且通过 Sm2Util,可以使用 SM2 密钥进行数据加密和解密,这些实用工具简化了使用 SM2 算法的过程,简单明了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值