首页 > 编程语言 >Java SM2 工具类,依赖bcprov-jdk15on

Java SM2 工具类,依赖bcprov-jdk15on

时间:2024-02-24 17:14:03浏览次数:20  
标签:Java 32 org bouncycastle SM2 jdk15on import new byte

老版本bcprov-jdk15on和新版本的有点不一样,新版本的签名后需要DER格式和r|s格式转换,可以和老版本一样

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.70</version>
</dependency>
import org.bouncycastle.asn1.*;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithID;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.Arrays;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.Security;
import java.util.Enumeration;
import java.util.function.Function;

/**
 * @author EvanY
 * @since 2023/01
 */
public class SM2Utils {
    public static final String CURVE_NAME = "sm2p256v1";

    private static ECDomainParameters getECDomainParameters() {
        ECParameterSpec spec = ECNamedCurveTable.getParameterSpec(CURVE_NAME);
        return new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN(), spec.getH(), spec.getSeed());
    }

    private static ECCurve getSM2Curve() {
        ECParameterSpec spec = ECNamedCurveTable.getParameterSpec(CURVE_NAME);
        return spec.getCurve();
    }

    private static ECPublicKeyParameters encodePublicKey(byte[] value) {
        byte[] x = new byte[32];
        byte[] y = new byte[32];
        System.arraycopy(value, 0, x, 0, 32);
        System.arraycopy(value, 32, y, 0, 32);
        BigInteger X = new BigInteger(1, x);
        BigInteger Y = new BigInteger(1, y);
        ECPoint Q = getSM2Curve().createPoint(X, Y);
        return new ECPublicKeyParameters(Q, getECDomainParameters());
    }

    public static byte[] sign(byte[] privateKey, byte[] iv, byte[] message) throws CryptoException {
        BigInteger d = new BigInteger(1, privateKey);
        CipherParameters privateKeyParam = new ECPrivateKeyParameters(d, getECDomainParameters());
        CipherParameters parameters = privateKeyParam;
        SM2Signer signer = new SM2Signer();
        if (iv != null && iv.length > 0) {
            parameters = new ParametersWithID(privateKeyParam, iv);
        }
        signer.init(true, parameters);
        signer.update(message, 0, message.length);
        return signer.generateSignature();
    }

    public static boolean verify(byte[] publicKey, byte[] iv, byte[] message, byte[] signature) {
        ECPublicKeyParameters pubKey = encodePublicKey(publicKey);
        CipherParameters parameters = pubKey;
        SM2Signer signer = new SM2Signer();
        if (iv != null && iv.length > 0) {
            signer.init(false, pubKey);
        } else {
            parameters = new ParametersWithID(pubKey, iv);
        }
        signer.init(false, parameters);
        signer.update(message, 0, message.length);
        return signer.verifySignature(signature);
    }
    /**
     * 将 r|s 格式的签名值转换为 DER 格式的签名
     */
    public static byte[] encodeDERSignature(byte[] signature) throws IOException {
        // 将签名的字节数组分割成两个 32 字节的数组,分别对应 R 和 S 的值
        byte[] r = new byte[32];
        byte[] s = new byte[32];
        System.arraycopy(signature, 0, r, 0, 32);
        System.arraycopy(signature, 32, s, 0, 32);
        // 将 R 和 S 的值转换为 BigInteger 对象,使用 ASN1Integer 类进行封装
        ASN1Integer rAsn1 = new ASN1Integer(new BigInteger(1, r));
        ASN1Integer sAsn1 = new ASN1Integer(new BigInteger(1, s));
        // 将两个 ASN1Integer 对象添加到一个 ASN1EncodableVector 对象中,使用 DERSequence 类进行编码
        ASN1EncodableVector vector = new ASN1EncodableVector();
        vector.add(rAsn1);
        vector.add(sAsn1);
        DERSequence derSequence = new DERSequence(vector);
        // 将 DERSequence 对象转换为字节数组,即为 DER 格式的签名
        return derSequence.getEncoded();
    }

    /**
     * 将DER格式的签名解码为r|s格式的签名值
     */
    public static byte[] decodeDERSignature(byte[] signature) throws IOException {
        // 格式化r和s的值,确保长度为32字节
        Function<byte[], byte[]> format = value -> {
            if (value.length == 32) {
                return value;
            } else {
                final byte[] bytes = new byte[32];
                if (value.length > 32) {
                    System.arraycopy(value, value.length - 32, bytes, 0, 32);
                } else {
                    System.arraycopy(value, 0, bytes, 32 - value.length, value.length);
                }
                return bytes;
            }
        };
        // 解析ASN.1结构,提取r和s的值
        final ASN1InputStream stream = new ASN1InputStream(new ByteArrayInputStream(signature));
        final ASN1Sequence primitive = (ASN1Sequence)stream.readObject();
        final Enumeration<?> enumeration = primitive.getObjects();
        final BigInteger R = ((ASN1Integer)enumeration.nextElement()).getValue();
        final BigInteger S = ((ASN1Integer)enumeration.nextElement()).getValue();
        final byte[] r = format.apply(R.toByteArray());
        final byte[] s = format.apply(S.toByteArray());
        // 拼接r和s的值,得到64字节的字节数组
        return Arrays.concatenate(r, s);
    }

    static {
        if (Security.getProvider("BC") == null) {
            Security.addProvider(new BouncyCastleProvider());
        }
    }
}

