vue+java国密SM2\SM3加密

536 阅读4分钟

前端

下载依赖

npm地址gm-crypto - npm (npmjs.com)

npm install --save gm-crypto

SM3

const { SM3 } = require('gm-crypto')
console.log(SM3.digest('abc'))
console.log(SM3.digest('YWJj', 'base64'))
console.log(SM3.digest('616263', 'hex', 'base64'))
SM3.digest(this.password, 'base64') ;

SM2

# res.data.data 为公钥,从服务端获取,encryptedData为加密结果
const encryptedData = SM2.encrypt("需要加密的数据", res.data.data, {
                inputEncoding: 'utf8',
                outputEncoding: 'hex'
})

后端

import com.rabbiter.oes.constant.Sm;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.*;
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.pqc.crypto.util.PrivateKeyFactory;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.bouncycastle.util.encoders.Hex;

import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class SmUtils {
    private static final String EC = "EC";
    private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder();
    private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder();
    private static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider();
    private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");

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

    /**
     * 创建密钥生成器
     * @return
     */
    public static KeyPair createECKeyPair() {
        final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
        // 获取一个椭圆曲线类型的密钥对生成器
        final KeyPairGenerator kpg;
        try {
            kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
            kpg.initialize(sm2Spec, new SecureRandom());

            return kpg.generateKeyPair();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    public static String sm2Encrypt(String publicKeyHex, String data) {
        return sm2Encrypt(getECPublicKeyByPublicKeyHex(publicKeyHex), data, 1);
    }

    public static String sm2Encrypt(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);

        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);
    }

    public static BCECPublicKey getECPublicKeyByPublicKeyHex(String pubKeyHex) {

        if (pubKeyHex.length() > 128) {
            pubKeyHex = pubKeyHex.substring(pubKeyHex.length() - 128);
        }
        String stringX = pubKeyHex.substring(0, 64);
        String stringY = pubKeyHex.substring(stringX.length());
        BigInteger x = new BigInteger(stringX, 16);
        BigInteger y = new BigInteger(stringY, 16);

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

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

    /**
     * SM3加密方式之:不提供密钥的方式 SM3加密,返回加密后长度为64位的16进制字符串
     * @param src 明文
     * @return
     */
    public static String encrypt(String src) {
        return ByteUtils.toHexString(getEncryptBySrcByte(src.getBytes()));
    }

    /**
     * 返回长度为32位的加密后的byte数组
     * @param srcByte
     * @return
     */
    public static byte[] getEncryptBySrcByte(byte[] srcByte) {
        SM3Digest sm3 = new SM3Digest();
        sm3.update(srcByte, 0, srcByte.length);
        byte[] encryptByte = new byte[sm3.getDigestSize()];
        sm3.doFinal(encryptByte, 0);
        return encryptByte;
    }

    /**
     * SM2解密算法
     * @param privateKey 私钥
     * @param cipherData 密文数据
     * @param mode       密文排列方式
     * @return
     */
    public static byte[] sm2Decrypt(BigInteger privateKey, byte[] cipherData, SM2Engine.Mode mode) throws InvalidCipherTextException {
        final ASN1ObjectIdentifier sm2p256v1 = GMObjectIdentifiers.sm2p256v1;
        //获取一条SM2曲线参数
        X9ECParameters parameters = GMNamedCurves.getByOID(sm2p256v1);
        // 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数N
        ECNamedDomainParameters namedDomainParameters = new ECNamedDomainParameters(
                sm2p256v1, parameters.getCurve(), parameters.getG(), parameters.getN());
        ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKey, namedDomainParameters);
        SM2Engine sm2Engine = new SM2Engine(mode);
        // 设置sm2为解密模式
        sm2Engine.init(false, privateKeyParameters);
        // 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上
        if (cipherData[0] == 0x04) {
            return sm2Engine.processBlock(cipherData, 0, cipherData.length);
        } else {
            byte[] bytes = new byte[cipherData.length + 1];
            bytes[0] = 0x04;
            System.arraycopy(cipherData, 0, bytes, 1, cipherData.length);
            return sm2Engine.processBlock(bytes, 0, bytes.length);
        }
    }

    public static String decryptHex(String privateKey, String cipherData) throws InvalidCipherTextException {
        return decryptHex(privateKey, cipherData, SM2Engine.Mode.C1C3C2);
    }

    public static String decryptHex(String privateKey, String cipherData, SM2Engine.Mode mode) throws InvalidCipherTextException {
        final BigInteger key = new BigInteger(privateKey, 16);
        final byte[] decrypt = sm2Decrypt(key, Hex.decode(cipherData), mode);
        return new String(decrypt, StandardCharsets.UTF_8);
    }

    public static String decryptBase64(String privateKey, String cipherData) throws InvalidCipherTextException {
        return decryptBase64(privateKey, cipherData, SM2Engine.Mode.C1C3C2);
    }

    public static String decryptBase64(String privateKey, String cipherData, SM2Engine.Mode mode) throws InvalidCipherTextException {
        final BigInteger key = new BigInteger(BASE64_DECODER.decode(privateKey));
        final byte[] decrypt = sm2Decrypt(key, BASE64_DECODER.decode(cipherData), mode);
        return new String(decrypt, StandardCharsets.UTF_8);
    }

    public static void main(String[] args) throws IOException, InvalidCipherTextException {
        /**
         * sm3加密
         */
        String encrypt = encrypt("123456");
        System.out.println("encrypt sm3:"+encrypt);
        String publicKeyHex = null;
        String privateKeyHex = null;
        KeyPair keyPair = createECKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        if (publicKey instanceof BCECPublicKey) {
            //获取65字节非压缩缩的十六进制公钥串(0x04)
            publicKeyHex = Hex.toHexString(((BCECPublicKey) publicKey).getQ().getEncoded(false));
            System.out.println("SM2公钥:" + publicKeyHex);
        }
        PrivateKey privateKey = keyPair.getPrivate();
        if (privateKey instanceof BCECPrivateKey) {
            //获取32字节十六进制私钥串
            privateKeyHex = ((BCECPrivateKey) privateKey).getD().toString(16);
            System.out.println("SM2私钥:" + privateKeyHex);
        }

        /**
         * 公钥加密
         */
        String data = "123";
        //将十六进制公钥串转换为 BCECPublicKey 公钥对象
        String encryptData = sm2Encrypt(Sm.PUBLIC_KEY, data);
        System.out.println("加密结果:" + encryptData);
        /**
         * 私钥解密
         */
        String decrypted = decryptHex(Sm.PRIVATE_KEY,encryptData);
        System.out.println(decrypted);
    }
}

结果

encrypt sm3:207cf410532f92a47dee245ce9b11ff71f578ebd763eb3bbea44ebd043d018fb
SM2公钥:04560523505ec7d226171b3d802c1dc070ac30408d1ecccb8e449adc7f47c8b93ca300ba9e1b4c362d24cdd980558633d2c14d37d0d24023aefa737833eb62e046
SM2私钥:b7c5b4504dafa92aa6a680234c95108ef422572578973df0f52e71239cad1679
加密结果:0494ccab2c9a28991b9fa993c5d5f9c11f4c3df04b589d644d6aed6ae7ddc6d775f877e4f629ad57232fa281de0dab6dd495c7fa3c96093e2c7801a119d56f35a68b2f605dfa583a6f936c2aa6f2ead746cb5c8e3682b7f1a87e5eadc73966beb6e09331
解密结果:123