SM2国密生成公私钥加密解密

import org.bouncycastle.asn1.gm.GMNamedCurves;

import org.bouncycastle.asn1.x9.X9ECParameters;

import org.bouncycastle.crypto.AsymmetricCipherKeyPair;

import org.bouncycastle.crypto.CryptoException;

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.jcajce.provider.asymmetric.ec.BCECPrivateKey;

import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import org.bouncycastle.jce.spec.ECParameterSpec;

import org.bouncycastle.jce.spec.ECPrivateKeySpec;

import org.bouncycastle.jce.spec.ECPublicKeySpec;

import org.bouncycastle.math.ec.ECPoint;

import org.bouncycastle.util.Strings;

import org.bouncycastle.util.encoders.Hex;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;

import java.math.BigInteger;

import java.security.*;

import java.security.spec.ECGenParameterSpec;

public class SM2Utils {

private static final LoggerLOGGER = LoggerFactory.getLogger(SM2Utils.class);

    /**

    * @Description 生成秘钥对

    * @Author zd

    * @return KeyPair

*/

    public static KeyPaircreateECKeyPair() {

//使用标准名称创建EC参数生成的参数规范

        final ECGenParameterSpec sm2Spec =new ECGenParameterSpec("sm2p256v1");

        // 获取一个椭圆曲线类型的密钥对生成器

        final KeyPairGenerator kpg;

        try {

kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());

            // 使用SM2算法域参数集初始化密钥生成器(默认使用以最高优先级安装的提供者的 SecureRandom 的实现作为随机源)

            // kpg.initialize(sm2Spec);

            // 使用SM2的算法域参数集和指定的随机源初始化密钥生成器

            kpg.initialize(sm2Spec, new SecureRandom());

            // 通过密钥生成器生成密钥对

            return kpg.generateKeyPair();

        }catch (Exception e) {

e.printStackTrace();

return null;

        }

}

/**

    * 生成SM2公私钥对

    * @return

    */

    private static AsymmetricCipherKeyPairgenKeyPair0() {

//获取一条SM2曲线参数

        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");

        //构造domain参数

        ECDomainParameters domainParameters =new ECDomainParameters(sm2ECParameters.getCurve(),

                sm2ECParameters.getG(), sm2ECParameters.getN());

        //1.创建密钥生成器

        ECKeyPairGenerator keyPairGenerator =new ECKeyPairGenerator();

        //2.初始化生成器,带上随机数

        try {

keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));

        }catch (NoSuchAlgorithmException e) {

LOGGER.error("生成公私钥对时出现异常:", e);

//            e.printStackTrace();

        }

//3.生成密钥对

        AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();

        return asymmetricCipherKeyPair;

    }

/**

    * @Description 公钥加密

    * @Author zd

    * @param publicKeyHex SM2十六进制公钥

    * @param data        明文数据

    * @return String

*/

    public static Stringencrypt(String publicKeyHex, String data) {

return encrypt(getECPublicKeyByPublicKeyHex(publicKeyHex), data, 1);

    }

/**

    * @Description 公钥加密

    * @Author zd

    * @param publicKey SM2公钥

    * @param data      明文数据

    * @param modeType  加密模式

    * @return String

*/

    public static Stringencrypt(BCECPublicKey publicKey, String data, int modeType) {

//加密模式

        SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;

        if (modeType !=1) {

mode = SM2Engine.Mode.C1C2C3;

        }

//通过公钥对象获取公钥的基本域参数。

        ECParameterSpec ecParameterSpec = publicKey.getParameters();

        ECDomainParameters ecDomainParameters =new ECDomainParameters(ecParameterSpec.getCurve(),

                ecParameterSpec.getG(), ecParameterSpec.getN());

        //通过公钥值和公钥基本参数创建公钥参数对象

        ECPublicKeyParameters ecPublicKeyParameters =new ECPublicKeyParameters(publicKey.getQ(), ecDomainParameters);

        //根据加密模式实例化SM2公钥加密引擎

        SM2Engine sm2Engine =new SM2Engine(mode);

        //初始化加密引擎

        sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));

        byte[] arrayOfBytes =null;

        try {

//将明文字符串转换为指定编码的字节串

            byte[] in = data.getBytes("utf-8");

            //通过加密引擎对字节数串行加密

            arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);

        }catch (Exception e) {

System.out.println("SM2加密时出现异常:" + e.getMessage());

            e.printStackTrace();

        }

//将加密后的字节串转换为十六进制字符串

        return Hex.toHexString(arrayOfBytes);

    }

