Java SM2 工具类,依赖bcprov-jdk15on

老版本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());
        }
    }
}
posted @ 2024-02-24 16:56  yy299  阅读(3614)  评论(0)    收藏  举报