国密SM2生成公钥密钥/SM3进行加签

使用国密SM2和SM3引入Maven

<!-- hutool工具类 -->
<dependency>
	<groupId>cn.hutool</groupId>
	<artifactId>hutool-all</artifactId>
	<version>5.8.2</version>
</dependency>
<!-- 国密Jar -->
<dependency>
	<groupId>org.bouncycastle</groupId>
	<artifactId>bcprov-jdk15on</artifactId>
	<version>1.62</version>
	<scope>compile</scope>
</dependency>

创建SM3的工具类

import cn.hutool.crypto.SmUtil;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Objects;

@Component
public class Sm3UtilHua {

    private static Integer httpCheckSignTimeOut = 1;//请求签名有效时间 默认1分钟

    /**
     * SM3加密方式之:不提供密钥的方式 SM3加密,返回加密后长度为64位的16进制字符串
     *
     * @param src 明文
     * @return
     */
    public static String encrypt(String src) {
        return ByteUtils.toHexString(getEncryptBySrcByte(src.getBytes()));
    }

    /**
     * 返回长度为32位的加密后的byte数组
     *
     * @param srcByte
     * @return
     */
    public static byte[] getEncryptBySrcByte(byte[] srcByte) {
        SM3Digest sm3 = new SM3Digest();
        sm3.update(srcByte, 0, srcByte.length);
        byte[] encryptByte = new byte[sm3.getDigestSize()];
        sm3.doFinal(encryptByte, 0);
        return encryptByte;
    }

    /**
     * 获取实体类拼成的加密字段
     *
     * @param checkSign  前端传入的签名(从请求报文头获取)
     * @param signModel  查询的DTO模型类
     * @param privateKey 签名加密私钥
     * @param timestamp  时间戳(从请求报文头获取)
     * @return 比对结果
     */
    public boolean checkSign(String checkSign, Object signModel, String privateKey, Long timestamp) throws Exception {
        Long thisTime = System.currentTimeMillis() - timestamp;
        Integer checkSignTimeOut = httpCheckSignTimeOut;
        if (!(Objects.isNull(checkSignTimeOut) || checkSignTimeOut.intValue() == 0)) {
            //时间为0或者未配置签名超时时间,默认不验证时间戳
            if (thisTime >= 60 * 1000 * checkSignTimeOut || thisTime <= 0) {
                //checkSignTimeOut分钟内的时间戳才处理
                System.out.println("时间戳异常,非" + checkSignTimeOut + "分钟内请求,当前时间戳:" + System.currentTimeMillis());
                return false;
            }
        }
        String signValue = getSignValue(signModel) + "&timestamp=" + timestamp + "&privateKey=" + privateKey;
        String sign = getSign(signValue);
        System.out.println("【本地加密后 sm3 签名】" + sign);//生产上建议注释此行,防止泄露
        return sign.toUpperCase().equals(checkSign.toUpperCase()) ? true : false;
    }

    /**
     * 加密签名
     *
     * @param signValue 待加密签名字符串
     * @return 加密后签名字符串
     */
    public String getSign(String signValue) {
        return SmUtil.sm3(signValue);
    }

    /**
     * 获取实体类拼成的加密字段
     *
     * @param classA 传入参数实体类
     * @return 待加密字符串
     */
    public String getSignValue(Object classA) {
        Field[] fs = classA.getClass().getDeclaredFields();//获取所有属性
        String[][] temp = new String[fs.length][2]; //用二维数组保存  参数名和参数值
        for (int i = 0; i < fs.length; i++) {
            fs[i].setAccessible(true);
            temp[i][0] = fs[i].getName().toLowerCase(); //获取属性名
            try {
                temp[i][1] = String.valueOf(fs[i].get(classA));//把属性值放进数组
            } catch (Exception e) {
                System.out.println("【签名字段:" + fs[i].getName() + "添加失败】");
            }
        }
        temp = doChooseSort(temp); //对参数实体类按照字母顺序排续
        String result = "";
        for (int i = 0; i < temp.length; i++) {//按照签名规则生成待加密字符串
            result = result + temp[i][0] + "=" + temp[i][1] + "&";
        }
        result = result.substring(0, result.length() - 1);//消除掉最后的“&”
        System.out.println("【签名信息】{}" + result);
        return result;
    }

    /**
     * 对二维数组里面的数据进行选择排序,按字段名按abcd顺序排列
     *
     * @param data 未按照字母顺序排序的二维数组
     * @return
     */
    private String[][] doChooseSort(String[][] data) {//排序方式为选择排序
        String[][] temp = new String[data.length][2];
        temp = data;
        int n = temp.length;
        for (int i = 0; i < n - 1; i++) {
            int k = i;// 初始化最小值的小标
            for (int j = i + 1; j < n; j++) {
                if (temp[k][0].compareTo(temp[j][0]) > 0) {    //下标k字段名大于当前字段名
                    k = j;// 修改最大值的小标
                }
            }
            // 将最小值放到排序序列末尾
            if (k > i) {  //用相加相减法交换data[i] 和 data[k]
                String tempValue;
                tempValue = temp[k][0];
                temp[k][0] = temp[i][0];
                temp[i][0] = tempValue;
                tempValue = temp[k][1];
                temp[k][1] = temp[i][1];
                temp[i][1] = tempValue;
            }
        }
        return temp;
    }

}

