mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-04-22 12:05:05 +08:00
247 lines
12 KiB
Java
247 lines
12 KiB
Java
|
|
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));
|
|||
|
|
}
|
|||
|
|
}
|