SM2加密解密算法

1.pom引入包

	<dependency>
			<groupId>org.bouncycastle</groupId>
			<artifactId>bcprov-jdk15on</artifactId>
			<version>1.68</version>
		</dependency>

2.创建SM2Utils工具类

import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
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.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.ECGenParameterSpec;
import java.util.Base64;

/**
 * SM2工具类
 *
 * @author van
 */
public class SM2Utils {

    /**
     * 生成 SM2 公私钥对
     *
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidAlgorithmParameterException
     */
    public static KeyPair geneSM2KeyPair() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
        final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
        // 获取一个椭圆曲线类型的密钥对生成器
        final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
        // 产生随机数
        SecureRandom secureRandom = new SecureRandom();
        // 使用SM2参数初始化生成器
        kpg.initialize(sm2Spec, secureRandom);
        // 获取密钥对
        KeyPair keyPair = kpg.generateKeyPair();
        return keyPair;
    }

    /**
     * 生产hex秘钥对
     */
    public static void geneSM2HexKeyPair() {
        try {
            KeyPair keyPair = geneSM2KeyPair();
            PrivateKey privateKey = keyPair.getPrivate();
            PublicKey publicKey = keyPair.getPublic();
            System.out.println("========  EC X Y keyPair    ========");
            System.out.println(privateKey);
            System.out.println(publicKey);
            System.out.println("========  hex keyPair       ========");
            System.out.println("hex priKey: " + getPriKeyHexString(privateKey));
            System.out.println("hex pubKey: " + getPubKeyHexString(publicKey));
            System.out.println("========  base64 keyPair    ========");
            System.out.println("base64 priKey: " + new String(Base64.getEncoder().encode(privateKey.getEncoded())));
            System.out.println("base64 pubKey: " + new String(Base64.getEncoder().encode(publicKey.getEncoded())));
            System.out.println("========  generate finished ========");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取私钥(16进制字符串,头部不带00长度共64)
     *
     * @param privateKey 私钥PrivateKey型
     * @return
     */
    public static String getPriKeyHexString(PrivateKey privateKey) {
        // OK
//	        BCECPrivateKey s=(BCECPrivateKey)privateKey;
//	        String priKeyHexString = Hex.toHexString(s.getD().toByteArray());
//	        if(null!= priKeyHexString && priKeyHexString.length()==66 && "00".equals(priKeyHexString.substring(0,2))){
//	            return priKeyHexString.substring(2);
//	        }
        // OK
        BCECPrivateKey key = (BCECPrivateKey) privateKey;
        BigInteger intPrivateKey = key.getD();
        String priKeyHexString = intPrivateKey.toString(16);
        return priKeyHexString;
    }

    /**
     * 获取私钥 base64字符串
     *
     * @param privateKey 私钥PrivateKey型
     * @return
     */
    public static String getPriKeyBase64String(PrivateKey privateKey) {
        return new String(Base64.getEncoder().encode(privateKey.getEncoded()));
    }

    /**
     * 获取公钥(16进制字符串,头部带04长度共130)
     *
     * @param publicKey 公钥PublicKey型
     * @return
     */
    public static String getPubKeyHexString(PublicKey publicKey) {
        BCECPublicKey key = (BCECPublicKey) publicKey;
        return Hex.toHexString(key.getQ().getEncoded(false));
    }

    /**
     * 获取公钥 base64字符串
     *
     * @param publicKey 公钥PublicKey型
     * @return
     */
    public static String getPubKeyBase64String(PublicKey publicKey) {
        return new String(Base64.getEncoder().encode(publicKey.getEncoded()));
    }

    /**
     * SM2加密算法
     *
     * @param publicKey 公钥
     * @param data      明文数据
     * @return
     */
    public static String encrypt(String data, PublicKey publicKey) {
        return encrypt(data.getBytes(StandardCharsets.UTF_8), publicKey);
    }

    /**
     * @param data
     * @param publicKey
     * @return
     * @author
     * @version 1.0
     * 2023年4月12日下午4:41:24
     */
    public static String encrypt(byte[] data, PublicKey publicKey) {
        BCECPublicKey key = (BCECPublicKey) publicKey;
        return encrypt(data, Hex.toHexString(key.getQ().getEncoded(false)));
    }

    /**
     * @param data
     * @param pubKeyHexString
     * @return
     * @author
     * @version 1.0
     * 2023年4月12日下午4:46:37
     */
    public static String encrypt(String data, String pubKeyHexString) {
        return encrypt(data.getBytes(StandardCharsets.UTF_8), pubKeyHexString);
    }

    /**
     * SM2加密算法
     *
     * @param pubKeyHexString 公钥(16进制字符串)
     * @param data            明文数据
     * @return hex字符串
     */
    public static String encrypt(byte[] data, String pubKeyHexString) {
        // 获取一条SM2曲线参数
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        // 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数N
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
        //提取公钥点
        ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(pubKeyHexString));
        // 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
        ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);

        SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
        // 设置sm2为加密模式
        sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));

        byte[] arrayOfBytes = null;
        try {
            arrayOfBytes = sm2Engine.processBlock(data, 0, data.length);
        } catch (Exception e) {
            System.out.println("SM2加密时出现异常:" + e.getMessage());
        }
        return Hex.toHexString(arrayOfBytes);

    }

    /**
     * SM2解密算法
     *
     * @param cipherData hex格式密文
     * @param privateKey 密钥PrivateKey型
     * @return 明文
     */
    public static String decrypt(String cipherData, PrivateKey privateKey) {
        return decrypt(Hex.decode(cipherData), privateKey);
    }

    /**
     * @param cipherData
     * @param privateKey
     * @return
     * @author
     * @version 1.0
     * 2023年4月12日下午4:46:50
     */
    public static String decrypt(byte[] cipherData, PrivateKey privateKey) {
        BCECPrivateKey key = (BCECPrivateKey) privateKey;
        return decrypt(cipherData, Hex.toHexString(key.getD().toByteArray()));
    }

    /**
     * @param cipherData
     * @param priKeyHexString
     * @return
     * @author
     * @version 1.0
     * 2023年4月12日下午4:46:53
     */
    public static String decrypt(String cipherData, String priKeyHexString) {
        // 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上
        if (!cipherData.startsWith("04")) {
            cipherData = "04" + cipherData;
        }
        return decrypt(Hex.decode(cipherData), priKeyHexString);
    }

    /**
     * SM2解密算法
     *
     * @param cipherData      密文数据
     * @param priKeyHexString 私钥(16进制字符串)
     * @return
     */
    public static String decrypt(byte[] cipherData, String priKeyHexString) {
        //获取一条SM2曲线参数
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        //构造domain参数
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());

        BigInteger privateKeyD = new BigInteger(priKeyHexString, 16);
        ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);

        SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
        // 设置sm2为解密模式
        sm2Engine.init(false, privateKeyParameters);

        String result = "";
        try {
            byte[] arrayOfBytes = sm2Engine.processBlock(cipherData, 0, cipherData.length);
            return new String(arrayOfBytes);
        } catch (Exception e) {
            System.out.println("SM2解密时出现异常:" + e.getMessage());
        }
        return result;
    }

    /**
     * @param data
     * @param priKeyHexString hex私钥,长度64
     * @return hex格式签名值
     * @throws Exception
     */
    public static String sign(String data, String priKeyHexString) throws Exception {
        return sign(data.getBytes(StandardCharsets.UTF_8), priKeyHexString);
    }

    /**
     * 签名
     *
     * @param data            原始数据,字节数组
     * @param priKeyHexString hex私钥,64长度
     * @return Hex字符串
     * @throws Exception
     */
    public static String sign(byte[] data, String priKeyHexString) throws Exception {
        String signValue = null;
        SM2Signer signer = new SM2Signer();
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        //构造domain参数
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
        CipherParameters param = new ParametersWithRandom(new ECPrivateKeyParameters(new BigInteger(priKeyHexString, 16), domainParameters));
        signer.init(true, param);
        signer.update(data, 0, data.length);
        signValue = Hex.toHexString(signer.generateSignature());
        return signValue;
    }

    /**
     * 验签
     *
     * @param data        数据
     * @param signValue   签名值(hex型)
     * @param pKHexString hex130长度公钥
     * @return
     */
    public static boolean verify(String data, String signValue, String publicKeyHexString) throws Exception {
        return verify(data.getBytes(StandardCharsets.UTF_8), Hex.decode(signValue), publicKeyHexString);
    }

    /**
     * 验签
     *
     * @param data        原始数据字节数组
     * @param sign        字节数组()
     * @param pKHexString hex130长度公钥
     * @return true or false
     * @throws Exception
     */
    public static boolean verify(byte[] data, byte[] sign, String pKHexString) throws Exception {
        SM2Signer signer = new SM2Signer();
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        //构造domain参数
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
        if (pKHexString.length() == 128) {
            pKHexString = "04" + pKHexString;
        }
        ECPoint ecPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(pKHexString));
        CipherParameters param = new ECPublicKeyParameters(ecPoint, domainParameters);
        signer.init(false, param);
        signer.update(data, 0, data.length);
        return signer.verifySignature(sign);
    }

    /**
     * 私钥生成公钥
     *
     * @param priKeyHexString 私钥Hex格式,必须64位
     * @return 公钥Hex格式,04开头,130位
     * @throws Exception 例如:
     */
    public static String getPubKeyByPriKey(String priKeyHexString) throws Exception {
        if (priKeyHexString == null || priKeyHexString.length() != 64) {
            System.err.println("priKey 必须是Hex 64位格式,例如:11d0a44d47449d48d614f753ded6b06af76033b9c3a2af2b8b2239374ccbce3a");
            return "";
        }
        String pubKeyHexString = null;
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        //构造domain参数
        BigInteger privateKeyD = new BigInteger(priKeyHexString, 16);

        ECParameterSpec ecParameterSpec = new ECParameterSpec(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
        ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(privateKeyD, ecParameterSpec);
        PrivateKey privateKey = null;
        privateKey = KeyFactory.getInstance("EC", new BouncyCastleProvider()).generatePrivate(ecPrivateKeySpec);

        // 临时解决办法
        String pointString = privateKey.toString();
//	        System.out.println(pointString);
        String pointString_X = pointString.substring(pointString.indexOf("X: ") + "X: ".length(), pointString.indexOf("Y: ")).trim();
        String pointString_Y = pointString.substring(pointString.indexOf("Y: ") + "Y: ".length()).trim();
//	        System.out.println(pointString_X);
//	        System.out.println(pointString_Y);

        pubKeyHexString = "04" + pointString_X + pointString_Y;
        return pubKeyHexString;

    }

}

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是使用JavaScript实现SM2加密解密算法的基本步骤: 1. 安装依赖库 首先,我们需要下载并安装crypto-js和pako这两个JavaScript库。crypto-js是一个常用的加密算法库,而pako则用于进行数据压缩。 可以在命令行中使用npm命令来安装它们: ``` npm install crypto-js pako ``` 2. 生成密钥对 SM2算法是基于椭圆曲线密码学的公钥加密算法,因此我们需要生成一对公私钥对。可以使用crypto-js库中的随机数生成函数来生成私钥,然后通过椭圆曲线计算得到公钥。 ```javascript const CryptoJS = require('crypto-js'); const pako = require('pako'); // 生成随机私钥 const privateKey = CryptoJS.lib.WordArray.random(32); console.log('privateKey:', privateKey.toString()); // 计算公钥 const ecparams = CryptoJS.ECParams['sm2']; const curve = ecparams['curve']; const G = curve['G']; const publicKey = curve['multiply'](G, privateKey); console.log('publicKey:', publicKey.getEncoded(false).toString()); ``` 3. 加密数据 要加密数据,首先需要将明文数据进行压缩,然后添加一些头部信息。其中头部信息包括公钥、随机数等信息,这些信息都需要进行编码。最后,将编码后的头部信息和压缩后的明文数据一起加密。 ```javascript // 加密数据 const data = 'Hello, SM2!'; const compressedData = pako.deflate(data); const publicKeyBytes = publicKey.getEncoded(false); const userId = '1234567812345678'; const msg = CryptoJS.lib.WordArray.create(compressedData); const len = msg.sigBytes + publicKeyBytes.length + userId.length; const buf = new ArrayBuffer(len); const bufView = new Uint8Array(buf); let offset = 0; bufView.set(publicKeyBytes, offset); offset += publicKeyBytes.length; bufView.set(CryptoJS.enc.Utf8.parse(userId), offset); offset += userId.length; bufView.set(msg.words, offset); const cipher = CryptoJS.sm2.encrypt(CryptoJS.enc.Hex.parse(privateKey.toString()), CryptoJS.enc.Hex.parse(bufView.toString())); console.log('cipher:', cipher.toString()); ``` 4. 解密数据 要解密数据,首先需要将密文进行解密,然后获取头部信息。头部信息中包含了公钥、随机数等信息,我们可以使用这些信息来计算出会话密钥。最后,使用会话密钥来解密压缩后的明文数据。 ```javascript // 解密数据 const cipherText = CryptoJS.enc.Hex.parse(cipher.toString()); const decrypted = CryptoJS.sm2.decrypt(cipherText, CryptoJS.enc.Hex.parse(privateKey.toString())); const decryptedBuf = new Uint8Array(decrypted.words); offset = 0; const decryptedPublicKeyBytes = decryptedBuf.slice(offset, offset + publicKeyBytes.length); offset += publicKeyBytes.length; const decryptedUserId = CryptoJS.enc.Utf8.stringify(decryptedBuf.slice(offset, offset + userId.length)); offset += userId.length; const decryptedData = decryptedBuf.slice(offset); const decryptedPublicKey = curve['decodePointHex'](CryptoJS.enc.Hex.stringify(decryptedPublicKeyBytes)); const z = CryptoJS.sm2.getZ(decryptedUserId, decryptedPublicKey); const sessionKey = CryptoJS.sm2.getECDHKey(privateKey, decryptedPublicKey, z); console.log('sessionKey:', sessionKey.toString()); const decompressedData = pako.inflate(decryptedData); const plaintext = CryptoJS.AES.decrypt({ciphertext: CryptoJS.lib.WordArray.create(decompressedData)}, sessionKey, {iv: CryptoJS.lib.WordArray.create(16)}).toString(CryptoJS.enc.Utf8); console.log('plaintext:', plaintext); ``` 这样,我们就完成了SM2加密解密算法JavaScript实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值