Alipay-HK 接入 暂存
This commit is contained in:
329
src/main/java/com/ai/da/common/utils/AlipayHKEncryptionUtil.java
Normal file
329
src/main/java/com/ai/da/common/utils/AlipayHKEncryptionUtil.java
Normal file
@@ -0,0 +1,329 @@
|
||||
package com.ai.da.common.utils;
|
||||
|
||||
import com.ai.da.model.dto.AlipayHKParametersDTO;
|
||||
import com.ai.da.model.dto.AlipayHKRequestDTO;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bouncycastle.util.io.pem.PemObject;
|
||||
import org.bouncycastle.util.io.pem.PemReader;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
import javax.crypto.*;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.*;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
public class AlipayHKEncryptionUtil {
|
||||
|
||||
@Value("${alipay.hk.merchant-id}")
|
||||
private static String merchantId;
|
||||
|
||||
@Value("${alipay.hk.segment-id}")
|
||||
private static String segmentId;
|
||||
|
||||
@Value("${alipay.hk.AESKey}")
|
||||
private static String aesKey;
|
||||
|
||||
@Value("${alipay.hk.rsaPrivateKey}")
|
||||
private static String privateKeyPath;
|
||||
|
||||
@Value("${alipay.hk.rsaPublicKey}")
|
||||
private static String publicKeyPath;
|
||||
|
||||
/**
|
||||
* 加密
|
||||
* @param param
|
||||
* @param serviceName
|
||||
* @return
|
||||
* @throws NoSuchPaddingException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws InvalidAlgorithmParameterException
|
||||
* @throws InvalidKeyException
|
||||
* @throws IllegalBlockSizeException
|
||||
* @throws BadPaddingException
|
||||
* @throws IOException
|
||||
*/
|
||||
public static AlipayHKRequestDTO AESCBCWithRSA(HashMap<String, Object> param, String serviceName) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, IOException {
|
||||
// Pre-shared secret key, DO NOT hardcode this key
|
||||
String key = aesKey;
|
||||
// The path to the rsa private key file, DO NOT save this key to a publicly accessible location
|
||||
// 使用私钥创建数字签名
|
||||
// Mode, aes-128-cbc for 128bit key
|
||||
String mode = "aes-128-cbc";
|
||||
// Padding mode
|
||||
String padding = "pkcs7";
|
||||
// signature alogrithm
|
||||
String signatureAlgorithm = "rsa-sha256";
|
||||
// String signatureAlgorithm = "SHA256withRSA";
|
||||
// Message body
|
||||
AlipayHKParametersDTO requestMessage = new AlipayHKParametersDTO();
|
||||
requestMessage.setService(serviceName);
|
||||
requestMessage.setMerchant_id(merchantId);
|
||||
requestMessage.setRequest_time(Long.toString(System.currentTimeMillis() / 1000L));
|
||||
requestMessage.setRequest_uuid(UUID.randomUUID().toString());
|
||||
// HashMap<String, Object> param = new HashMap<>();
|
||||
requestMessage.setParameters(param);
|
||||
/*String orderRef = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
|
||||
param.put("order_ref", orderRef);
|
||||
param.put("amount", 208.12);
|
||||
param.put("subject", "四月帳單 April Bill");
|
||||
param.put("wallet", "ALIPAYHK");
|
||||
param.put("segment_id", segmentId);
|
||||
param.put("payment_solution", "WAP");*/
|
||||
|
||||
|
||||
// Serialize message body
|
||||
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
|
||||
String content = gson.toJson(requestMessage);
|
||||
|
||||
// Secure random iv 获取随机种子
|
||||
SecureRandom secureRandom = new SecureRandom();
|
||||
byte[] iv = new byte[16]; //NEVER REUSE THIS IV WITH THE SAME KEY
|
||||
secureRandom.nextBytes(iv);
|
||||
|
||||
// Algorithms
|
||||
String alogrithm = "AES/CBC/PKCS5Padding";
|
||||
|
||||
// 创建 AES 密钥对象
|
||||
SecretKey secretKey = new SecretKeySpec(key.getBytes(), "AES");
|
||||
// 为加密算法提供一个随机的初始化向量,以增强加密安全性
|
||||
IvParameterSpec parameterSpec = new IvParameterSpec(iv);
|
||||
// 创建 Cipher 对象
|
||||
Cipher cipher = Cipher.getInstance(alogrithm);
|
||||
// 初始化为加密模式 Cipher.ENCRYPT_MODE
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
|
||||
|
||||
// Cipher text 对请求内容加密
|
||||
byte[] cipherText = cipher.doFinal(content.getBytes());
|
||||
|
||||
// Concat iv and cipher text for signing
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
outputStream.write(iv);
|
||||
outputStream.write(cipherText);
|
||||
byte signMessage[] = outputStream.toByteArray();
|
||||
|
||||
// Sign key 读取rsa私钥文件,解析并得到私钥对象 privateKey
|
||||
String rsaPrivateKeyPath = privateKeyPath;
|
||||
PrivateKey privateKey = null;
|
||||
byte[] signature;
|
||||
try {
|
||||
privateKey = readPrivateKeyFromFile(rsaPrivateKeyPath);
|
||||
Signature signer = Signature.getInstance("SHA256withRSA");
|
||||
signer.initSign(privateKey);
|
||||
// 将加密内容和随机向量均加签
|
||||
signer.update(signMessage);
|
||||
signature = signer.sign();
|
||||
} catch (SignatureException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
Base64.Encoder encoder = Base64.getEncoder();
|
||||
// Combine result
|
||||
AlipayHKRequestDTO alipayHKRequestDTO = new AlipayHKRequestDTO();
|
||||
alipayHKRequestDTO.setMerchant_id(merchantId);
|
||||
alipayHKRequestDTO.setNonce(encoder.encodeToString(iv));
|
||||
alipayHKRequestDTO.setMessage(encoder.encodeToString(cipherText));
|
||||
alipayHKRequestDTO.setTag(encoder.encodeToString(signature));
|
||||
String cipherSuite = mode + "-" + padding + "-with-" + signatureAlgorithm;
|
||||
alipayHKRequestDTO.setCipher_suite(cipherSuite);
|
||||
|
||||
// Encode to json
|
||||
String jsonEncoded = JSONObject.toJSONString(alipayHKRequestDTO);
|
||||
log.info(jsonEncoded);
|
||||
|
||||
// String info = AlipayHKRequestUtil.createOrder(alipayHKRequestDTO);
|
||||
// log.info(info);
|
||||
return alipayHKRequestDTO;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件中读取RSA私钥
|
||||
* @param filePath
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
private static PrivateKey readPrivateKeyFromFile(String filePath) throws Exception {
|
||||
BufferedReader reader = new BufferedReader(new FileReader(filePath));
|
||||
StringBuilder keyBuffer = new StringBuilder();
|
||||
String line;
|
||||
|
||||
// 读取文本文件中的内容
|
||||
while ((line = reader.readLine()) != null) {
|
||||
keyBuffer.append(line);
|
||||
}
|
||||
reader.close();
|
||||
|
||||
// 去除私钥内容中的头部和尾部信息
|
||||
String privateKeyContent = keyBuffer.toString()
|
||||
.replace("-----BEGIN PRIVATE KEY-----", "")
|
||||
.replace("-----END PRIVATE KEY-----", "")
|
||||
.replaceAll("\\s+", ""); // 去除空格、换行等字符
|
||||
|
||||
// 进行 Base64 解码
|
||||
byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyContent);
|
||||
|
||||
// 根据 PKCS8 格式的私钥字节数组构造私钥对象
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
|
||||
return keyFactory.generatePrivate(keySpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 AES 密钥和随机向量进行解密
|
||||
*/
|
||||
public static String decryptAES(String encryptedText, String iv) throws Exception {
|
||||
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedText);
|
||||
byte[] ivBytes = Base64.getDecoder().decode(iv);
|
||||
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(aesKey.getBytes(), "AES");
|
||||
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
|
||||
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
|
||||
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
|
||||
|
||||
return new String(decryptedBytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static void test() throws Exception {
|
||||
// 加密数据
|
||||
AlipayHKParametersDTO requestMessage = new AlipayHKParametersDTO();
|
||||
requestMessage.setService("create_order");
|
||||
requestMessage.setMerchant_id(merchantId);
|
||||
requestMessage.setRequest_time(Long.toString(System.currentTimeMillis() / 1000L));
|
||||
requestMessage.setRequest_uuid(UUID.randomUUID().toString());
|
||||
HashMap<String, Object> param = new HashMap<>();
|
||||
requestMessage.setParameters(param);
|
||||
String orderRef = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
|
||||
param.put("order_ref", orderRef);
|
||||
param.put("amount", 208.12);
|
||||
param.put("subject", "四月帳單 April Bill");
|
||||
param.put("wallet", "ALIPAYHK");
|
||||
param.put("segment_id", segmentId);
|
||||
param.put("payment_solution", "WAP");
|
||||
Gson gson = new Gson();
|
||||
String plaintext = gson.toJson(requestMessage);
|
||||
System.out.println(plaintext);
|
||||
|
||||
// 生成 AES 密钥
|
||||
String key = aesKey;
|
||||
SecretKey aesKey = new SecretKeySpec(key.getBytes(), "AES");
|
||||
|
||||
// 使用 RSA 公钥加密 AES 密钥
|
||||
String rsaPublicKeyPath = publicKeyPath;
|
||||
PublicKey publicKey = readPublicKey(new File(rsaPublicKeyPath));
|
||||
|
||||
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
||||
// Cipher rsaCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
rsaCipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||
byte[] encryptedAesKey = rsaCipher.doFinal(aesKey.getEncoded());
|
||||
|
||||
// 使用 AES-128-CBC 加密数据
|
||||
Cipher aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
byte[] ivBytes = generateRandomIV(aesCipher.getBlockSize());
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
|
||||
|
||||
aesCipher.init(Cipher.ENCRYPT_MODE, aesKey, ivSpec);
|
||||
byte[] encryptedData = aesCipher.doFinal(plaintext.getBytes());
|
||||
|
||||
// 数字签名(使用 RSA 私钥)
|
||||
String rsaPrivateKeyPath = privateKeyPath;
|
||||
PrivateKey privateKey = readPrivateKeyFromFile(rsaPrivateKeyPath);
|
||||
Signature signer = Signature.getInstance("SHA256withRSA");
|
||||
signer.initSign(privateKey);
|
||||
signer.update(encryptedData);
|
||||
byte[] signatureBytes = signer.sign();
|
||||
Base64.Encoder encoder = Base64.getEncoder();
|
||||
|
||||
// 将加密后的数据和签名进行Base64编码
|
||||
String base64EncryptedData = Base64.getEncoder().encodeToString(encryptedData);
|
||||
String base64Signature = Base64.getEncoder().encodeToString(signatureBytes);
|
||||
|
||||
// 输出加密后的数据和签名
|
||||
System.out.println("加密后的数据:" + base64EncryptedData);
|
||||
System.out.println("数字签名:" + base64Signature);
|
||||
|
||||
|
||||
HashMap<String, String> result = new HashMap<String, String>();
|
||||
result.put("merchant_id",merchantId);
|
||||
result.put("nonce", encoder.encodeToString(ivBytes));
|
||||
result.put("message", encoder.encodeToString(encryptedData));
|
||||
result.put("tag", encoder.encodeToString(signatureBytes));
|
||||
|
||||
String mode = "aes-128-cbc";
|
||||
// Padding mode
|
||||
String padding = "pkcs7";
|
||||
// signature alogrithm
|
||||
String signatureAlgorithm = "rsa-sha256";
|
||||
String cipherSuite = mode + "-" + padding + "-with-" + signatureAlgorithm;
|
||||
result.put("cipher_suite", cipherSuite);
|
||||
|
||||
System.out.println("map 格式:" + result);
|
||||
// Encode to json
|
||||
String jsonEncoded = JSONObject.toJSONString(result);
|
||||
System.out.println("json 格式:" + jsonEncoded);
|
||||
|
||||
AlipayHKRequestDTO alipayHKRequestDTO = new AlipayHKRequestDTO();
|
||||
alipayHKRequestDTO.setMerchant_id(merchantId);
|
||||
alipayHKRequestDTO.setNonce(encoder.encodeToString(ivBytes));
|
||||
alipayHKRequestDTO.setMessage(encoder.encodeToString(encryptedData));
|
||||
alipayHKRequestDTO.setTag(encoder.encodeToString(signatureBytes));
|
||||
alipayHKRequestDTO.setCipher_suite(cipherSuite);
|
||||
|
||||
// String info = AlipayHKRequestUtil.createOrder(result);
|
||||
// log.info(info);
|
||||
|
||||
// 解密和验证
|
||||
// byte[] decryptedAesKey = rsaCipher.doFinal(encryptedAesKey);
|
||||
byte[] decryptedAesKey = rsaCipher.doFinal(aesKey.getEncoded());
|
||||
SecretKey decryptedAesSecretKey = new SecretKeySpec(decryptedAesKey, "AES");
|
||||
|
||||
aesCipher.init(Cipher.DECRYPT_MODE, decryptedAesSecretKey, ivSpec);
|
||||
byte[] decryptedData = aesCipher.doFinal(Base64.getDecoder().decode(base64EncryptedData));
|
||||
|
||||
// 验证数字签名
|
||||
Signature verifier = Signature.getInstance("SHA256withRSA");
|
||||
verifier.initVerify(publicKey);
|
||||
verifier.update(decryptedData);
|
||||
boolean signatureValid = verifier.verify(Base64.getDecoder().decode(base64Signature));
|
||||
|
||||
System.out.println("解密后的数据:" + new String(decryptedData));
|
||||
System.out.println("数字签名验证结果:" + signatureValid);
|
||||
}
|
||||
|
||||
public static PublicKey readPublicKey(File file) throws Exception {
|
||||
KeyFactory factory = KeyFactory.getInstance("RSA");
|
||||
|
||||
try (FileReader keyReader = new FileReader(file);
|
||||
PemReader pemReader = new PemReader(keyReader)) {
|
||||
|
||||
PemObject pemObject = pemReader.readPemObject();
|
||||
byte[] content = pemObject.getContent();
|
||||
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content);
|
||||
return factory.generatePublic(pubKeySpec);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] generateRandomIV(int blockSize) {
|
||||
SecureRandom random = new SecureRandom();
|
||||
byte[] iv = new byte[blockSize];
|
||||
random.nextBytes(iv);
|
||||
return iv;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.ai.da.common.utils;
|
||||
|
||||
import com.ai.da.common.config.exception.BusinessException;
|
||||
import com.ai.da.model.dto.AlipayHKRequestDTO;
|
||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
@Slf4j
|
||||
public class AlipayHKRequestUtil {
|
||||
|
||||
public static String createOrder(AlipayHKRequestDTO alipayHKRequestDTO) throws IOException {
|
||||
OkHttpClient client = new OkHttpClient().newBuilder()
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.pingInterval(5, TimeUnit.SECONDS)//websocket轮训间隔(单位:秒)
|
||||
.readTimeout(60, TimeUnit.SECONDS)//读取超时(单位:秒)
|
||||
.writeTimeout(60, TimeUnit.SECONDS)//写入超时(单位:秒)
|
||||
.build();
|
||||
MediaType mediaType = MediaType.parse("application/json;charset=utf-8");
|
||||
long epochMilli = Instant.now().toEpochMilli();
|
||||
log.info("Alipay-HK send request unix timestamp: {}", epochMilli);
|
||||
|
||||
String jsonString = JSONObject.toJSONString(alipayHKRequestDTO, SerializerFeature.WriteMapNullValue);
|
||||
log.info(jsonString);
|
||||
|
||||
RequestBody body = RequestBody.create(mediaType, jsonString);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
// .url("https://aqs-api.sandbox-codpayment.com")
|
||||
.url("https://aqs-api.sandbox-codpayment.com/v1/service")
|
||||
.method("POST", body)
|
||||
.addHeader("Content-Type", "application/json;charset=utf-8")
|
||||
.build();
|
||||
Response response = null;
|
||||
String bodyString;
|
||||
try {
|
||||
// log.info("generateSketchOrPrint请求入参content###{}", JSON.toJSONString(alipayHKRequestDTO, SerializerFeature.WriteMapNullValue));
|
||||
response = client.newCall(request).execute();
|
||||
} catch (Exception e) {
|
||||
// log.error("PythonService##generateSketchOrPrint异常###{}", ExceptionUtil.getThrowableList(ioException));
|
||||
// throw new BusinessException("generate.interface.error");
|
||||
throw new BusinessException(e.getMessage());
|
||||
}
|
||||
return response.body().string();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -308,4 +308,46 @@ public class SendEmailUtil {
|
||||
throw new BusinessException("failed.to.send.mail");
|
||||
}
|
||||
}
|
||||
|
||||
private final static Long GENERATE_EXCEPTION_WARNING_ID = 122589L;
|
||||
public static void sendGenerateExceptionWarning(String message){
|
||||
try {
|
||||
// 实例化一个认证对象
|
||||
Credential cred = new Credential(SECRET_ID, SECRET_KEy);
|
||||
HttpProfile httpProfile = new HttpProfile();
|
||||
httpProfile.setEndpoint("ses.tencentcloudapi.com");
|
||||
ClientProfile clientProfile = new ClientProfile();
|
||||
clientProfile.setHttpProfile(httpProfile);
|
||||
SesClient client = new SesClient(cred, "ap-hongkong", clientProfile);
|
||||
SendEmailRequest req = new SendEmailRequest();
|
||||
req.setFromEmailAddress(CODE_CREATE_SEND_ADDRESS);
|
||||
req.setDestination(new String[]{"xupei3360@163.com"});
|
||||
|
||||
// 根据邮件类型设置不同的主题和模板
|
||||
String subject = "";
|
||||
Template template = new Template();
|
||||
subject = "Warning: AiDA 3.0 Generate Exception Warning";
|
||||
template.setTemplateID(GENERATE_EXCEPTION_WARNING_ID);
|
||||
|
||||
JSONObject parameter = new JSONObject();
|
||||
parameter.put("errorMessage", message);
|
||||
parameter.put("time", DateUtil.dateToStr(new Date(), DateUtil.YYYY_MM_DD_HH_MM_SS));
|
||||
|
||||
template.setTemplateData(parameter.toJSONString());
|
||||
|
||||
req.setSubject(subject);
|
||||
req.setTemplate(template);
|
||||
|
||||
// 发送邮件
|
||||
SendEmailResponse resp = client.SendEmail(req);
|
||||
log.info("短信发送结果res###{}", SendEmailResponse.toJsonString(resp));
|
||||
} catch (TencentCloudSDKException e) {
|
||||
log.info("邮件发送失败###{}", e.toString());
|
||||
throw new BusinessException("failed.to.send.mail");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user