SM2国密加解、签名验签(包含ASN1转换)

依赖

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15to18</artifactId>
    <version>1.69</version>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.10</version>
</dependency>

代码

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.BCUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.core.codec.Base64;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.math.ec.ECPoint;

import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;

public class SM2Util {

    private static final int C1_LEN = 65;
    private static final int C3_LEN = 32;

    /**
     * 将c1c3c2转成标准的ASN1格式
     */
    public static byte[] changeC1C3C2ToAsn1(byte[] c1c3c2) throws IOException {
        byte[] c1 = Arrays.copyOfRange(c1c3c2, 0, C1_LEN);
        byte[] c3 = Arrays.copyOfRange(c1c3c2, C1_LEN, C1_LEN + C3_LEN);
        byte[] c2 = Arrays.copyOfRange(c1c3c2, C1_LEN + C3_LEN, c1c3c2.length);
        byte[] c1X = Arrays.copyOfRange(c1, 1, 33);
        byte[] c1Y = Arrays.copyOfRange(c1, 33, 65);

        BigInteger r = new BigInteger(1, c1X);
        BigInteger s = new BigInteger(1, c1Y);

        ASN1Integer x = new ASN1Integer(r);
        ASN1Integer y = new ASN1Integer(s);
        DEROctetString derDig = new DEROctetString(c3);
        DEROctetString derEnc = new DEROctetString(c2);
        ASN1EncodableVector v = new ASN1EncodableVector();
        v.add(x);
        v.add(y);
        v.add(derDig);
        v.add(derEnc);
        DERSequence seq = new DERSequence(v);
        return seq.getEncoded(ASN1Encoding.DER);
    }

    /**
     * 将ASN1格式转成c1c3c2
     */
    public static byte[] changeAsn1ToC1C3C2(byte[] asn1) throws IOException {
        ASN1InputStream aIn = new ASN1InputStream(asn1);
        ASN1Sequence seq = (ASN1Sequence) aIn.readObject();
        BigInteger x = ASN1Integer.getInstance(seq.getObjectAt(0)).getValue();
        BigInteger y = ASN1Integer.getInstance(seq.getObjectAt(1)).getValue();
        byte[] c3 = ASN1OctetString.getInstance(seq.getObjectAt(2)).getOctets();
        byte[] c2 = ASN1OctetString.getInstance(seq.getObjectAt(3)).getOctets();

        ECPoint c1Point =  GMNamedCurves.getByName("sm2p256v1").getCurve().createPoint(x, y);
        byte[] c1 = c1Point.getEncoded(false);
        return ArrayUtil.addAll(c1, c3, c2);
    }

    public static void main(String[] args) throws IOException {
        //需要加密的明文
        String text = "1111";
        //创建sm2 对象
        SM2 sm2 = SmUtil.sm2();
        //生成公钥
        byte[] publicKey = ((BCECPublicKey) sm2.getPublicKey()).getQ().getEncoded(false);
        //生成私钥
        byte[] privateKey = BCUtil.encodeECPrivateKey(sm2.getPrivateKey());
        //打印当前的公私秘钥
        System.out.println("公钥: " + HexUtil.encodeHexStr(publicKey));
        System.out.println("私钥: " + HexUtil.encodeHexStr(privateKey));

        SM2 sm2Test = SmUtil.sm2(privateKey, publicKey);
        byte[] encrypt = sm2Test.encrypt(text, KeyType.PublicKey);
        byte[] bytes = changeC1C3C2ToAsn1(encrypt);
        String encryptData = Base64.encode(bytes);
        System.out.println("加密后:"+encryptData);

        byte[] asn1 = Base64.decode(encryptData);
        byte[] c1c3c2 = changeAsn1ToC1C3C2(asn1);
        byte[] decryptData = sm2Test.decrypt(c1c3c2, KeyType.PrivateKey);
        System.out.println("解密后:"+new String(decryptData));

        byte[] sign = sm2Test.sign(text.getBytes());
        String signText = Base64.encode(sign);
        System.out.println("签名:"+ signText);
        boolean verify = sm2Test.verify(text.getBytes(), Base64.decode(signText));
        System.out.println("验签结果::"+ verify);
    }

}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值