package com.nova.sankuai.infra.utils;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class RbUtil {
/**
* 密钥长度 于原文长度对应 以及越长速度越慢
*/
private final static int KEY_SIZE = 1024;
private static final int MAX_ENCRYPT_BLOCK = 117;
private static final int MAX_DECRYPT_BLOCK = 128;
private static final String KEY_ALGORITHM = "RSA";
private static final String SIGNATURE_ALGORITHM = "SHA256WithRSA";
private static final Logger log = LoggerFactory.getLogger("capitalLogger");
private static final String CHARSET_NAME = String.valueOf(StandardCharsets.UTF_8);
public static final String AES_CBC_PKCS5PADDING = "AES/CBC/PKCS5PADDING";
/**
* 创建RSA公钥和私钥对
*
* 公钥:RSAUtils.PUBLIC_KEY
* 私钥:RSAUtils.PRIVATE_KEY
*
* @throws NoSuchAlgorithmException 创建异常
*/
private static void newRsaKeys() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGen.initialize(KEY_SIZE);
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
String publicKeyStr = Base64.encodeBase64String(publicKey.getEncoded());
String privateKeyStr = Base64.encodeBase64String(privateKey.getEncoded());
String logNumber = IdUtil.randomUUID();
log.info("日志编号:{}, RSA::PrivateKey {}", logNumber, privateKeyStr);
log.info("日志编号:{}, RSA::PublicKey {}", logNumber, publicKeyStr);
}
/**
* 响应结果
*
* 解密响应数据及验签
*
* @param response 响应数据
* @param sign 签名, 响应数据中的sign字段
* @param key aes密钥, 请求体中的key字段
* @param data 密文业务数据, 响应数据中的data字段
* @param rsaPublicKey rsa公钥
* @param rsaPrivateKey rsa私钥
* @return 解密后的业务数据
*/
public static String decrypt(Object response, String sign, String key, String data, String rsaPublicKey, String rsaPrivateKey) {
// 验签
HashMap map = JSONObject.parseObject(JSON.toJSON(response).toString(), HashMap.class);
String signContent = getSignContent(map);
boolean isSignCorrect = verifySignByPublicKey(signContent, sign, rsaPublicKey);
String logNumber = IdUtil.randomUUID();
log.info("日志编号:{}, decrypt: {}, isSignCorrect: {}", logNumber, signContent, isSignCorrect);
if (!isSignCorrect) {
throw new RuntimeException("验签失败");
}
try {
// 使用私钥解密得到AES秘钥
String aesKey = decryptByPrivateKey(key, rsaPrivateKey);
log.info("日志编号:{}, aesKey: {}", logNumber, aesKey);
// 使用AES秘钥解密params得到业务参数
String decryptStr = aesDecrypt(data, aesKey);
log.info("日志编号:{}, decryptStr: {}", logNumber, decryptStr);
return decryptStr;
} catch (Exception e) {
throw new RuntimeException("解密失败", e);
}
}
/**
* 请求接口
*
* 加密请求参数及加签
*
* @param channel 渠道方名称
* @param t 系统时间
* @param data 加密前的业务数据
* @param rsaPublicKey rsa公钥
* @param rsaPrivateKey rsa私钥
* @return 加密后的请求数据
*/
public static String encrypt(String channel, Long t, String data, String rsaPublicKey, String rsaPrivateKey) {
// 随机字符串 作为 “AES秘钥” (注意AES加解密128/192/256 bits.对应秘钥16/24/32位)
String aesKey = RandomStringUtils.randomAlphanumeric(16);
String params, key;
try {
// 使用AES秘钥加密得到data
params = aesEncrypt(data, aesKey);
// 使用RSA公钥 加密 “AES秘钥” 得到 key
key = rsaEncryptWithPublicKeyToBase64(getPublicKey(rsaPublicKey), aesKey.getBytes(CHARSET_NAME));
} catch (Exception e) {
throw new RuntimeException("加密失败", e);
}
// 组装数据
Map request = new HashMap<>(5, 1F);
request.put("channel", channel);
request.put("t", String.valueOf(t));
request.put("params", params);
request.put("key", key);
// 加签
String signContent = getSignContent(request);
String sign = signByPrivateKey(signContent, rsaPrivateKey);
request.put("sign", sign);
return JSON.toJSONString(request);
}
/**
* 验签 及 解密请求数据
*
* @param request 请求数据
* @param sign 签名, 请求数据中的sign字段
* @param key aes密钥, 请求数据中的key字段
* @param params 密文业务数据, 请求数据中的params字段
* @param rsaPublicKey rsa公钥
* @param rsaPrivateKey rsa私钥
* @return 解密后的业务数据
*/
public static String decryptRequest(Object request, String sign, String key, String params, String rsaPublicKey, String rsaPrivateKey) {
// 验签
String signContent = getSignContent(JSON.parseObject(JSON.toJSONString(request), new TypeReference