目录
- 参数和返回值得加密目的
- 具体实现方式
- 大致思路
- 代码实现
- 身份检验
参数和返回值得加密目的
为了保证接口不被人拦截下来恶意请求,保证程序的稳定性,我们可以使用接口加密的方法来保证参数和返回值的保密性。
具体实现方式
因为本人是写Java 的,所以这里就直接以Java代码为例。思想都是一样的,只是不同的语言都不同的实现方式。
大致思路
- 我们的参数和返回值只需要定义两个参数:ak,ct,ak存放堆成加密的秘钥,ct存放加密之后的请求内容。
- 加密方式用到AES对称加密和RSA非对称加密,将请求参数使用AES加密,HTTP的POST请求和GET请求都可以将实际请求参数的格式定义成同一种格式(比如:JSON格式),便于后台处理。AES加密使用的秘钥用RSA加密。这样我们就不需要操心参数加密的秘钥是什么了,双方都可以随时更换。
- 使用过滤器获取到请求的加密内容后,通过RSA解密将参数加密的秘钥解析出来,然后再通过解析出来的秘钥通过AES解密去获取实际的请求内容。
注意:不同的请求方式获取参数的方式不同,需要根据不同的请求方式去做不同的解析
代码实现
1. 首先需要一个过滤器,因为我们要拦截请求参数,和加密返回的数据。
因为并不是所有的接口都需要加密,过滤器不能想Spring拦截器那样直接配置拦截和不拦截的类,所以定义了一个ALLOWED_PATHS 集合,用于过滤不需要加解密的请求。然后根据不同的请求方式去解析不同的请求内容,我这里只处理了get请求和post请求这两种常用的请求方式。
package com.pay.filter;
import lombok.extern.logj.Log4j2;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* 请求信息加解密
*/
@Logj2
@Component
@WebFilter(filterName = "EncryptionFilter", urlPatterns = {"/*"})
public class EncryptionFilter implements Filter {
private static final Set<String> ALLOWED_PATHS = Collections.unmodifiableSet(new HashSet<>(
Arrays.asList("/pay/notify")));
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if(request instanceof HttpServletRequest) {
HttpServletRequest hRequest = (HttpServletRequest) request;
String servletPath = hRequest.getServletPath();
log.info("request_path:path="+servletPath);
boolean allow = false;
for (String allowedPath : ALLOWED_PATHS) {
if(servletPath.contains(allowedPath)) {
allow = true;
break;
}
}
if(allow) {
chain.doFilter(request, response);
log.info("no Encryption");
return;
}
MyHttpServletResponseWrapper responseWrapper = new MyHttpServletResponseWrapper((HttpServletResponse) response);
if(hRequest.getMethod().equalsIgnoreCase("post")) {
MyHttpServletRequestWrapper requestWrapper = null;
try {
requestWrapper = new MyHttpServletRequestWrapper(hRequest);
} catch (Exception e) {
request.getRequestDispatcher(".html").forward(request,response);
}
chain.doFilter(requestWrapper, responseWrapper);
}else if(!hRequest.getMethod().equalsIgnoreCase("options")){
log.info("收到 potions请求");
} else { //其余默认get请求
ParameterRequestWrapper requestWrapper = null;
try {
requestWrapper = new ParameterRequestWrapper(hRequest);
} catch (Exception e) {
request.getRequestDispatcher(".html").forward(request,response);
}
chain.doFilter(requestWrapper, responseWrapper);
}
String resp = responseWrapper.getTextContent(); //获取接口返回内容
//加密处理返回
response.getOutputStream().write(HttpEncryptUtil.serverEncrypt(resp).getBytes(StandardCharsets.UTF_));
}
}
}
2. 对于POST请求,定义一个解析request中参数的类 MyHttpServletRequestWrapper继承HttpServletRequestWrapper ,去解析请求参数。
具体实现方式如下:
readBody:实际解析请求参数
package com.pay.filter;
import com.alipay.api.internal.util.file.IOUtils;
import com.pay.util.HttpEncryptUtil;
import org.apache.commons.lang.StringUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {
private String requestBody = null;
public MyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
requestBody = readBody(request);
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody.getBytes(StandardCharsets.UTF_));
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return bais.read();
}
};
}
private static String readBody(ServletRequest request) throws IOException {
String param = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_);
if(StringUtils.isNotBlank(param)) {
//解密请求参数
return HttpEncryptUtil.serverDecrypt(param);
}
return param;
}
}
3. 对于get请求,定义一个参数类ParameterRequestWrapper,继承HttpServletRequestWrapper 去解析URL上的参数
具体方式如下:
重载构造方法获取加密之后的参数,去解析出来
ParameterRequestWrapper(HttpServletRequest request)
重写getParameter和setParameter等获取参数和设置参数的方法
package com.pay.filter;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.pay.util.HttpEncryptUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.HashMap;
import java.util.Map;
public class ParameterRequestWrapper extends HttpServletRequestWrapper {
private Map<String , String[]> params = new HashMap<String, String[]>();
@SuppressWarnings("unchecked")
public ParameterRequestWrapper(HttpServletRequest request) {
// 将request交给父类,以便于调用对应方法的时候,将其输出,其实父亲类的实现方式和第一种new的方式类似
super(request);
//将参数表,赋予给当前的Map以便于持有request中的参数
//解析
String ak = request.getParameter("ak");
String ct = request.getParameter("ct");
JSONObject jsonObject = new JSONObject();
jsonObject.put("ak",ak);
jsonObject.put("ct",ct);
String s = HttpEncryptUtil.serverDecrypt(jsonObject.toJSONString());
addAllParameters(JSON.parseObject(s));
}
//重载一个构造方法
public ParameterRequestWrapper(HttpServletRequest request , Map<String , Object> extendParams) {
this(request);
addAllParameters(extendParams);//这里将扩展参数写入参数表
}
@Override
public String getParameter(String name) {//重写getParameter,代表参数从当前类中的map获取
String[]values = params.get(name);
if(values == null || values.length ==) {
return null;
}
return values[];
}
public String[] getParameterValues(String name) {//同上
return params.get(name);
}
public void addAllParameters(Map<String , Object>otherParams) {//增加多个参数
for(Map.Entry<String , Object>entry : otherParams.entrySet()) {
addParameter(entry.getKey() , entry.getValue());
}
}
public void addParameter(String name , Object value) {//增加参数
if(value != null) {
if(value instanceof String[]) {
params.put(name , (String[])value);
}else if(value instanceof String) {
params.put(name , new String[] {(String)value});
}else {
params.put(name , new String[] {String.valueOf(value)});
}
}
}
}
注意:为了便于区分我才把获取post请求内容和get请求内容的类写成了两个,其实可以尝试着去将两个解析类合成一个
4. 加解密工具类 HttpEncryptUtil ,和两个网上百度的AESUtil,RSAUtil
package com.pay.util;
import com.alibaba.fastjson.JSONObject;
import com.pay.common.Constant;
import org.apache.commons.codec.binary.Base;
import org.apache.commons.lang.StringUtils;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.interfaces.RSAPublicKey;
public class HttpEncryptUtil {
//解密请求内容
public static String serverDecrypt(String content) {
JSONObject result = JSONObject.parseObject(content);
String encryptAesKeyStr = result.getString("ak");
String encryptContent = result.getString("ct");
//使用私钥解密 获取解密内容的AES密钥
// encryptAesKeyStr = decodeBaseString(encryptAesKeyStr); //base64解码
PrivateKey privateKey = RSAUtil.getPrivateKey(HTTP_PRIVATE_KEY);
String aesKey = RSAUtil.decryptByPrivate(decodeBaseString(encryptAesKeyStr), privateKey);
//使用解密出来的key解密content:使用AES
return AESUtil.decryptAES(decodeBaseString(encryptContent), aesKey);
}
public static String serverDecryptByPublic(String content) {
JSONObject result = JSONObject.parseObject(content);
String encryptAesKeyStr = result.getString("ak");
String encryptContent = result.getString("ct");
//使用私钥解密 获取解密内容的AES密钥
// encryptAesKeyStr = decodeBaseString(encryptAesKeyStr); //base64解码
RSAPublicKey publicKey = RSAUtil.getPublicKey(HTTP_PUBLIC_KEY);
String aesKey = RSAUtil.decryptByPublic(decodeBaseString(encryptAesKeyStr), publicKey);
//使用解密出来的key解密content:使用AES
return AESUtil.decryptAES(decodeBaseString(encryptContent), aesKey);
}
//加密返回内容
public static String serverEncrypt(String content) {
String aesKey = HTTP_CONTENT_KEY;
//使用私钥加密AES的key
PrivateKey privateKey = RSAUtil.getPrivateKey(HTTP_PRIVATE_KEY);
String ak = RSAUtil.encryptByPrivate(aesKey.getBytes(StandardCharsets.UTF_), privateKey);
String ct = AESUtil.encryptAES(content, aesKey);
JSONObject result = new JSONObject();
result.put("ak",encodeBaseString(ak));
result.put("ct",encodeBaseString(ct));
return result.toJSONString();
}
public static String serverEncryptByPublic(String content) {
String aesKey = HTTP_CONTENT_KEY;
//使用公钥钥加密AES的key
RSAPublicKey publicKey = RSAUtil.getPublicKey(HTTP_PUBLIC_KEY);
String ak = RSAUtil.encryptByPublic(aesKey.getBytes(StandardCharsets.UTF_), publicKey);
String ct = AESUtil.encryptAES(content, aesKey);
JSONObject result = new JSONObject();
result.put("ak",encodeBaseString(ak));
result.put("ct",encodeBaseString(ct));
return result.toJSONString();
}
public static String encodeBaseString(String content) {
if(StringUtils.isBlank(content)) {
return null;
}
return Base.encodeBase64String(content.getBytes(StandardCharsets.UTF_8));
}
public static String decodeBaseString(String content) {
if(StringUtils.isBlank(content)) {
return null;
}
return new String(Base.decodeBase64(content), StandardCharsets.UTF_8);
}
}
package com.pay.util;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Base;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
public class AESUtil {
public static final Charset CHARSET = StandardCharsets.UTF_;
public static final String ALGORITHMS_MD = "MD5";
public static final String SHA = "SHAPRNG";
public static final String ALGORITHM = "AES";
/**
* 加密
*
* @param content 需要加密的内容
* @param password 加密密码
* @return
*/
public static byte[] encrypt(String content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance(ALGORITHM);
SecureRandom random = SecureRandom.getInstance(SHA);
random.setSeed(password.getBytes());
kgen.init(, random);
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);// 创建密码器
byte[] byteContent = content.getBytes(CHARSET);
cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
return cipher.doFinal(byteContent); // 加密
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 解密
*
* @param content 待解密内容
* @param password 解密密钥
* @return
*/
public static byte[] decrypt(byte[] content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance(ALGORITHM);
SecureRandom random = SecureRandom.getInstance(SHA);
random.setSeed(password.getBytes());
kgen.init(, random);
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);// 创建密码器
cipher.init(Cipher.DECRYPT_MODE, key);// 初始化
return cipher.doFinal(content); // 加密
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 将二进制转换成进制
*
* @param buf
* @return
*/
public static String parseByteHexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i =; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] &xFF);
if (hex.length() ==) {
hex = '' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
/**
* 将进制转换为二进制
*
* @param hexStr
* @return
*/
public static byte[] parseHexStrByte(String hexStr) {
if (hexStr.length() <)
return null;
byte[] result = new byte[hexStr.length() /];
for (int i =; i < hexStr.length() / 2; i++) {
int high = Integer.parseInt(hexStr.substring(i *, i * 2 + 1), 16);
int low = Integer.parseInt(hexStr.substring(i * + 1, i * 2 + 2), 16);
result[i] = (byte) (high * + low);
}
return result;
}
private static byte[] doSign(String algorithms, String inputStr) throws NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance(algorithms);
messageDigest.update(inputStr.getBytes(CHARSET));
return messageDigest.digest();
}
private static String signToHexStr(String algorithms, String inputStr) {
try {
return parseByteHexStr(doSign(algorithms, inputStr));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
public static String sign(String key, String sourceStr) {
String mdStr1 = signToHexStr(ALGORITHMS_MD5, sourceStr);
String sourceStr = md5Str1 + key;
return signToHexStr(ALGORITHMS_MD, sourceStr2);
}
public static String toDecryptParam(String sourceStr, String password){
try {
byte[] sourceByte = parseHexStrByte(sourceStr);
byte[] bytes = decrypt(sourceByte, password);
return new String(bytes,CHARSET);
}catch (Exception e){
e.printStackTrace();
}
return "";
}
public static String toEncryptResult(Object sourceStr, String password){
String parameterJson = JSONObject.toJSONString(sourceStr);
byte[] encode = encrypt(parameterJson, password);
return parseByteHexStr(encode);
}
public static String encryptAES(String content, String password) {
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCSPadding");
byte[] byteContent = content.getBytes("utf-");
cipher.init(, getSecretKey(password));
byte[] result = cipher.doFinal(byteContent);
return Base.encodeBase64String(result);
} catch (Exception var) {
return null;
}
}
public static String decryptAES(String content, String password) {
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCSPadding");
cipher.init(, getSecretKey(password));
byte[] result = cipher.doFinal(Base.decodeBase64(content));
return new String(result, "utf-");
} catch (Exception var) {
return null;
}
}
private static SecretKeySpec getSecretKey(String password) {
KeyGenerator kg = null;
try {
kg = KeyGenerator.getInstance("AES");
SecureRandom random = SecureRandom.getInstance("SHAPRNG");
random.setSeed(password.getBytes());
kg.init(, random);
SecretKey secretKey = kg.generateKey();
return new SecretKeySpec(secretKey.getEncoded(), "AES");
} catch (NoSuchAlgorithmException var) {
return null;
}
}
package com.pay.util;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slfj.Slf4j;
import org.apache.commons.lang.StringUtils;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.*;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCSEncodedKeySpec;
import java.security.spec.XEncodedKeySpec;
import java.util.*;
/**
* RSA算法加密/解密工具类
*/
@Slfj
public class RSAUtil {
/** 算法名称 */
private static final String ALGORITHM = "RSA";
/** 默认密钥大小 */
private static final int KEY_SIZE =;
/** 用来指定保存密钥对的文件名和存储的名称 */
private static final String PUBLIC_KEY_NAME = "publicKey";
private static final String PRIVATE_KEY_NAME = "privateKey";
private static final String PUBLIC_FILENAME = "publicKey.properties";
private static final String PRIVATE_FILENAME = "privateKey.properties";
/** 密钥对生成器 */
private static
KeyPairGenerator keyPairGenerator = null;
private static KeyFactory keyFactory = null;
/** 缓存的密钥对 */
private static KeyPair keyPair = null;
/** Base 编码/解码器 JDK1.8 */
private static Base.Decoder decoder = Base64.getDecoder();
private static Base.Encoder encoder = Base64.getEncoder();
/** 初始化密钥工厂 */
static{
try {
keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
keyFactory = KeyFactory.getInstance(ALGORITHM);
} catch (NoSuchAlgorithmException e) {
log.error("RSAUtil[ERROR]:e={}",e);
}
}
/** 私有构造器 */
private RSAUtil(){}
/**
* 生成密钥对
* 将密钥分别用Base编码保存到#publicKey.properties#和#privateKey.properties#文件中
* 保存的默认名称分别为publicKey和privateKey
*/
public static synchronized Map<String, Object> generateKeyPair(){
try {
keyPairGenerator.initialize(KEY_SIZE,new SecureRandom(UUID.randomUUID().toString().replaceAll("-","").getBytes()));
keyPair = keyPairGenerator.generateKeyPair();
} catch (InvalidParameterException e){
log.error("KeyPairGenerator does not support a key length of " + KEY_SIZE + ".",e);
} catch (NullPointerException e){
log.error("RSAUtil#key_pair_gen is null,can not generate KeyPairGenerator instance.",e);
}
RSAPublicKey rsaPublicKey = (RSAPublicKey)keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey)keyPair.getPrivate();
String publicKeyString = encoder.encodeToString(rsaPublicKey.getEncoded());
String privateKeyString = encoder.encodeToString(rsaPrivateKey.getEncoded());
storeKey(publicKeyString,PUBLIC_KEY_NAME,PUBLIC_FILENAME);
storeKey(privateKeyString,PRIVATE_KEY_NAME,PRIVATE_FILENAME);
Map<String, Object> keyPair = new HashMap<>();
keyPair.put("public", publicKeyString);
keyPair.put("private", privateKeyString);
return keyPair;
}
/**
* 将指定的密钥字符串保存到文件中,如果找不到文件,就创建
* @param keyString 密钥的Base编码字符串(值)
* @param keyName 保存在文件中的名称(键)
* @param fileName 目标文件名
*/
private static void storeKey(String keyString,String keyName,String fileName){
Properties properties = new Properties();
//存放密钥的绝对地址
String path = null;
try{
path = RSAUtil.class.getClassLoader().getResource(fileName).toString();
path = path.substring(path.indexOf(":") +);
}catch (NullPointerException e){
//如果不存#fileName#就创建
log.warn("storeKey()# " + fileName + " is not exist.Begin to create this file.");
String classPath = RSAUtil.class.getClassLoader().getResource("").toString();
String prefix = classPath.substring(classPath.indexOf(":") +);
String suffix = fileName;
File file = new File(prefix + suffix);
try {
file.createNewFile();
path = file.getAbsolutePath();
} catch (IOException e) {
log.error(fileName +" create fail.",e);
}
}
try(OutputStream out = new FileOutputStream(path)){
properties.setProperty(keyName,keyString);
properties.store(out,"There is " + keyName);
} catch (FileNotFoundException e) {
log.error("ModulusAndExponent.properties is not found.",e);
} catch (IOException e) {
log.error("OutputStream output failed.",e);
}
}
/**
* 获取密钥字符串
* @param keyName 需要获取的密钥名
* @param fileName 密钥所在文件
* @return Base编码的密钥字符串
*/
private static String getKeyString(String keyName,String fileName){
if (RSAUtil.class.getClassLoader().getResource(fileName) == null){
log.warn("getKeyString()# " + fileName + " is not exist.Will run #generateKeyPair()# firstly.");
generateKeyPair();
}
try(InputStream in = RSAUtil.class.getClassLoader().getResource(fileName).openStream()){
Properties properties = new Properties();
properties.load(in);
return properties.getProperty(keyName);
} catch (IOException e) {
log.error("getKeyString()#" + e.getMessage(),e);
}
return null;
}
/**
* 从文件获取RSA公钥
* @return RSA公钥
* @throws InvalidKeySpecException
*/
public static RSAPublicKey getPublicKey(){
try {
byte[] keyBytes = decoder.decode(getKeyString(PUBLIC_KEY_NAME,PUBLIC_FILENAME));
XEncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
return (RSAPublicKey)keyFactory.generatePublic(xEncodedKeySpec);
}catch (InvalidKeySpecException e) {
log.error("getPublicKey()#" + e.getMessage(),e);
}
return null;
}
public static RSAPublicKey getPublicKey(String publicKeyString) {
if(StringUtils.isBlank(publicKeyString)) {
return null;
}
try {
byte[] keyBytes = decoder.decode(publicKeyString);
XEncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
return (RSAPublicKey)keyFactory.generatePublic(xEncodedKeySpec);
}catch (InvalidKeySpecException e) {
log.error("getPublicKey()#" + e.getMessage(),e);
}
return null;
}
/**
* 从文件获取RSA私钥
* @return RSA私钥
* @throws InvalidKeySpecException
*/
public static RSAPrivateKey getPrivateKey(){
try {
byte[] keyBytes = decoder.decode(getKeyString(PRIVATE_KEY_NAME,PRIVATE_FILENAME));
PKCSEncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
return (RSAPrivateKey)keyFactory.generatePrivate(pkcsEncodedKeySpec);
} catch (InvalidKeySpecException e) {
log.error("getPrivateKey()#" + e.getMessage(),e);
}
return null;
}
public static RSAPrivateKey getPrivateKey(String privateKeyString) {
if(StringUtils.isBlank(privateKeyString)) {
return null;
}
try {
byte[] keyBytes = decoder.decode(privateKeyString);
PKCSEncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
return (RSAPrivateKey)keyFactory.generatePrivate(pkcsEncodedKeySpec);
} catch (InvalidKeySpecException e) {
log.error("getPrivateKey()#" + e.getMessage(),e);
}
return null;
}
/**
* RSA公钥加密
* @param content 等待加密的数据
* @param publicKey RSA 公钥 if null then getPublicKey()
* @return 加密后的密文(进制的字符串)
*/
public static String encryptByPublic(byte[] content,PublicKey publicKey){
if (publicKey == null){
publicKey = getPublicKey();
}
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE,publicKey);
//该密钥能够加密的最大字节长度
int splitLength = ((RSAPublicKey)publicKey).getModulus().bitLength() / -11;
byte[][] arrays = splitBytes(content,splitLength);
StringBuffer stringBuffer = new StringBuffer();
for (byte[] array : arrays){
stringBuffer.append(bytesToHexString(cipher.doFinal(array)));
}
return stringBuffer.toString();
} catch (NoSuchAlgorithmException e) {
log.error("encrypt()#NoSuchAlgorithmException",e);
} catch (NoSuchPaddingException e) {
log.error("encrypt()#NoSuchPaddingException",e);
} catch (InvalidKeyException e) {
log.error("encrypt()#InvalidKeyException",e);
} catch (BadPaddingException e) {
log.error("encrypt()#BadPaddingException",e);
} catch (IllegalBlockSizeException e) {
log.error("encrypt()#IllegalBlockSizeException",e);
}
return null;
}
/**
* RSA私钥加密
* @param content 等待加密的数据
* @param privateKey RSA 私钥 if null then getPrivateKey()
* @return 加密后的密文(进制的字符串)
*/
public static String encryptByPrivate(byte[] content,PrivateKey privateKey){
if (privateKey == null){
privateKey = getPrivateKey();
}
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE,privateKey);
//该密钥能够加密的最大字节长度
int splitLength = ((RSAPrivateKey)privateKey).getModulus().bitLength() / -11;
byte[][] arrays = splitBytes(content,splitLength);
StringBuffer stringBuffer = new StringBuffer();
for(byte[] array : arrays){
stringBuffer.append(bytesToHexString(cipher.doFinal(array)));
}
return stringBuffer.toString();
} catch (NoSuchAlgorithmException e) {
log.error("encrypt()#NoSuchAlgorithmException",e);
} catch (NoSuchPaddingException e) {
log.error("encrypt()#NoSuchPaddingException",e);
} catch (InvalidKeyException e) {
log.error("encrypt()#InvalidKeyException",e);
} catch (BadPaddingException e) {
log.error("encrypt()#BadPaddingException",e);
} catch (IllegalBlockSizeException e) {
log.error("encrypt()#IllegalBlockSizeException",e);
}
return null;
}
/**
* RSA私钥解密
* @param content 等待解密的数据
* @param privateKey RSA 私钥 if null then getPrivateKey()
* @return 解密后的明文
*/
public static String decryptByPrivate(String content,PrivateKey privateKey){
if (privateKey == null){
privateKey = getPrivateKey();
}
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE,privateKey);
//该密钥能够加密的最大字节长度
int splitLength = ((RSAPrivateKey)privateKey).getModulus().bitLength() /;
byte[] contentBytes = hexStringToBytes(content);
byte[][] arrays = splitBytes(contentBytes,splitLength);
StringBuffer stringBuffer = new StringBuffer();
String sTemp = null;
for (byte[] array : arrays){
stringBuffer.append(new String(cipher.doFinal(array)));
}
return stringBuffer.toString();
} catch (NoSuchAlgorithmException e) {
log.error("encrypt()#NoSuchAlgorithmException",e);
} catch (NoSuchPaddingException e) {
log.error("encrypt()#NoSuchPaddingException",e);
} catch (InvalidKeyException e) {
log.error("encrypt()#InvalidKeyException",e);
} catch (BadPaddingException e) {
log.error("encrypt()#BadPaddingException",e);
} catch (IllegalBlockSizeException e) {
log.error("encrypt()#IllegalBlockSizeException",e);
}
return null;
}
/**
* RSA公钥解密
* @param content 等待解密的数据
* @param publicKey RSA 公钥 if null then getPublicKey()
* @return 解密后的明文
*/
public static String decryptByPublic(String content,PublicKey publicKey){
if (publicKey == null){
publicKey = getPublicKey();
}
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE,publicKey);
//该密钥能够加密的最大字节长度
int splitLength = ((RSAPublicKey)publicKey).getModulus().bitLength() /;
byte[] contentBytes = hexStringToBytes(content);
byte[][] arrays = splitBytes(contentBytes,splitLength);
StringBuffer stringBuffer = new StringBuffer();
String sTemp = null;
for (byte[] array : arrays){
stringBuffer.append(new String(cipher.doFinal(array)));
}
return stringBuffer.toString();
} catch (NoSuchAlgorithmException e) {
log.error("encrypt()#NoSuchAlgorithmException",e);
} catch (NoSuchPaddingException e) {
log.error("encrypt()#NoSuchPaddingException",e);
} catch (InvalidKeyException e) {
log.error("encrypt()#InvalidKeyException",e);
} catch (BadPaddingException e) {
log.error("encrypt()#BadPaddingException",e);
} catch (IllegalBlockSizeException e) {
log.error("encrypt()#IllegalBlockSizeException",e);
}
return null;
}
/**
* 根据限定的每组字节长度,将字节数组分组
* @param bytes 等待分组的字节组
* @param splitLength 每组长度
* @return 分组后的字节组
*/
public static byte[][] splitBytes(byte[] bytes,int splitLength){
//bytes与splitLength的余数
int remainder = bytes.length % splitLength;
//数据拆分后的组数,余数不为时加1
int quotient = remainder != ? bytes.length / splitLength + 1:bytes.length / splitLength;
byte[][] arrays = new byte[quotient][];
byte[] array = null;
for (int i =;i<quotient;i++){
//如果是最后一组(quotient-),同时余数不等于0,就将最后一组设置为remainder的长度
if (i == quotient - && remainder != 0){
array = new byte[remainder];
System.arraycopy(bytes,i * splitLength,array,,remainder);
} else {
array = new byte[splitLength];
System.arraycopy(bytes,i*splitLength,array,,splitLength);
}
arrays[i] = array;
}
return arrays;
}
/**
* 将字节数组转换成进制字符串
* @param bytes 即将转换的数据
* @return进制字符串
*/
public static String bytesToHexString(byte[] bytes){
StringBuffer sb = new StringBuffer(bytes.length);
String temp = null;
for (int i =;i< bytes.length;i++){
temp = Integer.toHexString(xFF & bytes[i]);
if(temp.length() <){
sb.append();
}
sb.append(temp);
}
return sb.toString();
}
/**
* 将进制字符串转换成字节数组
* @param hex进制字符串
* @return byte[]
*/
public static byte[] hexStringToBytes(String hex){
int len = (hex.length() /);
hex = hex.toUpperCase();
byte[] result = new byte[len];
char[] chars = hex.toCharArray();
for (int i=;i<len;i++){
int pos = i *;
result[i] = (byte)(toByte(chars[pos]) << | toByte(chars[pos + 1]));
}
return result;
}
/**
* 将char转换为byte
* @param c char
* @return byte
*/
private static byte toByte(char c){
return (byte)"ABCDEF".indexOf(c);
}
身份检验
除了参数加密之外,我们还可以做一个简单的身份校验。这里就需要使用到Spring的拦截器了。
可以在header中放一个身份token,拦截器里面校验
具体实现:
一个拦截器 TokenInterceptor.java 和配置类 WebConfig.java
package com.pay.filter;
import com.alibaba.fastjson.JSONObject;
import com.pay.common.ErrorCode;
import com.pay.common.GlobalEnums;
import com.pay.entity.SourceConfig;
import com.pay.service.SourceConfigService;
import com.pay.util.RedisUtil;
import lombok.extern.slfj.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
@Slfj
public class TokenInterceptor implements HandlerInterceptor {
@Autowired
private SourceConfigService sourceConfigService;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {
String token = httpServletRequest.getHeader("token");
httpServletResponse.setContentType("text/html;charset=utf-");
httpServletResponse.setCharacterEncoding("utf-");
ServletOutputStream outputStream = httpServletResponse.getOutputStream();
JSONObject jsonObject =new JSONObject();
if(StringUtils.isBlank(token)) {
jsonObject.put("code", ErrorCode.Ax.getCode());
jsonObject.put("message", ErrorCode.Ax.getMessage());
outputStream.write(jsonObject.toJSONString().getBytes());
return false;
}
SourceConfig sourceConfig = sourceConfigService.selectBySource(token);
if(sourceConfig == null) {
jsonObject.put("code", ErrorCode.Ax.getCode());
jsonObject.put("message", ErrorCode.Ax.getMessage());
outputStream.write(jsonObject.toJSONString().getBytes());
return false;
}
if(sourceConfig.getStatus().equals(GlobalEnums.EnableEnum.FALSE.getEnable())) {
jsonObject.put("code", ErrorCode.Ax.getCode());
jsonObject.put("message", ErrorCode.Ax.getMessage());
outputStream.write(jsonObject.toJSONString().getBytes());
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object arg, Exception arg3)
throws Exception {
RequestTokenHolder.remove();
}
}
package com.pay.config;
import com.pay.filter.TokenInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
}
@Resource
private TokenInterceptor tokenInterceptor;
/**
* 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
*
* @param registry ""
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor)
.addPathPatterns("/**")
.excludePathPatterns(
"/pay/notify/**"//登录接口
)
// 过滤swagger
.excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v/**", "/swagger-ui.html/**");
}
}