基于BC库的国密算法 SM2算法工具

import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.engines.SM2Engine.Mode;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.custom.gm.SM2P256V1Curve;
 
import java.math.BigInteger;
import java.security.SecureRandom;
 
 
/**
 * SM2算法工具
 *
 */
public final class SM2Utils
{
    /**
     * Begin: SM2推荐曲线参数
     */
    private static final String SM2_ECC_GY_VAL = "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0";
    private static final String SM2_ECC_GX_VAL = "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7";
 
    private static final SM2P256V1Curve CURVE = new SM2P256V1Curve();
    private final static BigInteger SM2_ECC_N = CURVE.getOrder();
    private final static BigInteger SM2_ECC_H = CURVE.getCofactor();
 
    private final static BigInteger SM2_ECC_GX = new BigInteger(SM2_ECC_GX_VAL, 16);
    private final static BigInteger SM2_ECC_GY = new BigInteger(SM2_ECC_GY_VAL, 16);
 
    private static final ECPoint G_POINT = CURVE.createPoint(SM2_ECC_GX, SM2_ECC_GY);
    private static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(CURVE, G_POINT, SM2_ECC_N, SM2_ECC_H);
    /**
     * End: SM2推荐曲线参数
     */
 
    private SM2Utils()
    {
    }
 
    /**
     * 生成ECC密钥对
     *
     * @return ECC密钥对
     */
    public static AsymmetricCipherKeyPair generateKeyPairParameter()
    {
        final SecureRandom random = new SecureRandom();
        return BCECUtils.generateKeyPairParameter(DOMAIN_PARAMS, random);
    }
 
    /**
     * @param pubKey
     *                   公钥
     * @param srcData
     *                   原文
     * @return 默认输出C1C3C2顺序的密文。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
     * @throws InvalidCipherTextException
     */
    public static byte[] encrypt(final BCECPublicKey pubKey, final byte[] srcData) throws InvalidCipherTextException
    {
        return encrypt(Mode.C1C3C2, pubKey, srcData);
    }
 
    /**
     * @param pubKeyParams
     *                        公钥
     * @param srcData
     *                        原文
     * @return 默认输出C1C3C2顺序的密文。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
     * @throws InvalidCipherTextException
     */
    public static byte[] encrypt(final ECPublicKeyParameters pubKeyParams, final byte[] srcData) throws InvalidCipherTextException
    {
        return encrypt(Mode.C1C3C2, pubKeyParams, srcData);
    }
 
    /**
     * @param priKey
     *                     私钥
     * @param sm2Cipher
     *                     默认输入C1C3C2顺序的密文。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
     * @return 原文。SM2解密返回了数据则一定是原文,因为SM2自带校验,如果密文被篡改或者密钥对不上,都是会直接报异常的。
     * @throws InvalidCipherTextException
     */
    public static byte[] decrypt(final BCECPrivateKey priKey, final byte[] sm2Cipher) throws InvalidCipherTextException
    {
        return decrypt(Mode.C1C3C2, priKey, sm2Cipher);
    }
 
    /**
     * @param priKeyParams
     *                        私钥
     * @param sm2Cipher
     *                        默认输入C1C3C2顺序的密文。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
     * @return 原文。SM2解密返回了数据则一定是原文,因为SM2自带校验,如果密文被篡改或者密钥对不上,都是会直接报异常的。
     * @throws InvalidCipherTextException
     */
    public static byte[] decrypt(final ECPrivateKeyParameters priKeyParams, final byte[] sm2Cipher)
            throws InvalidCipherTextException
    {
        return decrypt(Mode.C1C3C2, priKeyParams, sm2Cipher);
    }
 
    /**
     * 签名
     *
     * @param priKey
     *                   私钥
     * @param srcData
     *                   原文
     * @return DER编码后的签名值
     * @throws CryptoException
     */
    public static byte[] sign(BCECPrivateKey priKey, byte[] srcData) throws CryptoException
    {
        return sign(priKey, null, srcData);
    }
 