创建SM2工具类

import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import org.bouncycastle.jce.interfaces.ECPrivateKey;
import org.bouncycastle.jce.interfaces.ECPublicKey;

import java.util.HashMap;
import java.util.Map;

public class Sm2Util {

    /**
     * 公钥常量
     */
    public static final String KEY_PUBLIC_KEY = "publicKey";
    /**
     * 私钥返回值常量
     */
    public static final String KEY_PRIVATE_KEY = "privateKey";

    /**
     * 生成SM2公私钥
     *
     * @return
     */
    public static Map<String, String> generateSm2Key() {
        SM2 sm2 = new SM2();
        ECPublicKey publicKey = (ECPublicKey) sm2.getPublicKey();
        ECPrivateKey privateKey = (ECPrivateKey) sm2.getPrivateKey();
        // 获取公钥
        byte[] publicKeyBytes = publicKey.getQ().getEncoded(false);
        String publicKeyHex = HexUtil.encodeHexStr(publicKeyBytes);

        // 获取64位私钥
        String privateKeyHex = privateKey.getD().toString(16);
        // BigInteger转成16进制时,不一定长度为64,如果私钥长度小于64,则在前方补0
        StringBuilder privateKey64 = new StringBuilder(privateKeyHex);
        while (privateKey64.length() < 64) {
            privateKey64.insert(0, "0");
        }

        Map<String, String> result = new HashMap<>();
        result.put(KEY_PUBLIC_KEY, publicKeyHex);
        result.put(KEY_PRIVATE_KEY, privateKey64.toString());
        return result;
    }

    /**
     * SM2私钥签名
     *
     * @param privateKey 私钥
     * @param content    待签名内容
     * @return 签名值
     */
    public static String sign(String privateKey, String content) {
        SM2 sm2 = new SM2(privateKey, null);
        return sm2.signHex(HexUtil.encodeHexStr(content));
    }

    /**
     * SM2公钥验签
     *
     * @param publicKey 公钥
     * @param content   原始内容
     * @param sign      签名
     * @return 验签结果
     */
    public static boolean verify(String publicKey, String content, String sign) {
        SM2 sm2 = new SM2(null, publicKey);
        return sm2.verifyHex(HexUtil.encodeHexStr(content), sign);
    }

    /**
     * SM2公钥加密
     *
     * @param content   原文
     * @param publicKey SM2公钥
     * @return
     */
    public static String encryptBase64(String content, String publicKey) {
        SM2 sm2 = new SM2(null, publicKey);
        return sm2.encryptBase64(content, KeyType.PublicKey);
    }

    /**
     * SM2私钥解密
     *
     * @param encryptStr SM2加密字符串
     * @param privateKey SM2私钥
     * @return
     */
    public static String decryptBase64(String encryptStr, String privateKey) {
        SM2 sm2 = new SM2(privateKey, null);
        return StrUtil.utf8Str(sm2.decrypt(encryptStr, KeyType.PrivateKey));
    }

}

首先开始生成SM2的公钥密钥

   public static Map<String, String> initSM2Key() {
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());  //设置权限 否则会报错
        SM2 sm2 = SmUtil.sm2();
        //这里会自动生成对应的随机秘钥对 , 注意! 这里一定要强转,才能得到对应有效的秘钥信息
        byte[] privateKey = BCUtil.encodeECPrivateKey(sm2.getPrivateKey());
        //这里公钥不压缩  公钥的第一个字节用于表示是否压缩  可以不要
        byte[] publicKey = ((BCECPublicKey) sm2.getPublicKey()).getQ().getEncoded(false);
        //打印当前的公私秘钥
        System.out.println("私钥: " + HexUtil.encodeHexStr(privateKey));
        System.out.println("公钥: " + HexUtil.encodeHexStr(publicKey));
        Map<String, String> map = Maps.newHashMap();
        map.put(OsConstants.PUBLIC_KEY, HexUtil.encodeHexStr(publicKey));
        map.put(OsConstants.PRIVATE_KEY, HexUtil.encodeHexStr(privateKey));
        return map;
    }

开始写自己的业务逻辑

// 创建参数(自己根据业务定义)
License license = new License();
license.setVersion("1.0");
license.setProduct("测试产品");
license.setOrganization("A公司");
license.setSignaturer("");  // 签名

// 生成公钥密钥 自己拿密钥 给对方公钥
Map<String, String> map = initSM2Key();
String publicKey = map.get(OsConstants.PUBLIC_KEY);
String privateKey = map.get(OsConstants.PRIVATE_KEY);

//使用SM3对参数进行加密 后期不需要反解析
String hexStrNoKey = Sm3UtilHua.encrypt(JSON.toJSONString(license));

// 使用私钥对参对加密后的参数进行一个加签 后续对方根据公钥可以进行验签
String sign = Sm2Util.sign(privateKey, hexStrNoKey);
System.out.println("签名:" + sign);

// 下面是验签过程 对方拿到公钥后调用SM2Utils进行验证
String str = Sm3UtilHua.encrypt(JSON.toJSONString(license));
boolean verify = Sm2Util.verify(publicKey, str , sign);
System.out.println("正确的验签结果:" + verify);
  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值