/**

    * @Description 私钥解密

    * @Author zd

    * @param privateKeyHex SM2十六进制私钥

    * @param cipherData    密文数据

    * @return String

*/

    public static Stringdecrypt(String privateKeyHex, String cipherData) {

return decrypt(getBCECPrivateKeyByPrivateKeyHex(privateKeyHex), cipherData, 1);

    }

/**

    * @Description 私钥解密

    * @Author zd

    * @param privateKey SM私钥

    * @param cipherData 密文数据

    * @param modeType  解密模式

    * @return

    */

    public static Stringdecrypt(BCECPrivateKey privateKey, String cipherData, int modeType) {

//解密模式

        SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;

        if (modeType !=1) {

mode = SM2Engine.Mode.C1C2C3;

        }

//将十六进制字符串密文转换为字节数组(需要与加密一致,加密是:加密后的字节数组转换为了十六进制字符串)

        byte[] cipherDataByte = Hex.decode(cipherData);

        //通过私钥对象获取私钥的基本域参数。

        ECParameterSpec ecParameterSpec = privateKey.getParameters();

        ECDomainParameters ecDomainParameters =new ECDomainParameters(ecParameterSpec.getCurve(),

                ecParameterSpec.getG(), ecParameterSpec.getN());

        //通过私钥值和私钥钥基本参数创建私钥参数对象

        ECPrivateKeyParameters ecPrivateKeyParameters =new ECPrivateKeyParameters(privateKey.getD(),

                ecDomainParameters);

        //通过解密模式创建解密引擎并初始化

        SM2Engine sm2Engine =new SM2Engine(mode);

        sm2Engine.init(false, ecPrivateKeyParameters);

        String result =null;

        try {

//通过解密引擎对密文字节串进行解密

            byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);

            //将解密后的字节串转换为utf8字符编码的字符串(需要与明文加密时字符串转换成字节串所指定的字符编码保持一致)

            result =new String(arrayOfBytes, "utf-8");

        }catch (Exception e) {

System.out.println("SM2解密时出现异常" + e.getMessage());

        }

return result;

    }

//椭圆曲线ECParameters ASN.1 结构

    private static X9ECParametersx9ECParameters = GMNamedCurves.getByName("sm2p256v1");

    //椭圆曲线公钥或私钥的基本域参数。

    private static ECParameterSpececDomainParameters =new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());

    /**

    * @Description 公钥字符串转换为 BCECPublicKey 公钥对象

    * @Author zd

    * @param pubKeyHex 64字节十六进制公钥字符串(如果公钥字符串为65字节首个字节为0x04:表示该公钥为非压缩格式,操作时需要删除)

    * @return BCECPublicKey SM2公钥对象

    */

    public static BCECPublicKeygetECPublicKeyByPublicKeyHex(String pubKeyHex) {

//截取64字节有效的SM2公钥(如果公钥首个字节为0x04)

        if (pubKeyHex.length() >128) {

pubKeyHex = pubKeyHex.substring(pubKeyHex.length() -128);

        }

//将公钥拆分为x,y分量(各32字节)

        String stringX = pubKeyHex.substring(0, 64);

        String stringY = pubKeyHex.substring(stringX.length());

        //将公钥x、y分量转换为BigInteger类型

        BigInteger x =new BigInteger(stringX, 16);

        BigInteger y =new BigInteger(stringY, 16);

        //通过公钥x、y分量创建椭圆曲线公钥规范

        ECPublicKeySpec ecPublicKeySpec =new ECPublicKeySpec(x9ECParameters.getCurve().createPoint(x, y), ecDomainParameters);

        //通过椭圆曲线公钥规范,创建出椭圆曲线公钥对象(可用于SM2加密及验签)

        return new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION);

    }

/**

    * @Description 私钥字符串转换为 BCECPrivateKey 私钥对象

    * @Author zd

    * @param privateKeyHex 32字节十六进制私钥字符串

    * @return BCECPrivateKey SM2私钥对象

    */

    public static BCECPrivateKeygetBCECPrivateKeyByPrivateKeyHex(String privateKeyHex) {

//将十六进制私钥字符串转换为BigInteger对象

        BigInteger d =new BigInteger(privateKeyHex, 16);

        //通过私钥和私钥域参数集创建椭圆曲线私钥规范

        ECPrivateKeySpec ecPrivateKeySpec =new ECPrivateKeySpec(d, ecDomainParameters);

        //通过椭圆曲线私钥规范,创建出椭圆曲线私钥对象(可用于SM2解密和签名)

        return new BCECPrivateKey("EC", ecPrivateKeySpec, BouncyCastleProvider.CONFIGURATION);

    }