    /**
     * 签名,默认withId为字节数组:"1234567812345678".getBytes()
     *
     * @param priKeyParams
     *                        私钥
     * @param srcData
     *                        原文
     * @return DER编码后的签名值
     * @throws CryptoException
     */
    public static byte[] sign(final ECPrivateKeyParameters priKeyParams, final byte[] srcData) throws CryptoException
    {
        return sign(priKeyParams, null, srcData);
    }
 
    /**
     * 验签
     *
     * @param pubKey
     *                   公钥
     * @param srcData
     *                   原文
     * @param sign
     *                   DER编码的签名值
     * @return boolean
     */
    public static boolean verify(final BCECPublicKey pubKey, final byte[] srcData, final byte[] sign)
    {
        return verify(pubKey, null, srcData, sign);
    }
 
    /**
     * 验签 不指定withId,则默认withId为字节数组:"1234567812345678".getBytes()
     *
     * @param pubKeyParams
     *                        公钥
     * @param srcData
     *                        原文
     * @param sign
     *                        DER编码的签名值
     * @return 验签成功返回true,失败返回false
     */
    public static boolean verify(final ECPublicKeyParameters pubKeyParams, final byte[] srcData, final byte[] sign)
    {
        return verify(pubKeyParams, null, srcData, sign);
    }
 
    /**
     * @param mode
     *                   指定密文结构,新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
     * @param pubKey
     *                   公钥
     * @param srcData
     *                   原文
     * @return 根据mode不同,输出的密文C1C2C3排列顺序不同。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
     * @throws InvalidCipherTextException
     */
    private static byte[] encrypt(final Mode mode, final BCECPublicKey pubKey, final byte[] srcData)
            throws InvalidCipherTextException
    {
        final ECPublicKeyParameters pubKeyParams = BCECUtils.convertPublicKeyToParameters(pubKey);
        return encrypt(mode, pubKeyParams, srcData);
    }
 
    /**
     * @param mode
     *                        指定密文结构,新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
     * @param pubKeyParams
     *                        公钥
     * @param srcData
     *                        原文
     * @return 根据mode不同,输出的密文C1C2C3排列顺序不同。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
     * @throws InvalidCipherTextException
     */
    private static byte[] encrypt(final Mode mode, final ECPublicKeyParameters pubKeyParams, final byte[] srcData)
            throws InvalidCipherTextException
    {
        final SM2Engine engine = new SM2Engine(mode);
        final ParametersWithRandom pwr = new ParametersWithRandom(pubKeyParams, new SecureRandom());
        engine.init(true, pwr);
        return engine.processBlock(srcData, 0, srcData.length);
    }
 
    /**
     * 验签
     *
     * @param pubKey
     *                   公钥
     * @param withId
     *                   可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
     * @param srcData
     *                   原文
     * @param sign
     *                   DER编码的签名值
     * @return boolean
     */
    private static boolean verify(final BCECPublicKey pubKey, final byte[] withId, final byte[] srcData, final byte[] sign)
    {
        final ECPublicKeyParameters pubKeyParams = BCECUtils.convertPublicKeyToParameters(pubKey);
        return verify(pubKeyParams, withId, srcData, sign);
    }
 
    /**
     * @param mode
     *                     指定密文结构,新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
     * @param priKey
     *                     私钥
     * @param sm2Cipher
     *                     根据mode不同,需要输入的密文C1C2C3排列顺序不同。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
     * @return 原文。SM2解密返回了数据则一定是原文,因为SM2自带校验,如果密文被篡改或者密钥对不上,都是会直接报异常的。
     * @throws InvalidCipherTextException
     */
    private static byte[] decrypt(final Mode mode, final BCECPrivateKey priKey, final byte[] sm2Cipher)
            throws InvalidCipherTextException
    {
        final ECPrivateKeyParameters priKeyParams = BCECUtils.convertPrivateKeyToParameters(priKey);
        return decrypt(mode, priKeyParams, sm2Cipher);
    }
 
