RSA与AES工具类
1. 需求分析
公司要新增一个dmz网关,网关中已经有源码部分,可以点击查看。此处为整理方便直接查看。具体需求为外部进入数据需要加密解密,使用AES与RSA进行加密解密,具体过程如下
- 使用AES生成随机加密秘钥转换为base64字符串
- 生成网关验签的sign信息(md5[系统标识|业务时间|约定md5随机key])
- 使用AES随机秘钥对请求参数Body进行AES对称加密
- 将AES随机秘钥byte[]转换为16进制字符串
- 将16进制的秘钥串进行RSA非对称加密[提前自己生成需要的私钥和公钥]既signature
2. 工具类
AES工具类
package cn.git.dmz.util;
import cn.git.common.exception.ServiceException;
import cn.git.dmz.constants.DmzConstants;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
/**
* @description: AES加解密工具类
* @program: bank-credit-sy
* @author: lixuchun
* @create: 2024-01-05 08:52:41
*/
@Component
@Slf4j
public class AESUtil {
@Autowired
private LogUtil logUtil;
/**
* 获取随机AES密钥
* @return 返回加密秘钥
*/
public SecretKey getRandomKey() {
// 使用种子生成种子随机秘钥,如果想每一次都生成不同秘钥,则去掉种子即可
SecureRandom secureRandom = new SecureRandom(DmzConstants.DMZ_LOCK_KEY.getBytes(StandardCharsets.UTF_8));
// 实例
KeyGenerator kg = null;
try {
kg = KeyGenerator.getInstance(DmzConstants.AES_TYPE);
// AES
kg.init(DmzConstants.KEY_SIZE, secureRandom);
// 生成密钥
SecretKey secretKey = kg.generateKey();
return secretKey;
} catch (NoSuchAlgorithmException e) {
String errorMsg = logUtil.getStackTraceInfo(e, DmzConstants.NUM_2000);
log.error(errorMsg);
throw new ServiceException("获取随机AES密钥失败");
}
}
/**
* 加密
*
* @param content 内容
* @param randomPassKey 秘钥
* @return 加密后的数据
*/
public String aesEncrypt(String content, String randomPassKey) {
try {
// 新建Cipher 类
Cipher cipher = Cipher.getInstance(DmzConstants.AES_MODE);
// 初始化秘钥
SecretKeySpec sks = new SecretKeySpec(Base64.getDecoder().decode(randomPassKey), DmzConstants.AES_TYPE);
// 初始化加密类
cipher.init(Cipher.ENCRYPT_MODE, sks);
// 进行加密
byte[] encrypt = cipher.doFinal(content.getBytes());
// 进行base64编码
encrypt = Base64.getEncoder().encode(encrypt);
// 转成字符串返回
return new String(encrypt, StandardCharsets.UTF_8);
} catch (Exception e) {
String errorMsg = logUtil.getStackTraceInfo(e, DmzConstants.NUM_2000);
log.error(errorMsg);
throw new ServiceException("AES加密失败");
}
}
/**
* AES进行解密数据
*
* @param content 解密内容
* @param randomPassKey aes随机秘钥
* @return 解密后数据
*/
public String aesDecrypt(String content, String randomPassKey) {
try {
// base64里会携带换行符导致解码失败,替换base64里的换行
content = content.replaceAll("[\\n\\r]", StrUtil.EMPTY);
// base64 解码,跟上面的编码对称
byte[] data = Base64.getDecoder().decode(content.getBytes(StandardCharsets.UTF_8));
// 新建Cipher 类
Cipher cipher = Cipher.getInstance(DmzConstants.AES_MODE);
// 初始化秘钥
SecretKeySpec keySpec = new SecretKeySpec(Base64.getDecoder().decode(randomPassKey), DmzConstants.AES_TYPE);
// 初始化类
cipher.init(Cipher.DECRYPT_MODE, keySpec);
// 进行AES
byte[] result = cipher.doFinal(data);
// 返回解密后内容信息
return new String(result);
} catch (Exception e) {
String errorMsg = logUtil.getStackTraceInfo(e, DmzConstants.NUM_2000);
log.error(errorMsg);
throw new ServiceException("AES解密失败");
}
}
/**
* 将byte数组转换成16进制String
* @param bytes 转换的byte数组
* @return 16进制字符串
*/
public String parseBytesToHexStr(byte[] bytes) {
StringBuilder stringBuilder = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
stringBuilder.append(hex.toUpperCase());
}
return stringBuilder.toString();
}
/**
* 将16进制String转换为byte数组
* @param hexStr 16进制字符串
* @return 2进制byte数组
*/
public byte[] parseHexStrToBytes(String hexStr) {
// 空值判断
if (hexStr.length() < 1) {
return null;
}
byte[] result = new byte[hexStr.length() / 2];
for (int i = 0; i < hexStr.length() / DmzConstants.NUM_2; i++) {
int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
}
RSA工具类
package cn.git.dmz.util;
import cn.git.common.exception.ServiceException;
import cn.git.dmz.constants.DmzConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
/**
* @description: RSA加解密工具类
* @program: bank-credit-sy
* @author: lixuchun
* @create: 2024-01-05 08:58:58
*/
@Component
@Slf4j
public class RSAUtil {
@Autowired
private LogUtil logUtil;
/**
* RSA初始化key pair,初始化公钥私钥方法
* @return KeyPair
*/
public KeyPair getRSAKeyPair() {
try {
// 生成RSA密钥对
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(DmzConstants.RSA_TYPE);
// 根据种子生成秘钥对
SecureRandom secureRandom = new SecureRandom(DmzConstants.DMZ_LOCK_KEY.getBytes(StandardCharsets.UTF_8));
keyGen.initialize(DmzConstants.NUM_1024, secureRandom);
return keyGen.generateKeyPair();
} catch (Exception e) {
String errorMsg = logUtil.getStackTraceInfo(e, DmzConstants.NUM_2000);
log.error(errorMsg);
throw new ServiceException("RSA初始化keyPairs失败");
}
}
/**
* RSA加密
* @param data 待加密数据
* @param publicKeyStr 公钥
* @return 加密后的数据
*/
public String encryptRSA(String data, String publicKeyStr) {
try {
byte[] keyBytes = Base64.getDecoder().decode(publicKeyStr);
// 初始化公钥key
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(DmzConstants.RSA_TYPE);
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
// 使用公钥进行加密
Cipher encryptCipher = Cipher.getInstance(DmzConstants.RSA_MODE);
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = encryptCipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedBytes);
} catch (Exception e) {
String errorMsg = logUtil.getStackTraceInfo(e, DmzConstants.NUM_2000);
log.error(errorMsg);
throw new ServiceException("RSA加密失败");
}
}
/**
* 进行RSA解密
* @param content 解密文本
* @param privateKeyStr 私钥字符串
* @return
*/
public String decryptRSA(String content, String privateKeyStr) {
try {
byte[] keyBytes = Base64.getDecoder().decode(privateKeyStr);
// 初始化私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(DmzConstants.RSA_TYPE);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 使用私钥进行解密
Cipher decryptCipher = Cipher.getInstance(DmzConstants.RSA_MODE);
decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedBytes = decryptCipher.doFinal(Base64.getDecoder().decode(content));
return new String(decryptedBytes, StandardCharsets.UTF_8);
} catch (Exception e) {
String errorMsg = logUtil.getStackTraceInfo(e, DmzConstants.NUM_2000);
log.error(errorMsg);
throw new ServiceException("RSA解密失败");
}
}
}
3. 本人测试类
测试类则是没有封装成util的方法,进行需求进行简单开发的逻辑代码,可以直接运行,注意如果要自己使用,修改自己的秘钥。
package cn.git.demo.contract;
import cn.git.common.constant.CommonConstant;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSONObject;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* a11111密码初始化类
* 密码初始化
*/
public class TestEncodePassword {
/**
* 外部系统请求网关加密密匙
*/
public static final String DMZ_LOCK_KEY = "DMZ:XdXt:loan==,";
/**
* AES
* 算法名称/加密模式/数据填充方式
*/
public static String MODE = "AES/ECB/PKCS5Padding";
/**
* RSA
* 算法名称/加密模式/数据填充方式
*/
private final static String RSA_MODE = "RSA/ECB/PKCS1Padding";
/**
* 加密模式
*/
public static String KEY_ALGORITHM = "AES";
/**
* 加密单位长度
*/
private static final int KEY_SIZE = 128;
/**
* RSA公钥
*/
private static final String RSA_PUB_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCMZ/Q5CIvZjBTLD4sTwxbWYNNZWd7rImD+sAgwOAf+nH1vWN+Qk63iBGQYHzYK5EhSAFqsqX04lsTiJUH42YRJ9yIXUnvd6Osp2JYOXW8WTGgMtfDJp5WzbYzJRxQ0RmJNcfqMcHPVepgpjs2HNj59aTqNPLdiouRSRlh2E0umGwIDAQAB";
/**
* RSA私钥
*/
private static final String RSA_PRI_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIxn9DkIi9mMFMsPixPDFtZg01lZ3usiYP6wCDA4B/6cfW9Y35CTreIEZBgfNgrkSFIAWqypfTiWxOIlQfjZhEn3IhdSe93o6ynYlg5dbxZMaAy18MmnlbNtjMlHFDRGYk1x+oxwc9V6mCmOzYc2Pn1pOo08t2Ki5FJGWHYTS6YbAgMBAAECgYEAiQRNNXccmsDz7bGOZEumtrAor/Je8xFKnGCGrR+Q1aw7UHTnPvyO3JiyYUPcBkb+OF+2HPcNhzLCkXoQZltGlznwOwGvHl4qEheVAMwdgijuYQZpsiGQyVyr4C506ydoPjPXbWD+9GGLuakHtIlRP9FAGvwQe/5fkUYsiAJD8mkCQQDop8hxDCrsH9eBQ7PusaGjmW213zmX0O5yAntwuznX3zsQOo+AgeM00ottF4J5BXWCeyF5ZxKi6WoPjSgTw77fAkEAmn6QNwMSEbcWHbuaC3ofjcYhnOl47aQWNr+56G+Wc7vs4xs8PXdd3sVmlepOFSdJLWCxguUBO60Dg6cpnAFMRQJABZLPaHXkKVfx77TRgKxctPCeAjdgx9RHgg+xKVgy4IsGfTMJ8Qgriz5n/KsNgxywXfnZKXFgrupskgbNqPuNfQJAQlOjxnpi/4gCzrED6Xl8onk1ZRA3Ao83mjmlrsx5YyaDBN1kd18PxdwptqLo8tvy5rBkhTWb2erlX1gc3QURoQJABakHaa+GNmKyc7pasJHQ3HMR39k888TQPKEQiE461oc5//Cyqek7u91rCG0HR7O4Hk+ostH9lkxc4bG+HYgWgg==";
public static void main(String[] args) throws Exception {
testAES();
}
/**
* AES加解密,RES加密
* 1.生成需要发送的交易请求明文报文JSON格式
* 2.生成sign签名:MD5(sysid|transtime|md5key),并将生成的sign签名放入明文报文的JSON报文中
* 3.生成AES随机密钥:Byte[] = getRandomKey(随机加密串)
* 4.将随机密钥由二进制转为16进制:byteToHexString(AES随机密钥)
* 5.使用AES随机密钥对报文明文进行加密:message=aesEncrypt(报文明文)
* 6.使用RSA公钥对随机密钥进行加密,signature=BASE64.encode(RSAPublicEnc(16进制AES密钥)) 此时,将计算得出的message字段以及signature字段以post的方式发送至数字信贷平台。
*/
private static void testAES() throws Exception {
// 生成测试数据
String username = "331326";
String passowrd = "a11111";
List<String> userList = new ArrayList<>();
userList.add("赵六6.使用RSA公钥对随机密钥进行加密,signature=BASE64.encode(RSAPublicEnc(16进制AES密钥)) 此时,将计算得出的message字段以及signature字段以post的方式发送至数字信贷平台。");
userList.add("赵76.使用RSA公钥对随机密钥进行加密,signature=BASE64.encode(RSAPublicEnc(16进制AES密钥)) 此时,将计算得出的message字段以及signature字段以post的方式发送至数字信贷平台。");
userList.add("赵86.使用RSA公钥对随机密钥进行加密,signature=BASE64.encode(RSAPublicEnc(16进制AES密钥)) 此时,将计算得出的message字段以及signature字段以post的方式发送至数字信贷平台。");
// 测试数据生成需要发送的交易请求明文报文JSON格式
JSONObject jsonObject = new JSONObject();
jsonObject.put("username", username);
jsonObject.put("password", passowrd);
jsonObject.put("userList", userList);
String jsonMessage = jsonObject.toJSONString();
// 获取随机加密私钥key
Key privateRandomAESKey = getRandomKey();
// 随机加密串
String privateRandomAESKeyStr = Base64.getEncoder().encodeToString(privateRandomAESKey.getEncoded());
System.out.println("AES随机密钥 -> : " + privateRandomAESKeyStr);
// 将随机密钥由二进制转为16进制:byteToHexString(AES随机密钥)
String hexPassKey = parseByte2HexStr(privateRandomAESKey.getEncoded());
System.out.println("AES十六进制hexPassKey -> : " + hexPassKey);
// 获取RSA公钥和私钥
KeyPair keyPair = generatorKey();
String aesPublicKey = RSA_PUB_KEY;
String aesPrivateKey = RSA_PRI_KEY;
if (StrUtil.isBlank(aesPublicKey)) {
aesPublicKey = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
}
if (StrUtil.isBlank(aesPrivateKey)) {
aesPrivateKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
}
System.out.println("RSA公钥为 -> : " + aesPublicKey);
System.out.println("RSA私钥为 -> : " + aesPrivateKey);
// RSA对16进制key进行加密
String signature = encryptRSA(hexPassKey, aesPublicKey);
System.out.println("RSA加密signature为 -> : " + signature);
// 使用随机秘钥AES加密message信息
String encodeMessage = encrypt(jsonMessage, privateRandomAESKeyStr);
System.out.println("AES加密message为 -> : " + encodeMessage);
// 解密signature生成16进制字符串AES秘钥
String decodeHexPassKey = decryptRSA(signature, aesPrivateKey);
System.out.println("RSA解密signature为 -> : " + decodeHexPassKey);
// 解密16进制字符串AES秘钥
byte[] aesRandomKeyBytes = parseHexStr2Byte(decodeHexPassKey);
System.out.println("AES获取秘钥为 -> : " + Base64.getEncoder().encodeToString(aesRandomKeyBytes));
// 使用 signature 解密message数据
String decodeMessage = decrypt(encodeMessage, Base64.getEncoder().encodeToString(aesRandomKeyBytes));
System.out.println("AES解密message为 -> : " + decodeMessage);
}
/**
* 获取密钥
*
* @return
* @throws Exception
*/
private static SecretKey getRandomKey() throws Exception {
// 随机数
SecureRandom secureRandom = new SecureRandom(DMZ_LOCK_KEY.getBytes(StandardCharsets.UTF_8));
// 实例
KeyGenerator kg = KeyGenerator.getInstance("AES");
// AES
kg.init(KEY_SIZE, secureRandom);
// 生成密钥
SecretKey secretKey = kg.generateKey();
return secretKey;
}
/**
* 将byte数组转换成16进制String
* @param buf
* @return
*/
public static String parseByte2HexStr(byte[] buf) {
StringBuffer sb = new StringBuffer();
for (byte b : buf) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
/**
* 将16进制String转换为byte数组
* @param hexStr
* @return
*/
public static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1)
return null;
byte[] result = new byte[hexStr.length() / 2];
for (int i = 0; i < hexStr.length() / 2; i++) {
int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
result[i] = (byte) (high * 16 + low);
}
return result;
}
/**
* 加密
*
* @param content 内容
* @param randomPassKey 秘钥
* @return 加密后的数据
*/
public static String encrypt(String content, String randomPassKey) throws Exception {
// 新建Cipher 类
Cipher cipher = Cipher.getInstance(MODE);
// 初始化秘钥
SecretKeySpec sks = new SecretKeySpec(Base64.getDecoder().decode(randomPassKey), KEY_ALGORITHM);
// 初始化加密类
cipher.init(Cipher.ENCRYPT_MODE, sks);
// 进行加密
byte[] encrypt = cipher.doFinal(content.getBytes());
// 这一步非必须,是因为二进制数组不方便传输,所以加密的时候才进行base64编码
encrypt = Base64.getEncoder().encode(encrypt);
// 转成字符串返回
return new String(encrypt, StandardCharsets.UTF_8);
}
/**
* 解密数据
*
* @param content 内容
* @param randomPassKey 秘钥
* @return 数据
*/
public static String decrypt(String content, String randomPassKey) throws Exception{
// 替换base64里的换行,这一步也非必须,只是有些情况base64里会携带换行符导致解码失败
content = content.replaceAll("[\\n\\r]", "");
// base64 解码,跟上面的编码对称
byte[] data = Base64.getDecoder().decode(content.getBytes(StandardCharsets.UTF_8));
// 新建Cipher 类
Cipher cipher = Cipher.getInstance(MODE);
// 初始化秘钥
SecretKeySpec keySpec = new SecretKeySpec(Base64.getDecoder().decode(randomPassKey), KEY_ALGORITHM);
// 初始化类
cipher.init(Cipher.DECRYPT_MODE, keySpec);
// 解密
byte[] result = cipher.doFinal(data);
// 返回解密后的内容
return new String(result);
}
/**
* RSA初始化key pair
* @return KeyPair
*/
private static KeyPair generatorKey() throws NoSuchAlgorithmException {
// 生成RSA密钥对
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
SecureRandom secureRandom = new SecureRandom(DMZ_LOCK_KEY.getBytes(StandardCharsets.UTF_8));
keyGen.initialize(1024, secureRandom);
KeyPair pair = keyGen.generateKeyPair();
return pair;
}
/**
* RSA加密
* @param data 待加密数据
* @param publicKeyStr 公钥
* @return 加密后的数据
*/
private static String encryptRSA(String data, String publicKeyStr) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException {
byte[] keyBytes = Base64.getDecoder().decode(publicKeyStr);
// 初始化公钥key
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
// 使用公钥进行加密
Cipher encryptCipher = Cipher.getInstance(RSA_MODE);
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = encryptCipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedBytes);
}
/**
* RSA解密
* @param data
* @param privateKeyStr
* @return
*/
private static String decryptRSA(String data, String privateKeyStr) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException {
byte[] keyBytes = Base64.getDecoder().decode(privateKeyStr);
// 初始化私钥
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 使用私钥进行解密
Cipher decryptCipher = Cipher.getInstance(RSA_MODE);
decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedBytes = decryptCipher.doFinal(Base64.getDecoder().decode(data));
return new String(decryptedBytes, "UTF-8");
}
}
测试结果展示如下