/**

    * 私钥签名

    * @param privateKey    私钥

    * @param content      待签名内容

    * @return

    */

    public static Stringsign(String privateKey, String content)throws CryptoException {

//待签名内容转为字节数组

        byte[] message = Hex.decode(content);

        //获取一条SM2曲线参数

        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");

        //构造domain参数

        ECDomainParameters domainParameters =new ECDomainParameters(sm2ECParameters.getCurve(),

                sm2ECParameters.getG(), sm2ECParameters.getN());

        BigInteger privateKeyD =new BigInteger(privateKey, 16);

        ECPrivateKeyParameters privateKeyParameters =new ECPrivateKeyParameters(privateKeyD, domainParameters);

        //创建签名实例

        SM2Signer sm2Signer =new SM2Signer();

        //初始化签名实例,带上ID,国密的要求,ID默认值:1234567812345678

        try {

sm2Signer.init(true, new ParametersWithID(new ParametersWithRandom(privateKeyParameters, SecureRandom.getInstance("SHA1PRNG")), Strings.toByteArray("1234567812345678")));

        }catch (NoSuchAlgorithmException e) {

log.error("签名时出现异常:", e);

        }

sm2Signer.update(message, 0, message.length);

        //生成签名,签名分为两部分r和s,分别对应索引0和1的数组

        byte[] signBytes = sm2Signer.generateSignature();

        String sign = Hex.toHexString(signBytes);

        return sign;

    }

/**

    * 验证签名

    * @param publicKey    公钥

    * @param content      待签名内容

    * @param sign          签名值

    * @return

    */

    public static boolean verify(String publicKey, String content, String sign) {

//待签名内容

        byte[] message = Hex.decode(content);

        byte[] signData = Hex.decode(sign);

        // 获取一条SM2曲线参数

        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");

        // 构造domain参数

        ECDomainParameters domainParameters =new ECDomainParameters(sm2ECParameters.getCurve(),

                sm2ECParameters.getG(),

                sm2ECParameters.getN());

        //提取公钥点

        ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKey));

        // 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04

        ECPublicKeyParameters publicKeyParameters =new ECPublicKeyParameters(pukPoint, domainParameters);

        //创建签名实例

        SM2Signer sm2Signer =new SM2Signer();

        ParametersWithID parametersWithID =new ParametersWithID(publicKeyParameters, Strings.toByteArray("1234567812345678"));

        sm2Signer.init(false, parametersWithID);

        sm2Signer.update(message, 0, message.length);

        //验证签名结果

        boolean verify = sm2Signer.verifySignature(signData);

        return verify;

    }

public static void main(String[] args)throws UnsupportedEncodingException, CryptoException, NoSuchAlgorithmException, InvalidAlgorithmParameterException {

String data ="{\"head\":{\"reqCode\":\"1001\",\"reqDate\":\"20220321\",\"reqTime\":\"231815\",\"reqId\":\"2022\",\"bbkNbr\":\"379\"},\"body\":{\"regCode\":\"8238003300209049600\"}}";

        String timestamp = String.valueOf(System.currentTimeMillis());

        AsymmetricCipherKeyPair asymmetricCipherKeyPair =genKeyPair0();

        //提取公钥点

        ECPoint ecPoint = ((ECPublicKeyParameters) asymmetricCipherKeyPair.getPublic()).getQ();

        //公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥,04的时候,可以去掉前面的04

        String publicStr = Hex.toHexString(ecPoint.getEncoded(false));

        BigInteger privatekey = ((ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate()).getD();

        String privateStr = privatekey.toString(16);

        System.out.println("---->生成公钥:" + publicStr);

        System.out.println("---->生成私钥:" + privateStr);

        String encryptData =encrypt(publicStr, data);

        System.out.println("---->加密结果:" + encryptData);

        String decryptData =decrypt(privateStr,encryptData);

        System.out.println("---->解密结果:" + decryptData);

        String sign = SM2Utils.sign(privateStr,Hex.toHexString((data + timestamp).getBytes()));

        System.out.println("---->签名结果:" + sign);

        boolean verify = SM2Utils.verify(publicStr, Hex.toHexString((data + timestamp).getBytes()), sign);

        System.out.println("---->验签结果:" + verify);

    }

}

你可能感兴趣的:(SM2国密生成公私钥加密解密)