Bootstrap

SM国密算法

国密算法代码案列

SM2、SM4 java工具类使用,提供常见签名、验签静态方法;代码生成公私钥(生产环境不建议)。

编写pom文件

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

SM2算法案列

SM2Support类

import org.bouncycastle.asn1.*;
import org.bouncycastle.jce.provider.JCEECPrivateKey;
import org.bouncycastle.jce.provider.JCEECPublicKey;
import org.bouncycastle.util.encoders.Hex;

import java.io.IOException;
import java.math.BigInteger;
import java.security.PublicKey;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECFieldFp;
import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve;
import java.util.Arrays;


/**
 * create by Cliven on 2018-07-31 13:48
 * 解析SM2算法的相关参数的工具
 *
 * @author Cliven
 * 其中参数参考《SM2椭圆曲线公钥密码算法推荐曲线参数》
 */
public class SM2Support {

    /**
     * SM2椭圆曲线公钥密码推荐参数
     */
    public static final byte P[] = {
            (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFE, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
            (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
            (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
            (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
    };
    public static final byte A[] = {
            (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFE, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
            (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
            (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
            (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC
    };
    public static final byte B[] = {
            (byte) 0x28, (byte) 0xE9, (byte) 0xFA, (byte) 0x9E, (byte) 0x9D, (byte) 0x9F, (byte) 0x5E, (byte) 0x34,
            (byte) 0x4D, (byte) 0x5A, (byte) 0x9E, (byte) 0x4B, (byte) 0xCF, (byte) 0x65, (byte) 0x09, (byte) 0xA7,
            (byte) 0xF3, (byte) 0x97, (byte) 0x89, (byte) 0xF5, (byte) 0x15, (byte) 0xAB, (byte) 0x8F, (byte) 0x92,
            (byte) 0xDD, (byte) 0xBC, (byte) 0xBD, (byte) 0x41, (byte) 0x4D, (byte) 0x94, (byte) 0x0E, (byte) 0x93
    };


    public static final byte GX[] = {
            (byte) 0x32, (byte) 0xC4, (byte) 0xAE, (byte) 0x2C, (byte) 0x1F, (byte) 0x19, (byte) 0x81, (byte) 0x19,
            (byte) 0x5F, (byte) 0x99, (byte) 0x04, (byte) 0x46, (byte) 0x6A, (byte) 0x39, (byte) 0xC9, (byte) 0x94,
            (byte) 0x8F, (byte) 0xE3, (byte) 0x0B, (byte) 0xBF, (byte) 0xF2, (byte) 0x66, (byte) 0x0B, (byte) 0xE1,
            (byte) 0x71, (byte) 0x5A, (byte) 0x45, (byte) 0x89, (byte) 0x33, (byte) 0x4C, (byte) 0x74, (byte) 0xC7
    };

    public static final byte GY[] = {
            (byte) 0xBC, (byte) 0x37, (byte) 0x36, (byte) 0xA2, (byte) 0xF4, (byte) 0xF6, (byte) 0x77, (byte) 0x9C,
            (byte) 0x59, (byte) 0xBD, (byte) 0xCE, (byte) 0xE3, (byte) 0x6B, (byte) 0x69, (byte) 0x21, (byte) 0x53,
            (byte) 0xD0, (byte) 0xA9, (byte) 0x87, (byte) 0x7C, (byte) 0xC6, (byte) 0x2A, (byte) 0x47, (byte) 0x40,
            (byte) 0x02, (byte) 0xDF, (byte) 0x32, (byte) 0xE5, (byte) 0x21, (byte) 0x39, (byte) 0xF0, (byte) 0xA0
    };
    /**
     * 阶
     */
    public static final byte N[] = {
            (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFE, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
            (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
            (byte) 0x72, (byte) 0x03, (byte) 0xDF, (byte) 0x6B, (byte) 0x21, (byte) 0xC6, (byte) 0x05, (byte) 0x2B,
            (byte) 0x53, (byte) 0xBB, (byte) 0xF4, (byte) 0x09, (byte) 0x39, (byte) 0xD5, (byte) 0x41, (byte) 0x23
    };

    /**
     * 基点G = (Xg, Yg)
     */
    public static final ECPoint G_POINT = new ECPoint(new BigInteger(1, GX), new BigInteger(1, GY));
    private static final EllipticCurve EllC = new EllipticCurve(new ECFieldFp(new BigInteger(1, P)), new BigInteger(1, A), new BigInteger(1, B));

    public static final java.security.spec.ECParameterSpec SM2_SPEC =
            new java.security.spec.ECParameterSpec(EllC, G_POINT, new BigInteger(1, N), 1);


    /**
     * 解析SM2 类型的公钥,并构造公钥对象
     *
     * @param publicKeyBytes 公钥字节串 格式为 X || Y
     * @return 公钥对象
     * @author Cliven
     * @since 2018-07-31 14:04:16
     */
    public static ECPublicKey parseSM2PublicKey(byte[] publicKeyBytes) {
        if (publicKeyBytes == null || publicKeyBytes.length == 0) {
            throw new IllegalArgumentException("publicKeyBytes is blank!");
        }
        byte[] pX = new byte[32];
        byte[] pY = new byte[32];
        // 从公钥字节中取出两个坐标值字节
        System.arraycopy(publicKeyBytes, 0, pX, 0, 32);
        System.arraycopy(publicKeyBytes, 32, pY, 0, 32);
        // 将坐标值字节转为大数字
        BigInteger bnX = new BigInteger(1, pX);
        BigInteger bnY = new BigInteger(1, pY);
        // 构造公钥点坐标 Pa = (Xa, Ya)
        ECPoint pubPoint = new ECPoint(bnX, bnY);
        // 构造椭圆曲线公钥
        java.security.spec.ECPublicKeySpec ecPublicKeySpec = new java.security.spec.ECPublicKeySpec(pubPoint, SM2_SPEC);
        return new JCEECPublicKey("SM2", ecPublicKeySpec);
    }

    /**
     * 解析SM2 类型的私钥数据,并且构造私钥对象
     *
     * @param privateKeyBytes 私钥字节串
     * @return 私钥对象
     * @author Cliven
     * @since 2018-07-31 14:17:31
     */
    public static ECPrivateKey parseSM2PrivateKey(byte[] privateKeyBytes) {
        if (privateKeyBytes == null || privateKeyBytes.length == 0) {
            throw new IllegalArgumentException("privateKeyBytes is blank!");
        }
        byte[] pD = new byte[32];
        // 从私钥数据中截取出私钥数据 k, k 属于 [1, n - 1]
        System.arraycopy(privateKeyBytes, privateKeyBytes.length - 32, pD, 0, 32);
        BigInteger bnD = new BigInteger(1, pD);
        // 构造椭圆曲线私钥
        java.security.spec.ECPrivateKeySpec ecPriSpec = new java.security.spec.ECPrivateKeySpec(bnD, SM2_SPEC);
        return new JCEECPrivateKey("SM2", ecPriSpec);
    }



    /**
     * 获取 X||Y 格式的公钥字节串
     *
     * @param publicKey 公钥对象
     * @return X||Y 格式的公钥字节串
     * @author Cliven
     * @since 2018-07-31 10:42:43
     */
    public static byte[] getPurePubKey(PublicKey publicKey) throws IOException {
        ASN1Sequence sequence = ASN1Sequence.getInstance(publicKey.getEncoded());

        // 取出公钥所在字段
        byte[] asn1PubKeySrc = sequence.getObjectAt(1).toASN1Primitive().getEncoded();
        DERBitString pubKeyBitString = DERBitString.getInstance(asn1PubKeySrc);
        byte[] puk = pubKeyBitString.getBytes();
        // 公钥格式为 04||X||Y (04为16进制字符,参考《GM/64-2012》)
        return Arrays.copyOfRange(puk, 1, puk.length);
    }


    /**
     * 获取ASN1Encodable 后32Byte,作为数据
     *
     * @param encodable 可编码的ASN1对象
     * @return 后32byte数据
     * @throws IOException 获取字节码时异常
     * @author Cliven
     * @since 2018-07-31 09:49:28
     */
    public static byte[] getSignValuePair(ASN1Encodable encodable) throws IOException {
        byte[] out = new byte[32];
        ASN1Integer pair = ASN1Integer.getInstance(encodable.toASN1Primitive().getEncoded());
        byte[] valueBytes = pair.getValue().toByteArray();
        System.arraycopy(valueBytes, valueBytes.length - 32, out, 0, 32);
        return out;
    }

    /**
     * r || s 类型的数据结构的字节串转ASN1的 字节串
     *
     * @param pureData r || s 类型的数据
     * @return ASN1编码的字节串
     * @throws IOException 获取ASN1编码异常、
     * @author Cliven
     * @since 2018-07-31 16:13:21
     */
    public static byte[] getPureSignData2ASN1(byte[] pureData) throws IOException {
        byte[] r = Arrays.copyOfRange(pureData, 0, 32);
        byte[] s = Arrays.copyOfRange(pureData, 32, pureData.length);

        ASN1EncodableVector vector = new ASN1EncodableVector();
        vector.add(new ASN1Integer(new BigInteger(1, r)));
        vector.add(new ASN1Integer(new BigInteger(1, s)));
        DERSequence sequence = new DERSequence(vector);
        return sequence.getEncoded();
    }

    /**
     * 16进制字符串转int
     *
     * @param hex 16进制字符串
     * @return 数字
     * @author Cliven
     * @since 2018-08-01 09:24:56
     */
    public static int hexStr2int(String hex) {
        byte[] len = Hex.decode(hex);
        return new BigInteger(1, len).intValue();
    }

}

SM2Util类

import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
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.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jcajce.spec.SM2ParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.util.encoders.Hex;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;

public class SM2Util {

    private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
    private static ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
    private static ECParameterSpec ecParameterSpec = new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());


    /**
     * 签名
     * @param content
     * @param userId
     * @param privateKey
     * @return
     */
    public static byte[] signSm3WithSm2(byte[] content, byte[] userId, PrivateKey privateKey){
        try {
            SM2ParameterSpec parameterSpec = new SM2ParameterSpec(userId);
            Signature signer = Signature.getInstance("SM3withSM2", new BouncyCastleProvider());
            signer.setParameter(parameterSpec);
            signer.initSign(privateKey, new SecureRandom());
            signer.update(content, 0, content.length);
            byte[] sig = signer.sign();
            return sig;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 验签
     * @param content
     * @param userId
     * @param sign
     * @param publicKey
     * @return
     */
    public static boolean verifySm3WithSm2(byte[] content, byte[] userId, byte[] sign, PublicKey publicKey){
        try {
            SM2ParameterSpec parameterSpec = new SM2ParameterSpec(userId);
            Signature verifier = Signature.getInstance("SM3withSM2", new BouncyCastleProvider());
            verifier.setParameter(parameterSpec);
            verifier.initVerify(publicKey);
            verifier.update(content, 0, content.length);
            return verifier.verify(sign);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * c1||c2||c3 加密
     * @param data
     * @param key
     * @return
     */
    public static byte[] sm2EncryptOld(byte[] data, PublicKey key){
        BCECPublicKey localECPublicKey = new BCECPublicKey((ECPublicKey) key, null);
        ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(localECPublicKey.getQ(), ecDomainParameters);
        SM2Engine sm2Engine = new SM2Engine();
        sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));
        try {
            return sm2Engine.processBlock(data, 0, data.length);
        } catch (InvalidCipherTextException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * c1||c2||c3 解密
     * @param data
     * @param key
     * @return
     */
    public static byte[] sm2DecryptOld(byte[] data, PrivateKey key){
        BCECPrivateKey localECPrivateKey = new BCECPrivateKey((ECPrivateKey) key, null);
        ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(localECPrivateKey.getD(), ecDomainParameters);
        SM2Engine sm2Engine = new SM2Engine();
        sm2Engine.init(false, ecPrivateKeyParameters);
        try {
            return sm2Engine.processBlock(data, 0, data.length);
        } catch (Exception e) {
            return null;
        }
    }


    public static void main(String[] args) {
        String pubHex = "0CA34C2BA39BFE44DD4DDCCE5452C07CF9F4F9575F7B59488A2DFC32971730F39F5CBE944A786F74AD323CB60DC3F2F2B50945F1C";
        ECPublicKey ecPublicKey = SM2Support.parseSM2PublicKey(Hex.decode(pubHex));
        String priHex = "989187BFC882562E7E7791ED5B49DFC2C3E8E8A0E4A183C3F0A040E81E32BDA4";
        ECPrivateKey ecPrivateKey = SM2Support.parseSM2PrivateKey(Hex.decode(priHex));

        String userId = "00003";
        String content = "0467cf298677a207d5b9c2ba8950dbdab9c7c91652bb580cc16e74bf68d3cac9bb178b3f6c4afdb77443b9e079767bc0e8bf67492fcb7d3c39598718df65cc721fe273601f1b751bf7ec80c43e0e9fbf4db23564f32801882d5a8441c79f39423b5f69c656e4a0a24030d870a4dcb05385dad14c691fc52ae15b6466d7666752752c1c12cfda927f9b6";
//        byte[] bytes = signSm3WithSm2(Hex.decode(content), userId.getBytes(), ecPrivateKey);
//        System.out.println("签名16进制:"+ByteUtils.toHexString(bytes));

        System.out.println(verifySm3WithSm2(Hex.decode(content), userId.getBytes(), Hex.decode("3044022078fdf53f7c474499959b3d88b65ff4f9c63d59d3364162d5e9b002206e6ff7ad1b0dca26e5827708f36bd42114cc11ef162fda2ea4e905c5"), ecPublicKey));

    }



}

SM4Util类

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;

public class SM4Util {

    public static final String ALGORITHM_NAME = "SM4";
    public static final String DEFAULT_KEY = "random_seed";
    // 128-32位16进制;256-64位16进制
    public static final int DEFAULT_KEY_SIZE = 128;


    public static byte[] generateKey() throws NoSuchAlgorithmException, NoSuchProviderException {
        return generateKey(DEFAULT_KEY, DEFAULT_KEY_SIZE);
    }

    public static byte[] generateKey(String seed) throws NoSuchAlgorithmException, NoSuchProviderException {
        return generateKey(seed, DEFAULT_KEY_SIZE);
    }

    public static byte[] generateKey(String seed, int keySize) throws NoSuchAlgorithmException, NoSuchProviderException {
        KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, new BouncyCastleProvider());
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        if (null != seed && !"".equals(seed)) {
            random.setSeed(seed.getBytes());
        }
        kg.init(keySize, random);
        return kg.generateKey().getEncoded();
    }

    /**
     * @description 加密
     */
    public static byte[] encrypt(String algorithmName, byte[] key, byte[] iv, byte[] data) throws Exception {
        return sm4core(algorithmName,Cipher.ENCRYPT_MODE, key, iv, data);
    }

    /**
     * @description 解密
     */
    public static byte[] decrypt(String algorithmName, byte[] key, byte[] iv, byte[] data) throws Exception {
        return sm4core(algorithmName, Cipher.DECRYPT_MODE, key, iv, data);
    }

    private static byte[] sm4core(String algorithmName, int type, byte[] key, byte[] iv, byte[] data) throws Exception {
        Cipher cipher = Cipher.getInstance(algorithmName, new BouncyCastleProvider());
        Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
        if (algorithmName.contains("/ECB/")) {
            cipher.init(type, sm4Key);
        } else {
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
            cipher.init(type, sm4Key, ivParameterSpec);
        }

        return cipher.doFinal(data);
    }

}
;