    /**
     * @param mode
     *                        指定密文结构,新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
     * @param priKeyParams
     *                        私钥
     * @param sm2Cipher
     *                        根据mode不同,需要输入的密文C1C2C3排列顺序不同。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。
     * @return 原文。SM2解密返回了数据则一定是原文,因为SM2自带校验,如果密文被篡改或者密钥对不上,都是会直接报异常的。
     * @throws InvalidCipherTextException
     */
    private static byte[] decrypt(final Mode mode, final ECPrivateKeyParameters priKeyParams, final byte[] sm2Cipher)
            throws InvalidCipherTextException
    {
        final SM2Engine engine = new SM2Engine(mode);
        engine.init(false, priKeyParams);
        return engine.processBlock(sm2Cipher, 0, sm2Cipher.length);
    }
 
    /**
     * 私钥签名
     *
     * @param priKey
     *                   私钥
     * @param withId
     *                   可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
     * @param srcData
     *                   原文
     * @return DER编码后的签名值
     * @throws CryptoException
     */
    private static byte[] sign(final BCECPrivateKey priKey, final byte[] withId, final byte[] srcData) throws CryptoException
    {
        final ECPrivateKeyParameters priKeyParams = BCECUtils.convertPrivateKeyToParameters(priKey);
        return sign(priKeyParams, withId, srcData);
    }
 
    /**
     * 签名
     *
     * @param priKeyParams
     *                        私钥
     * @param withId
     *                        可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
     * @param srcData
     *                        源数据
     * @return DER编码后的签名值
     * @throws CryptoException
     */
    private static byte[] sign(final ECPrivateKeyParameters priKeyParams, final byte[] withId, final byte[] srcData)
            throws CryptoException
    {
        SM2Signer signer = new SM2Signer();
        CipherParameters param = null;
        ParametersWithRandom pwr = new ParametersWithRandom(priKeyParams, new SecureRandom());
        if (withId != null)
        {
            param = new ParametersWithID(pwr, withId);
        }
        else
        {
            param = pwr;
        }
        signer.init(true, param);
        signer.update(srcData, 0, srcData.length);
        return signer.generateSignature();
    }
 
    /**
     * 验签
     *
     * @param pubKeyParams
     *                        公钥
     * @param withId
     *                        可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
     * @param srcData
     *                        原文
     * @param sign
     *                        DER编码的签名值
     * @return 验签成功返回true,失败返回false
     */
    private static boolean verify(final ECPublicKeyParameters pubKeyParams, final byte[] withId, final byte[] srcData,
            final byte[] sign)
    {
        final SM2Signer signer = new SM2Signer();
        CipherParameters param;
        if (withId != null)
        {
            param = new ParametersWithID(pubKeyParams, withId);
        }
        else
        {
            param = pubKeyParams;
        }
        signer.init(false, param);
        signer.update(srcData, 0, srcData.length);
        return signer.verifySignature(sign);
    }
}

好的,我可以为你提供国密算法SM2的签名和验证签名示例。以下是示例代码: 签名示例(使用Java语言): ```java import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.crypto.digests.SM3Digest; import org.bouncycastle.crypto.engines.SM2Engine; import org.bouncycastle.crypto.generators.ECKeyPairGenerator; import org.bouncycastle.crypto.params.*; import org.bouncycastle.crypto.signers.SM2Signer; import org.bouncycastle.jce.provider.BouncyCastleProvider; import java.math.BigInteger; import java.security.*; import java.security.spec.ECGenParameterSpec; public class SM2SignatureExample { public static void main(String[] args) { Security.addProvider(new BouncyCastleProvider()); try { // 生成密钥对 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC"); ECGenParameterSpec ecGenParameterSpec = new ECGenParameterSpec("sm2p256v1"); keyPairGenerator.initialize(ecGenParameterSpec, new SecureRandom()); KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 获取私钥和公钥 PrivateKey privateKey = keyPair.getPrivate(); PublicKey publicKey = keyPair.getPublic(); // 待签名的数据 byte[] data = "Hello, SM2!".getBytes(); // 执行签名操作 SM2Signer signer = new SM2Signer(); signer.init(true, new ParametersWithRandom(privateKey, new SecureRandom())); signer.update(data, 0, data.length); byte[] signature = signer.generateSignature(); System.out.println("签名值: " + new BigInteger(1,
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

庞胖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值