标签:Java,32,org,bouncycastle,SM2,jdk15on,import,new,byte
From: https://www.cnblogs.com/yy299/p/18031268

相关文章

  • Java SM4 工具类,依赖bcprov-jdk15on
    Java实现SM4国密加解密,依赖bcprov-jdk15on.jar<dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.70</version></dependency>/***@authorEvanY*@since......
  • Java基础05:类型转换
    类型转换1.由于Java是强类型语言,所以要进行有些运算的时候,需要用到类型转换低------------------------------------------------->高byte,short,char--->int--->long--->float--->double强制转换:由高类型转换到低类型  自动......
  • Java RSA 加解密工具类,直接用
    importorg.junit.Test;importjavax.crypto.Cipher;importjavax.crypto.NoSuchPaddingException;importjava.io.ByteArrayOutputStream;importjava.nio.charset.Charset;importjava.nio.charset.StandardCharsets;importjava.security.*;importjava.security.i......
  • Java_6 函数
    title:(在线学习平台)link:(https://www.acwing.com/)cover:(https://cdn.acwing.com/media/activity/surface/log.png)1函数基础一个典型的函数定义包括以下部分:修饰符、返回类型、函数名字、由0个或多个形参组成的列表以及函数体。1.1编写函数我们来编写一个求阶乘的......
  • java异常
    java异常异常分类Throwable是所有异常子类的超类exception子类:编译器异常,进行编译,java程序出现问题error子类:错误,必须修改源代码产生过程JVM判断异常,并产生包含异常信息的对象,如果异常产生的方法没有try--catch方法,如果没有则把异常对象返回main方法main方法也判......
  • Java锁的思想和区别
    乐观锁VS悲观锁乐观锁和悲观锁是两种处理并发访问的不同策略,用于确保多个操作不会同时修改同一资源而导致数据不一致的问题。它们的区别在于处理并发时的思想和实现方式:乐观锁:思想:认为在大多数情况下,读操作远远多于写操作,因此假设在绝大多数情况下并发冲突是不会发生的,直到出......
  • Javascript/DOM:如何删除 DOM 对象的所有事件侦听器
    Javascript/DOM:如何删除DOM对象的所有事件侦听器一、重写重写EventTarget添加监听事件方法addEventListenerif(EventTarget.prototype.original_addEventListener==null){EventTarget.prototype.original_addEventListener=EventTarget.prototype.addEventList......
  • Java语法之HelloWorld!
    HelloWorld入门随便新建一个文件夹(coed),存放代码新建一个Java文件文件后缀名为.JavaHello.java编写代码publicclassHello{ publicstaticvoidmain(String[]args){ System.out.print("Hello,World!"); }}编译javacjava文件,会生成一个class文件运......
  • 什么是Java中的SPI机制
    SPI,全称ServiceProviderInterface,是Java中提供的一种服务发现机制它允许应用程序动态地加载和使用第三方提供的服务实现,而无需在代码中引用这些实现类。JavaSPI是基于接口编程思想的具体体现,通过将服务接口和其实现分离,从而具备更好的可扩展性和可维护性如何定义一个Java......
  • Java事件侦听器学习记录
    前言我们监听事件之前要有事件源source,创建事件源(Event),发布事件(publishEvent),然后才能到监听事件。事件驱动机制是观察者模式(称发布订阅)具体实现,事件对象(Event)相当于被观察对象(Subject),事件监听(EventListener)相当于观察者(Observer)1、包结构(个人): 2、创建事件源(Event)......