mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-04-20 02:55:04 +08:00
新增 国密二加密方式Util
This commit is contained in:
@@ -178,6 +178,20 @@
|
||||
<version>3.10.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>Sm2Util</groupId>
|
||||
<artifactId>Sm2Util</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcpg-jdk18on -->
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpg-jdk18on</artifactId>
|
||||
<version>1.77</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
package com.jsowell.common.util;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import org.bouncycastle.crypto.CryptoException;
|
||||
import org.bouncycastle.crypto.engines.SM2Engine;
|
||||
import org.bouncycastle.crypto.params.*;
|
||||
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.spec.ECParameterSpec;
|
||||
import org.bouncycastle.util.Strings;
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
|
||||
import java.security.*;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* 国密二加密 Util
|
||||
*
|
||||
* @author Lemon
|
||||
* @Date 2024/5/7 10:23:56
|
||||
*/
|
||||
public class Sm2Util {
|
||||
|
||||
static{
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机生成sm2的公钥私钥对
|
||||
* @return 公钥私钥对
|
||||
*/
|
||||
public static KeyPair generateSm2KeyPair() {
|
||||
try {
|
||||
final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
|
||||
// 获取一个椭圆曲线类型的密钥对生成器
|
||||
final KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
|
||||
SecureRandom random = new SecureRandom();
|
||||
// 使用SM2的算法区域初始化密钥生成器
|
||||
kpg.initialize(sm2Spec, random);
|
||||
// 获取密钥对
|
||||
KeyPair keyPair = kpg.generateKeyPair();
|
||||
return keyPair;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data 加密前的json数据
|
||||
* @param platformPublicKeyStr 点行平台的国密二公钥
|
||||
* @param thirdPartyPrivateKeyStr 第三方平台生成的国密二私钥
|
||||
* @return 用于http发送请求的数据
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws InvalidKeySpecException
|
||||
* @throws NoSuchProviderException
|
||||
* @throws CryptoException
|
||||
*/
|
||||
public static String generateEncryptedRequestInfo(JSONObject data, String platformPublicKeyStr, String thirdPartyPrivateKeyStr)
|
||||
throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException, CryptoException {
|
||||
String sign = sign(thirdPartyPrivateKeyStr, data);
|
||||
data.put("sign",sign);
|
||||
return encrypt(platformPublicKeyStr, data.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param publicKeyStr 以BASE64表示的sm2公钥
|
||||
* @param data 待加密字符串, UTF-8编码
|
||||
* @return 以BASE64表示的加密结果
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws NoSuchProviderException
|
||||
* @throws InvalidKeySpecException
|
||||
*/
|
||||
private static String encrypt(String publicKeyStr, String data) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
|
||||
BCECPublicKey publicKey = getPublicKey(Base64.decode(publicKeyStr));
|
||||
//通过公钥对象获取公钥的基本域参数。
|
||||
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(SM2Engine.Mode.C1C3C2);
|
||||
//初始化加密引擎
|
||||
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) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//将加密后的字节串转换为BASE 64字符串
|
||||
return Base64.toBase64String(arrayOfBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param privateKeyStr 以BASE64表示的sm2私钥
|
||||
* @param cipherData 以BASE64表示的加密结果
|
||||
* @return 解密后字符串, UTF-8编码
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws NoSuchProviderException
|
||||
* @throws InvalidKeySpecException
|
||||
*/
|
||||
private static String decrypt(String privateKeyStr, String cipherData) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
|
||||
BCECPrivateKey privateKey = getPrivateKey(Base64.decode(privateKeyStr));
|
||||
byte[] cipherDataByte = Base64.decode(cipherData);
|
||||
//通过私钥对象获取私钥的基本域参数。
|
||||
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(SM2Engine.Mode.C1C3C2);
|
||||
sm2Engine.init(false, ecPrivateKeyParameters);
|
||||
String result = null;
|
||||
try {
|
||||
//通过解密引擎对密文字节串进行解密
|
||||
byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
|
||||
//将解密后的字节串转换为utf8字符编码的字符串(需要与明文加密时字符串转换成字节串所指定的字符编码保持一致)
|
||||
result = new String(arrayOfBytes, "utf-8");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param privateKeyStr 以BASE64表示的sm2私钥
|
||||
* @param dataJson 待签名json数据
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws NoSuchProviderException
|
||||
* @throws InvalidKeySpecException
|
||||
* @throws CryptoException
|
||||
*/
|
||||
private static String sign(String privateKeyStr, JSONObject dataJson) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, CryptoException {
|
||||
BCECPrivateKey privateKey = getPrivateKey(Base64.decode(privateKeyStr));
|
||||
ECParameterSpec ecParameterSpec = privateKey.getParameters();
|
||||
ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
|
||||
ecParameterSpec.getG(), ecParameterSpec.getN());
|
||||
//通过私钥值和私钥钥基本参数创建私钥参数对象
|
||||
ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(privateKey.getD(),
|
||||
ecDomainParameters);
|
||||
|
||||
//创建签名实例
|
||||
SM2Signer sm2Signer = new SM2Signer();
|
||||
|
||||
//初始化签名实例,带上ID,国密的要求,ID默认值:1234567812345678
|
||||
try {
|
||||
sm2Signer.init(true, new ParametersWithID(new ParametersWithRandom(ecPrivateKeyParameters, SecureRandom.getInstance("SHA1PRNG")), Strings.toByteArray("1234567812345678")));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
String content = getSignContent(dataJson);
|
||||
byte[] message = content.getBytes();
|
||||
sm2Signer.update(message, 0, message.length);
|
||||
//生成签名,签名分为两部分r和s,分别对应索引0和1的数组
|
||||
byte[] signBytes = sm2Signer.generateSignature();
|
||||
return Base64.toBase64String(signBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param publicKeyStr 以BASE64表示的sm2公钥
|
||||
* @param dataJson 含有签名的待验签json数据
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws NoSuchProviderException
|
||||
* @throws InvalidKeySpecException
|
||||
*/
|
||||
private static boolean verify(String publicKeyStr, JSONObject dataJson) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
|
||||
BCECPublicKey publicKey = getPublicKey(Base64.decode(publicKeyStr));
|
||||
ECParameterSpec ecParameterSpec = publicKey.getParameters();
|
||||
ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
|
||||
ecParameterSpec.getG(), ecParameterSpec.getN());
|
||||
//通过公钥值和公钥基本参数创建公钥参数对象
|
||||
ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(publicKey.getQ(), ecDomainParameters);
|
||||
//创建签名实例
|
||||
SM2Signer sm2Signer = new SM2Signer();
|
||||
ParametersWithID parametersWithID = new ParametersWithID(ecPublicKeyParameters, Strings.toByteArray("1234567812345678"));
|
||||
sm2Signer.init(false, parametersWithID);
|
||||
String sign = (String) dataJson.remove("sign");
|
||||
String content = getSignContent(dataJson);
|
||||
byte[] message = content.getBytes();
|
||||
sm2Signer.update(message, 0, message.length);
|
||||
//验证签名结果
|
||||
return sm2Signer.verifySignature(Base64.decode(sign));
|
||||
}
|
||||
|
||||
private static String getSignContent(JSONObject rawData){
|
||||
JSONObject data = new JSONObject(new TreeMap<>());
|
||||
rawData.forEach((k,v) -> data.put(k,v));
|
||||
StringBuffer sb = new StringBuffer();
|
||||
data.forEach((k,v)->{
|
||||
if (v != null && !"".equals(v)) {
|
||||
sb.append(k + "=" + v + "&");
|
||||
}
|
||||
});
|
||||
String stringData = sb.toString();
|
||||
return stringData == null || stringData.isEmpty() ? "" : stringData.substring(0,stringData.length()-1);
|
||||
}
|
||||
|
||||
private static BCECPrivateKey getPrivateKey(byte[] privateBytes) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
PKCS8EncodedKeySpec eks2 = new PKCS8EncodedKeySpec(privateBytes);
|
||||
KeyFactory kf= KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
|
||||
return (BCECPrivateKey) kf.generatePrivate(eks2);
|
||||
}
|
||||
|
||||
private static BCECPublicKey getPublicKey(byte[] publicBytes) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
X509EncodedKeySpec eks = new X509EncodedKeySpec(publicBytes);
|
||||
KeyFactory kf = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
|
||||
return (BCECPublicKey) kf.generatePublic(eks);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException, CryptoException {
|
||||
// KeyPair keyPair = generateSm2KeyPair();
|
||||
// System.out.println(Base64.toBase64String(keyPair.getPublic().getEncoded()));
|
||||
// System.out.println(Base64.toBase64String(keyPair.getPrivate().getEncoded()));
|
||||
String platformPublicKeyStr = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEYPOlKmr/XY+na8KxiNvRui1esFugt4tT2AVk+eRlH4KCYLabDZDordal3kcn4UNM7t6J+dyhcfLstNWXpf4lQA==";
|
||||
String thirdPartyPrivateKeyStr = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgCMref1FGlPZ9RfeJw/cnU5uEvFZNhHt7OvF4sgXnBjWgCgYIKoEcz1UBgi2hRANCAARj6kqkCaeNJSxWExQFsot1OuSCFrQOblhKx0U/y8GhgSND2MOAM08yXzl308waLqLt+jcsLF2UTW6XfrZNS5pk";
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("outTradeId","202212231641900215435329");
|
||||
data.put("plateNum","浙B12345");
|
||||
data.put("startTime","2023-01-09 10:00:01");
|
||||
data.put("endTime","2023-01-09 11:00:02");
|
||||
data.put("recordTime","2023-01-09 12:00:03");
|
||||
data.put("payAmount","12.34");
|
||||
System.out.println(data);
|
||||
System.out.println(generateEncryptedRequestInfo(data,platformPublicKeyStr,thirdPartyPrivateKeyStr));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user