mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-04-21 11:35:12 +08:00
285 lines
14 KiB
Java
285 lines
14 KiB
Java
package com.jsowell.common.util;
|
||
|
||
import com.alibaba.fastjson2.JSON;
|
||
import com.jsowell.common.util.http.HttpUtils;
|
||
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.ArrayList;
|
||
import java.util.Collections;
|
||
import java.util.List;
|
||
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 String getSignContent(JSONObject rawData) {
|
||
StringBuilder content = new StringBuilder();
|
||
List<String> keys = new ArrayList<>(rawData.keySet());
|
||
// 将参数集合排序
|
||
Collections.sort(keys);
|
||
for (int i = 0; i < keys.size(); i++) {
|
||
String key = keys.get(i);
|
||
String value = (String) rawData.get(key);
|
||
// 拼装所有非空参数
|
||
if (key != null && !"".equalsIgnoreCase(key) && value != null && !"".equalsIgnoreCase(value)) {
|
||
content.append(i == 0 ? "" : "&").append(key).append("=").append(value);
|
||
}
|
||
}
|
||
return content.toString();
|
||
}
|
||
|
||
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","202212231641900215435");
|
||
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);
|
||
String recordInfoStr = generateEncryptedRequestInfo(data, platformPublicKeyStr, thirdPartyPrivateKeyStr);
|
||
String tpToken = "4121d1c9121a41d894ed5082d264db30";
|
||
|
||
// 发送请求
|
||
String url = "http://123.60.34.253:9527/parking-shop/tp/chargeRecord/report";
|
||
JSONObject postParam = new JSONObject();
|
||
postParam.put("recordInfo", recordInfoStr);
|
||
|
||
String result = HttpUtils.sendPostTpToken(url, postParam.toJSONString(), tpToken); // {"code":103,"msg":"数据验签不通过","data":null}
|
||
// String result = HttpUtil.post(url, postParam.toJSONString());
|
||
|
||
System.out.println(result);
|
||
}
|
||
|
||
public static JSONObject getPlaintextRequestInfo(String encryptedString, String platformPrivateKeyStr, String thirdPartyPublicKeyStr) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
|
||
String plaintext = decrypt(platformPrivateKeyStr, encryptedString);
|
||
JSONObject result = JSON.parseObject(plaintext);
|
||
if(verify(thirdPartyPublicKeyStr, result)){
|
||
return result;
|
||
}else{
|
||
return null;
|
||
}
|
||
}
|
||
}
|