diff --git a/docs/不加密、SM2+AES加密和SSL加密效率测试.MD b/docs/不加密、SM2+AES加密和SSL加密效率测试.MD new file mode 100644 index 00000000..e69de29b diff --git a/neutrino-proxy-core/src/main/java/org/dromara/neutrinoproxy/core/util/AesUtil.java b/neutrino-proxy-core/src/main/java/org/dromara/neutrinoproxy/core/util/AesUtil.java new file mode 100644 index 00000000..4606181f --- /dev/null +++ b/neutrino-proxy-core/src/main/java/org/dromara/neutrinoproxy/core/util/AesUtil.java @@ -0,0 +1,58 @@ +package org.dromara.neutrinoproxy.core.util; + +import cn.hutool.core.util.RandomUtil; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Random; + +/** + * AES工具 + */ +public class AesUtil { + + public static byte[] generateKey() { + byte[] keyBytes = new byte[16]; + Random random = RandomUtil.getRandom(true); + random.nextBytes(keyBytes); + return keyBytes; + } + + /** + * AES解密 + * @param decryptKey 秘钥,16位 + * @param encryptBytes 密文 + * @return 明文 + * @throws Exception + */ + public static byte[] decrypt(byte[] decryptKey, byte[] encryptBytes) { + try{ + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey, "AES")); + return cipher.doFinal(encryptBytes); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * AES加密 + * @param encryptKey 秘钥,必须为16个字符组成 + * @param data 明文 + * @return 密文 + * @throws Exception + */ + public static byte[] encrypt(byte[] encryptKey, byte[] data) { + try { + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey, "AES")); + return cipher.doFinal(data); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/neutrino-proxy-core/src/main/java/org/dromara/neutrinoproxy/core/util/EncryptUtil.java b/neutrino-proxy-core/src/main/java/org/dromara/neutrinoproxy/core/util/EncryptUtil.java index 6a660419..b1e67256 100644 --- a/neutrino-proxy-core/src/main/java/org/dromara/neutrinoproxy/core/util/EncryptUtil.java +++ b/neutrino-proxy-core/src/main/java/org/dromara/neutrinoproxy/core/util/EncryptUtil.java @@ -27,10 +27,15 @@ import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.SmUtil; import cn.hutool.crypto.symmetric.SymmetricAlgorithm; import cn.hutool.crypto.symmetric.SymmetricCrypto; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; +import org.bouncycastle.util.encoders.Hex; import org.dromara.neutrinoproxy.core.KeyPairRecord; import javax.crypto.SecretKey; import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; /** * 国密算法加解密工具 @@ -44,14 +49,25 @@ public class EncryptUtil { * @return */ public static KeyPairRecord generateSm2KeyPair() { - KeyPair keyPair = SecureUtil.generateKeyPair("SM2"); - byte[] privateKeyBytes = keyPair.getPrivate().getEncoded(); - byte[] publicKeyBytes = keyPair.getPublic().getEncoded(); - String privateKey = HexUtil.encodeHexStr(privateKeyBytes); - String publicKey = HexUtil.encodeHexStr(publicKeyBytes); + String privateKeyHex = null; + String publicKeyHex = null; - return new KeyPairRecord(privateKey, publicKey); + KeyPair keyPair = Sm2Util.createECKeyPair(); + + PrivateKey privateKey = keyPair.getPrivate(); + if (privateKey instanceof BCECPrivateKey) { + //获取32字节十六进制私钥串 + privateKeyHex = ((BCECPrivateKey) privateKey).getD().toString(16); + } + + PublicKey publicKey = keyPair.getPublic(); + if (publicKey instanceof BCECPublicKey) { + //获取65字节非压缩缩的十六进制公钥串(0x04) + publicKeyHex = Hex.toHexString(((BCECPublicKey) publicKey).getQ().getEncoded(false)); + } + + return new KeyPairRecord(privateKeyHex, publicKeyHex); } /** @@ -61,7 +77,7 @@ public class EncryptUtil { * @return 加密后的字节数组 */ public static byte[] encryptBySm2(String publicKey, byte[] data) { - return SmUtil.sm2(null, publicKey).encrypt(data); + return Sm2Util.encrypt(publicKey, data); } /** @@ -71,7 +87,7 @@ public class EncryptUtil { * @return 解密后的字节数组 */ public static byte[] decryptBySm2(String privateKey, byte[] data) { - return SmUtil.sm2(privateKey, null).decrypt(data); + return Sm2Util.decrypt(privateKey, data); } public static byte[] generateSm4Key() { @@ -99,7 +115,7 @@ public class EncryptUtil { } public static byte[] generateAesKey() { - return SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded(); + return AesUtil.generateKey(); } /** @@ -109,8 +125,7 @@ public class EncryptUtil { * @return 加密后的数据 */ public static byte[] encryptByAes(byte[] key, byte[] data) { - SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, key); - return aes.encrypt(data); + return AesUtil.encrypt(key, data); } /** @@ -120,8 +135,7 @@ public class EncryptUtil { * @return 解密后的数据 */ public static byte[] decryptByAes(byte[] key, byte[] encryptedData) { - SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, key); - return aes.decrypt(encryptedData); + return AesUtil.decrypt(key, encryptedData); } /** diff --git a/neutrino-proxy-core/src/main/java/org/dromara/neutrinoproxy/core/util/Sm2Util.java b/neutrino-proxy-core/src/main/java/org/dromara/neutrinoproxy/core/util/Sm2Util.java new file mode 100644 index 00000000..9a4af786 --- /dev/null +++ b/neutrino-proxy-core/src/main/java/org/dromara/neutrinoproxy/core/util/Sm2Util.java @@ -0,0 +1,216 @@ +package org.dromara.neutrinoproxy.core.util; + +import org.bouncycastle.asn1.gm.GMNamedCurves; +import org.bouncycastle.asn1.x9.X9ECParameters; +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.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.jce.spec.ECPrivateKeySpec; +import org.bouncycastle.jce.spec.ECPublicKeySpec; + +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.spec.ECGenParameterSpec; + +/** + * @ClassName SM2Utils + * @Description SM2算法工具类 + */ +public class Sm2Util { + + /** + * @Description 生成秘钥对 + * @return KeyPair + */ + public static KeyPair createECKeyPair() { + //使用标准名称创建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; + } + } + + /** + * @Description 公钥加密 + * @param publicKeyHex SM2十六进制公钥 + * @param data 明文数据 + * @return String + */ + public static byte[] encrypt(String publicKeyHex, byte[] data) { + return encrypt(getECPublicKeyByPublicKeyHex(publicKeyHex), data, 1); + } + + /** + * @Description 公钥加密 + * @param publicKey SM2公钥 + * @param data 明文数据 + * @param modeType 加密模式 + * @return String + */ + public static byte[] encrypt(BCECPublicKey publicKey, byte[] 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 { + //将明文字符串转换为指定编码的字节串 + //通过加密引擎对字节数串行加密 + arrayOfBytes = sm2Engine.processBlock(data, 0, data.length); + } catch (Exception e) { + System.out.println("SM2加密时出现异常:" + e.getMessage()); + e.printStackTrace(); + } + //将加密后的字节串转换为十六进制字符串 + return arrayOfBytes; + } + + /** + * @Description 私钥解密 + * @param privateKeyHex SM2十六进制私钥 + * @param cipherData 密文数据 + * @return String + */ + public static byte[] decrypt(String privateKeyHex, byte[] cipherData) { + return decrypt(getBCECPrivateKeyByPrivateKeyHex(privateKeyHex), cipherData, 1); + } + + /** + * @Description 私钥解密 + * @param privateKey SM私钥 + * @param cipherDataByte 密文数据 + * @param modeType 解密模式 + * @return + */ + public static byte[] decrypt(BCECPrivateKey privateKey, byte[] cipherDataByte, int modeType) { + //解密模式 + SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2; + if (modeType != 1) + mode = SM2Engine.Mode.C1C2C3; + //通过私钥对象获取私钥的基本域参数。 + 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); + try { + //通过解密引擎对密文字节串进行解密 + return sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length); + } catch (Exception e) { + System.out.println("SM2解密时出现异常" + e.getMessage()); + } + return new byte[0]; + } + //椭圆曲线ECParameters ASN.1 结构 + private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1"); + //椭圆曲线公钥或私钥的基本域参数。 + private static ECParameterSpec ecDomainParameters = new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN()); + + /** + * @Description 公钥字符串转换为 BCECPublicKey 公钥对象 + * @param pubKeyHex 64字节十六进制公钥字符串(如果公钥字符串为65字节首个字节为0x04:表示该公钥为非压缩格式,操作时需要删除) + * @return BCECPublicKey SM2公钥对象 + */ + public static BCECPublicKey getECPublicKeyByPublicKeyHex(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 私钥对象 + * @param privateKeyHex 32字节十六进制私钥字符串 + * @return BCECPrivateKey SM2私钥对象 + */ + public static BCECPrivateKey getBCECPrivateKeyByPrivateKeyHex(String privateKeyHex) { + //将十六进制私钥字符串转换为BigInteger对象 + BigInteger d = new BigInteger(privateKeyHex, 16); + //通过私钥和私钥域参数集创建椭圆曲线私钥规范 + ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(d, ecDomainParameters); + //通过椭圆曲线私钥规范,创建出椭圆曲线私钥对象(可用于SM2解密和签名) + return new BCECPrivateKey("EC", ecPrivateKeySpec, BouncyCastleProvider.CONFIGURATION); + } + + public static void main(String[] args) { + /*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 = "az"; + + //将十六进制公钥串转换为 BCECPublicKey 公钥对象 + /*String encryptData = encrypt(publicKeyHex, data); + System.out.println("---->加密结果:" + encryptData);*/ + + /** + * 私钥解密 + */ + //将十六进制私钥串转换为 BCECPrivateKey 私钥对象 + /*data = decrypt("xx", "xx"); + System.out.println("---->解密结果:" + data);*/ + } +} +