一:RSA非对称加密
RSA是一种常用的非对称加密算法,加密和加密使用不同的密钥,常用于要求安全性较高的加密场景,比如接口的验签和接口数据的加密与解密。与非对称加密算法对比,其安全性较高,但是加密性能却比较低,不适合高并发场景,一般只加密少量的数据。
二:AES对称加密
AES是一种最常见的对称加密算法(微信小程序加密传输就是用这个加密算法的),加密和解密使用的是相同的密钥。其加密性能好,加密解密速度非常快,内存需求低,适用于经常发送数据的场合。
三:RSA+AES实现接口验签和请求参数的加密与解密
我们经常需要在我们自己开发的系统上,开发一些接口供第三方调用,那么这个时候,对我们接口的安全性要求就比较高了,尤其是那种需要传输比较私密的信息的时候,其对安全性的要求就更高了。
实现思路
调用方:
- 使用AES对称加密算法对业务请求参数进行加密后传输
- 使用RSA非对称加密算法对AES的密钥进行公钥加密后传输
- 使用RSA的私钥对请求参数进行签名
接收方:
- 获取到请求参数后,对参数进行验签和业务参数的解密
问题:为什么要对AES的密钥进行RSA公钥加密后传输?
AES是对称加密算法,加密和解密的密钥都是同一个,为了防止被别人恶意获取到该密钥,然后对我们的业务请求参数进行解密,我们需要将AES密钥进行非对称加密后再进行传输。
四:代码实现
pom依赖
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.13</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<!--Jackson依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<!--Jackson依赖-->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.8.3</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
AESUtils实体类:
package aes;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
/**
* @author lizhangyu
* @date 2021/5/4 0:10
*/
public class AESUtils {
/**
* 编码格式
*/
public static final String CHARSET = "UTF-8";
/**
* 加密算法
*/
public static final String AES_ALGORITHM = "AES";
/**
* 生成密钥
* @return
*/
public static String generateKey() {
try {
//1.构造密钥生成器,指定为AES算法,不区分大小写
KeyGenerator keygen = KeyGenerator.getInstance(AES_ALGORITHM);
//2.生成一个128位的随机源
keygen.init(128, new SecureRandom());
//3.产生原始对称密钥
SecretKey secretKey = keygen.generateKey();
//4.获得原始对称密钥的字节数组
byte[] byteKey = secretKey.getEncoded();
//5.返回密钥
return Hex.encodeHexString(byteKey);
}catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
//如果有错就返加nulll
return null;
}
/**
* 加密
* @param thisKey 密钥
* @param data 数据
* @return 加密后的数据
*/
public static String encode(String thisKey, String data) {
try {
//1.转换KEY
Key key = new SecretKeySpec(Hex.decodeHex(thisKey), AES_ALGORITHM);
//2.根据指定算法AES自成密码器
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
//3.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(Cipher.ENCRYPT_MODE, key);
//4.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
byte[] byte_encode = data.getBytes(CHARSET);
//5.根据密码器的初始化方式--加密:将数据加密
byte[] result = cipher.doFinal(byte_encode);
//6.将字符串返回
return Hex.encodeHexString(result);
}catch (DecoderException e) {
e.printStackTrace();
}catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//如果有错就返加nulll
return null;
}
/**
* 解密
* @param thisKey 密钥
* @param data 加密的数据
* @return 解密后的数据
*/
public static String decode(String thisKey, String data) {
try {
//1.转换KEY
Key key = new SecretKeySpec(Hex.decodeHex(thisKey), AES_ALGORITHM);
//2.根据指定算法AES自成密码器
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
//3.初始化密码器,第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作,第二个参数为使用的KEY
cipher.init(Cipher.DECRYPT_MODE, key);
//4.将加密并编码后的内容解码成字节数组
byte[] byte_content = Hex.decodeHex(data);
//5.解密
byte[] byte_decode = cipher.doFinal(byte_content);
//6.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
String result = new String(byte_decode, CHARSET);
return result;
}catch (DecoderException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
//如果有错就返加nulll
return null;
}
public static void main(String[] args) {
String key = generateKey();
System.out.println("生成的密钥为:" + key);
// String pubKey = "6aa7066f738828f333b1bb84b62606c6";
// String priKey = "6aa7066f738828f333b1bb84b62606c6";
String data = "使用AES对称加密,请输入加密的规则使用AES对称加密AAAAAABBBBB";
String encodeData = encode(key, data);
System.out.println("加密后的数据为:" + encodeData);
String decodeData = decode(key, encodeData);
System.out.println("解密后的数据为:" + decodeData);
}
}
RSAUtils实体类:
package rsa;
import com.alibaba.fastjson.JSON;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import javax.crypto.Cipher;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Method;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
/**
* @author lizhangyu
* @date 2021/5/3 21:07
*/
public class RSAUtils {
/**
* 编码格式
*/
public static final String CHARSET = "UTF-8";
/**
* 加密算法
*/
public static final String RSA_ALGORITHM = "RSA";
/**
* 定义签名算法
*/
private final static String KEY_RSA_SIGNATURE = "MD5withRSA";
/**
* 公钥key
*/
private static final String PUBLIC_KEY = "publicKey";
/**
* 私钥KEY
*/
private static final String PRIVATE_KEY = "privateKey";
/**
* 生成公钥和私钥
* @param keySize
* @return
*/
public static Map<String, String> createKeys(int keySize){
//为RSA算法创建一个KeyPairGenerator对象
KeyPairGenerator kpg;
try{
kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM);
}catch(NoSuchAlgorithmException e){
throw new IllegalArgumentException("No such algorithm-->[" + RSA_ALGORITHM + "]");
}
//初始化KeyPairGenerator对象,密钥长度
kpg.initialize(keySize);
//生成密匙对
KeyPair keyPair = kpg.generateKeyPair();
//得到公钥
Key publicKey = keyPair.getPublic();
String publicKeyStr = Base64.encodeBase64URLSafeString(publicKey.getEncoded());
//得到私钥
Key privateKey = keyPair.getPrivate();
String privateKeyStr = Base64.encodeBase64URLSafeString(privateKey.getEncoded());
Map<String, String> keyPairMap = new HashMap<String, String>();
keyPairMap.put("publicKey", publicKeyStr);
keyPairMap.put("privateKey", privateKeyStr);
return keyPairMap;
}
/**
* 得到公钥
* @param publicKey 密钥字符串(经过base64编码)
* @throws Exception
*/
public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
//通过X509编码的Key指令获得公钥对象
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
return key;
}
/**
* 得到私钥
* @param privateKey 密钥字符串(经过base64编码)
* @throws Exception
*/
public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
//通过PKCS#8编码的Key指令获得私钥对象
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
return key;
}
/**
* 公钥加密
* @param data
* @param publicKey
* @return
*/
public static String publicEncrypt(String data, RSAPublicKey publicKey){
try{
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus().bitLength()));
}catch(Exception e){
throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
}
}
/**
* 私钥解密
* @param data
* @param privateKey
* @return
*/
public static String privateDecrypt(String data, RSAPrivateKey privateKey){
try{
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), privateKey.getModulus().bitLength()), CHARSET);
}catch(Exception e){
throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
}
}
// /**
// * 私钥加密
// * @param data
// * @param privateKey
// * @return
// */
// public static String privateEncrypt(String data, RSAPrivateKey privateKey){
// try{
// Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
// cipher.init(Cipher.ENCRYPT_MODE, privateKey);
// return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), privateKey.getModulus().bitLength()));
// }catch(Exception e){
// throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
// }
// }
//
// /**
// * 公钥解密
// * @param data
// * @param publicKey
// * @return
// */
// public static String publicDecrypt(String data, RSAPublicKey publicKey){
// try{
// Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
// cipher.init(Cipher.DECRYPT_MODE, publicKey);
// return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), publicKey.getModulus().bitLength()), CHARSET);
// }catch(Exception e){
// throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
// }
// }
/**
* 分段加解密
* @param cipher
* @param opmode
* @param datas
* @param keySize
* @return
*/
private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize){
int maxBlock = 0;
if(opmode == Cipher.DECRYPT_MODE){
maxBlock = keySize / 8;
}else{
maxBlock = keySize / 8 - 11;
}
ByteArrayOutputStream
out = new ByteArrayOutputStream();
int offSet = 0;
byte[] buff;
int i = 0;
try{
while(datas.length > offSet){
if(datas.length-offSet > maxBlock){
buff = cipher.doFinal(datas, offSet, maxBlock);
}else{
buff = cipher.doFinal(datas, offSet, datas.length-offSet);
}
out.write(buff, 0, buff.length);
i++;
offSet = i * maxBlock;
}
}catch(Exception e){
throw new RuntimeException("加解密阀值为["+maxBlock+"]的数据时发生异常", e);
}
byte[] resultDatas = out.toByteArray();
IOUtils.closeQuietly(out);
return resultDatas;
}
/**
* 用私钥对信息生成数字签名
*
* @param data 加密数据
* @param privateKey 私钥
*/
public static String sign(byte[] data, String privateKey) {
String str = "";
try {
// 解密由base64编码的私钥
byte[] bytes = decryptBase64(privateKey);
// 构造PKCS8EncodedKeySpec对象
PKCS8EncodedKeySpec pkcs = new PKCS8EncodedKeySpec(bytes);
// 指定的加密算法
KeyFactory factory = KeyFactory.getInstance(RSA_ALGORITHM);
// 取私钥对象
PrivateKey key = factory.generatePrivate(pkcs);
// 用私钥对信息生成数字签名
Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);
signature.initSign(key);
signature.update(data);
str = encryptBase64(signature.sign());
} catch (Exception e) {
e.printStackTrace();
}
return str;
}
/**
* 校验数字签名
*
* @param data 加密数据
* @param publicKey 公钥
* @param sign 数字签名
* @return 校验成功返回true,失败返回false
*/
public static boolean verify(byte[] data, String publicKey, String sign) {
boolean flag = false;
try {
// 解密由base64编码的公钥
byte[] bytes = decryptBase64(publicKey);
// 构造X509EncodedKeySpec对象
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
// 指定的加密算法
KeyFactory factory = KeyFactory.getInstance(RSA_ALGORITHM);
// 取公钥对象
PublicKey key = factory.generatePublic(keySpec);
// 用公钥验证数字签名
Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);
signature.initVerify(key);
signature.update(data);
flag = signature.verify(decryptBase64(sign));
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
/**
* BASE64 解密
* @param key 需要解密的字符串
* @return 字节数组
*/
public static byte[] decryptBase64(String key) throws Exception {
return Base64.decodeBase64(key);
}
/**
* BASE64 加密
* @param key 需要加密的字节数组
* @return 字符串
*/
public static String encryptBase64(byte[] key) throws Exception {
return new String(Base64.encodeBase64(key));
}
/**
* bean转map
* @param obj
* @return
*/
public static Map<String, Object> beanToMap(Object obj) {
if (obj == null) {
return null;
}
Map<String, Object> map = new HashMap<>();
try {
BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor property : propertyDescriptors) {
String key = property.getName();
// 过滤class属性
if (!key.equals("class")) {
// 得到property对应的getter方法
Method getter = property.getReadMethod();
Object value = getter.invoke(obj);
if (value == null) {
continue;
}
map.put(key, value);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
/**
* 按照红黑树(Red-Black tree)的 NavigableMap 实现
* 按照字母大小排序
* @param map
* @return
*/
public static Map<String, Object> sort(Map<String, Object> map) {
if (map == null) {
return null;
}
Map<String, Object> result = new TreeMap<>((Comparator<String>) (o1, o2) -> {
return o1.compareTo(o2);
});
result.putAll(map);
return result;
}
/**
* 组合参数
* @param map
* @return 如:key1Value1Key2Value2....
*/
public static String groupStringParam(Map<String, Object> map) {
if (map == null) {
return null;
}
StringBuffer sb = new StringBuffer();
for (Map.Entry<String, Object> item : map.entrySet()) {
if (item.getValue() != null) {
sb.append(item.getKey());
if (item.getValue() instanceof List) {
sb.append(JSON.toJSONString(item.getValue()));
} else {
sb.append(item.getValue());
}
}
}
return sb.toString();
}
public static void main (String[] args) throws Exception {
Map<String, String> keyMap = RSAUtils.createKeys(1024);
String publicKey = keyMap.get(PUBLIC_KEY);
String privateKey = keyMap.get(PRIVATE_KEY);
System.out.println("公钥: \n\r" + publicKey);
System.out.println("私钥: \n\r" + privateKey);
System.out.println("公钥加密——私钥解密");
String str = "code_cayden";
System.out.println("\r明文:\r\n" + str);
System.out.println("\r明文大小:\r\n" + str.getBytes().length);
String encodedData = RSAUtils.publicEncrypt(str, RSAUtils.getPublicKey(publicKey));
System.out.println("密文:\r\n" + encodedData);
String decodedData = RSAUtils.privateDecrypt(encodedData, RSAUtils.getPrivateKey(privateKey));
System.out.println("解密后文字: \r\n" + decodedData);
}
}
JsonRequest实体类:
package aes_rsa;
/**
* @author lizhangyu
* @date 2021/5/4 14:48
*/
public class JsonRequest {
/**
* 接口id (可空)
*/
private String serviceId;
/**
* 请求唯一id (非空)
*/
private String requestId;
/**
* 商户id (非空)
*/
private String appId;
/**
* 参数签名 (非空)
*/
private String sign;
/**
* 对称加密key (非空)
*/
private String aseKey;
/**
* 时间戳,精确到毫秒 (非空)
*/
private long timestamp;
/**
* 请求的业务参数(AES加密后传入,可空)
*/
private String body;
public String getServiceId() {
return serviceId;
}
public void setServiceId(String serviceId) {
this.serviceId = serviceId;
}
public String getRequestId() {
return requestId;
}
public void setRequestId(String requestId) {
this.requestId = requestId;
}
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getAseKey() {
return aseKey;
}
public void setAseKey(String aseKey) {
this.aseKey = aseKey;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
- serviceId:服务id(接口id)。接口设计分为两种,一种是所有的调用方针对类似的业务,都调用的是同一接口地址,然后内部系统根据serviceId去判断具体是要调用哪个业务方法;另一种是针对不同的调用方,开发不同的接口,接口地址也是不一样,那么这个时候就可以不要serviceId这个字段。本章是使用第二种方式,所以serviceId可以不要(可空)。
- requestId:请求唯一id。方便查询定位某个请求和防止同个请求多次调用。
- appId:商户id,即我们会给调用方分配一个这样的id,并且将这个id与调用方的信息进行关联,比如“通过appId查询出调用方的加密密钥等”
- aseKey:是AES对称加密的密钥。用于解密业务请求参数。这里要先用RSA公钥对aseKey进行加密后传输。
- timestamp:请求时间戳。可以用该字段,对请求的时间合法性进行校验。(如:过滤掉请求时间不在当前时间的正负10分钟范围内的请求)
- body:请求的业务参数。对请求的业务参数AES加密后再赋值。
JacksonUtil实体类:
package aes_rsa;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.StringUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @author lizhangyu
* @date 2021/5/4 15:05
*/
public class JacksonUtil {
private static ObjectMapper objectMapper = new ObjectMapper();
/**
* 对象转换成json
*
* @param obj
* @param <T>
* @return
*/
public static <T> String beanToJson(T obj) {
if (obj == null) {
return null;
}
try {
return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 将JSON字符串根据指定的Class反序列化成Java对象。
*
* @param json JSON字符串
* @param pojoClass Java对象Class
* @return 反序列化生成的Java对象
* @throws Exception 如果反序列化过程中发生错误,将抛出异常
*/
public static Object decode(String json, Class<?> pojoClass)
throws Exception {
try {
return objectMapper.readValue(json, pojoClass);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 将JSON字符串根据指定的Class反序列化成Java对象。
*
* @param json JSON字符串
* @param reference 类型引用
* @return 反序列化生成的Java对象
* @throws Exception 如果反序列化过程中发生错误,将抛出异常
*/
public static Object decode(String json, TypeReference<?> reference) throws Exception {
try {
return objectMapper.readValue(json, reference.getClass());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 将Java对象序列化成JSON字符串。
*
* @param obj 待序列化生成JSON字符串的Java对象
* @return JSON字符串
* @throws Exception 如果序列化过程中发生错误,将抛出异常
*/
public static String encode(Object obj) throws Exception {
try {
return objectMapper.writeValueAsString(obj);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 对象转换成格式化的json
* @param obj
* @param <T>
* @return
*/
public static <T> String beanToJsonPretty(T obj) {
if (obj == null) {
return null;
}
try {
return obj instanceof String ? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 将json转换成对象Class
*
* @param str
* @param clazz
* @param <T>
* @return
*/
public static <T> T jsonToBean(String str, Class<T> clazz) {
if (StringUtils.isEmpty(str) || clazz == null) {
return null;
}
try {
return clazz.equals(String.class) ? (T) str : objectMapper.readValue(str, clazz);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 将json转换为对象集合
* @param str
* @param clazz
* @param <T>
* @return
*/
public static <T> List<T> jsonToBeanList(String str, Class<T> clazz) {
if (StringUtils.isEmpty(str) || clazz == null) {
return null;
}
JavaType javaType = getCollectionType(ArrayList.class, clazz);
try {
return objectMapper.readValue(str, javaType);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 获取集合的类型
* @param collectionClass
* @param elementClasses
* @return
*/
public static JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
return objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
}
}
HexUtils实体类:
package aes_rsa;
/**
* @author lizhangyu
* @date 2021/5/4 17:13
*/
public class HexUtils {
/**
* 将Hex String转换为Byte数组
*
* @param hexString
* @return
*/
public static byte[] hexStringToBytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}
/**
* @brief char to byte
*/
public static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
}
RequestTest实体类:
package aes_rsa;
import aes.AESUtils;
import org.apache.commons.lang.StringUtils;
import rsa.RSAUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* @author lizhangyu
* @date 2021/5/4 15:17
* @description: 测试
*/
public class RequestTest {
/**
* 公钥key
*/
private static final String PUBLIC_KEY = "publicKey";
/**
* 私钥KEY
*/
private static final String PRIVATE_KEY = "privateKey";
public static void main(String[] args) throws Exception{
/****先给调用方分配一组RSA密钥和一个appId****/
//初始化RSA密钥
Map<String, String> keyMap = RSAUtils.createKeys(1024);
//公钥
String publicKey = keyMap.get(PUBLIC_KEY);
//私钥
String privateKey = keyMap.get(PRIVATE_KEY);
//appId,32位的uuid
String appId = getUUID();
/****先给调用方分配一组RSA密钥和一个appId****/
/*****调用方(请求方)*****/
//业务参数
Map<String,Object> businessParams = new HashMap<>();
businessParams.put("name","Longer");
businessParams.put("job","程序猿");
businessParams.put("hobby","打篮球");
JsonRequest jsonRequest = new JsonRequest();
jsonRequest.setRequestId(getUUID());
jsonRequest.setAppId(appId);
jsonRequest.setTimestamp(System.currentTimeMillis());
//使用AES密钥,并对密钥进行rsa公钥加密
String aseKey = AESUtils.generateKey();
String aseKeyStr = RSAUtils.publicEncrypt(aseKey, RSAUtils.getPublicKey(publicKey));
jsonRequest.setAseKey(aseKeyStr);
//请求的业务参数进行加密
String body = "";
try {
body = AESUtils.encode(aseKey, JacksonUtil.beanToJson(businessParams));
} catch (Exception e) {
throw new RuntimeException("报文加密异常", e);
}
jsonRequest.setBody(body);
//json转map
Map<String, Object> paramMap = RSAUtils.beanToMap(jsonRequest);
paramMap.remove("sign");
// 参数排序
Map<String, Object> sortedMap = RSAUtils.sort(paramMap);
//拼接参数:key1Value1key2Value2
String urlParams = RSAUtils.groupStringParam(sortedMap);
//私钥签名
String sign = RSAUtils.sign(HexUtils.hexStringToBytes(urlParams), privateKey);
jsonRequest.setSign(sign);
/*****调用方(请求方)*****/
/*****接收方(自己的系统)*****/
//参数判空(略)
//appId校验(略)
//本条请求的合法性校验《唯一不重复请求;时间合理》(略)
//验签
Map<String, Object> paramMap2 = RSAUtils.beanToMap(jsonRequest);
paramMap2.remove("sign");
//参数排序
Map<String, Object> sortedMap2 = RSAUtils.sort(paramMap2);
//拼接参数:key1Value1key2Value2
String urlParams2 = RSAUtils.groupStringParam(sortedMap2);
//签名验证
boolean verify = RSAUtils.verify(HexUtils.hexStringToBytes(urlParams2), publicKey, jsonRequest.getSign());
if (!verify) {
throw new RuntimeException("签名验证失败");
}
//私钥解密,获取aseKey
String aseKey2 = RSAUtils.privateDecrypt(jsonRequest.getAseKey(), RSAUtils.getPrivateKey(privateKey));
if (!StringUtils.isEmpty(jsonRequest.getBody())) {
// 解密请求报文
String requestBody = "";
try {
requestBody = AESUtils.decode(aseKey2, jsonRequest.getBody());
} catch (Exception e) {
throw new RuntimeException("请求参数解密异常");
}
System.out.println("业务参数解密结果:"+requestBody);
}
/*****接收方(自己的系统)*****/
}
/**
* 生成UUID
* @return 32位UUID
*/
public static String getUUID() {
String uuid = UUID.randomUUID().toString();
uuid = uuid.replace("-", "");
return uuid;
}
}
运行结果:
业务参数解密结果:{"name":"Longer","job":"程序猿","hobby":"打篮球"}
Process finished with exit code 0