当前位置:   article > 正文

国密SM2生成公钥密钥/SM3进行加签_php 使用sm2(sm2withsm3) 秘钥

php 使用sm2(sm2withsm3) 秘钥

使用国密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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

创建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;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133

创建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));
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99

首先开始生成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;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

开始写自己的业务逻辑

// 创建参数(自己根据业务定义)
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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/259750
推荐阅读
相关标签
  

闽ICP备14008679号