Pārlūkot izejas kodu

国产密码算法

wukai 2 gadi atpakaļ
vecāks
revīzija
05f2dbaa53

+ 6 - 1
doc-common/pom.xml

@@ -161,7 +161,12 @@
             <artifactId>poi-ooxml</artifactId>
             <version>4.1.2</version>
         </dependency>
-
+        <!-- 国密算法 -->
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcmail-jdk15on</artifactId>
+            <version>1.70</version>
+        </dependency>
         <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>

+ 365 - 0
doc-common/src/main/java/com/doc/common/utils/encrypt/Sm2Util.java

@@ -0,0 +1,365 @@
+package com.doc.common.utils.encrypt;
+
+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.*;
+import java.security.spec.ECGenParameterSpec;
+import java.util.Base64;
+
+/**
+ * SM2工具类
+ *
+ * @author van
+ */
+public class Sm2Util {
+
+    /**
+     * 生成 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) {
+        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);
+    }
+
+    public static String encrypt(byte[] data, PublicKey publicKey) {
+        BCECPublicKey key = (BCECPublicKey) publicKey;
+        return encrypt(data, Hex.toHexString(key.getQ().getEncoded(false)));
+    }
+
+    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);
+    }
+
+    public static String decrypt(byte[] cipherData, PrivateKey privateKey) {
+        BCECPrivateKey key = (BCECPrivateKey) privateKey;
+        return decrypt(cipherData, Hex.toHexString(key.getD().toByteArray()));
+    }
+
+    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 publicKeyHexString hex130长度公钥
+     * @return ture or false
+     * @throws Exception
+     */
+    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 publicKeyHexString hex130长度公钥
+     * @return true or false
+     * @throws Exception
+     */
+    public static boolean verify(byte[] data, byte[] sign, String publicKeyHexString) throws Exception {
+        SM2Signer signer = new SM2Signer();
+        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
+        //构造domain参数
+        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
+        if (publicKeyHexString.length() == 128) {
+            publicKeyHexString = "04" + publicKeyHexString;
+        }
+        ECPoint ecPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKeyHexString));
+        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 例如:
+     *                   04181db7fe400641115c0dec08e23d8ddb94c5999f2fb6efd03030780142e077a63eb4d47947ef5baee7f40fec2c29181d2a714d9c6cba87b582f252a4e3e9a9f8
+     *                   11d0a44d47449d48d614f753ded6b06af76033b9c3a2af2b8b2239374ccbce3a
+     */
+    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;
+
+    }
+
+    public static void main(String[] args) throws Exception {
+        System.out.println("======  sm2utils test  ======");
+
+        String M = "data_message";
+        System.out.println("明文:\t" + M);
+
+        System.out.println("begin 开始生成密钥对>>>");
+        KeyPair keyPair = geneSM2KeyPair();
+
+        PublicKey publicKey = keyPair.getPublic();
+        String pubKeyHexString = getPubKeyHexString(publicKey);
+        System.out.println("publicKey\t" + pubKeyHexString);
+
+        PrivateKey privateKey = keyPair.getPrivate();
+        String priKeyHexString = getPriKeyHexString(privateKey);
+        System.out.println("privateKey\t" + priKeyHexString);
+        System.out.println("end   结束生成密钥对>>>");
+
+        priKeyHexString = "ebaedbccd3f730ec1985b945f10429b468c7f3f70e66da19b8657f023e6cded4"; //1
+        pubKeyHexString = "04404c634de4deda80486b4a331adf03448a449d980fe040540abe242ba275cc815aada6a63c63e24d4672e14360c72b1819914f49708f25498ededc2217384960"; //1
+
+        String cipherData = encrypt(M, pubKeyHexString);
+        System.out.println("加密:\t" + cipherData);
+
+        String text = decrypt(cipherData, priKeyHexString);
+        System.out.println("解密:\t" + text);
+
+        String sign = sign(M, priKeyHexString);
+        System.out.println("signvalue\t" + sign);
+        sign = "3045022100d69b69421985160ac2a116a006525b0a414090d8d4b815dd98f97c74c1de68bd02201bfbddc24b3ee159faf13948f1ba1ef3368a5d019ea4137d2f69d4d5aaf2a05a";
+
+        boolean verifyResult = verify(M, sign, pubKeyHexString);
+        System.out.println("verifyResult\t" + verifyResult);
+
+    }
+}

+ 35 - 0
doc-common/src/main/java/com/doc/common/utils/encrypt/Sm3Util.java

@@ -0,0 +1,35 @@
+package com.doc.common.utils.encrypt;
+
+import org.bouncycastle.crypto.digests.SM3Digest;
+import org.bouncycastle.util.encoders.Hex;
+
+/**
+ * SM3 HASH加密
+ *
+ * @author wukai
+ * @date 2023-11-02
+ */
+public class Sm3Util {
+    public static String encrypt(String str) {
+        SM3Digest sm3 = new SM3Digest();
+
+        // 将消息转换为字节数组
+        byte[] input = str.getBytes();
+
+        // 更新摘要
+        sm3.update(input, 0, input.length);
+
+        // 完成摘要计算
+        byte[] result = new byte[sm3.getDigestSize()];
+        sm3.doFinal(result, 0);
+
+        // 将摘要结果转换为十六进制字符串
+        String digestHex = Hex.toHexString(result);
+        return digestHex;
+    }
+
+    public static void main(String[] args) throws Exception {
+        String message = "Hello World!!!";
+        System.out.println("SM3 Digest: " + encrypt(message));
+    }
+}