RSA+AES实现接口验签和参数加密

一: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

github源码地址

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
双向 RSA + AES 加密是一种常见的加密方式,其中使用 RSA 算法加密 AES 密钥,然后使用 AES 算法加密数据。在 C# 中,可以使用 `RSACryptoServiceProvider` 类和 `AesCryptoServiceProvider` 类来实现加密方式。以下是一个简单的示例: ```csharp using System; using System.IO; using System.Security.Cryptography; using System.Text; class Program { static void Main(string[] args) { string plainText = "Hello, world!"; byte[] encryptedData = Encrypt(plainText); string decryptedText = Decrypt(encryptedData); Console.WriteLine("Original text: {0}", plainText); Console.WriteLine("Encrypted data: {0}", Convert.ToBase64String(encryptedData)); Console.WriteLine("Decrypted text: {0}", decryptedText); } static byte[] Encrypt(string plainText) { byte[] aesKey = GenerateAesKey(); using (var rsa = new RSACryptoServiceProvider()) { rsa.PersistKeyInCsp = false; byte[] encryptedAesKey = rsa.Encrypt(aesKey, true); // 使用 RSA 加密 AES 密钥 using (var aes = new AesCryptoServiceProvider()) { aes.Key = aesKey; aes.GenerateIV(); using (var memoryStream = new MemoryStream()) { memoryStream.Write(aes.IV, 0, aes.IV.Length); using (var cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write)) { byte[] plainData = Encoding.UTF8.GetBytes(plainText); cryptoStream.Write(plainData, 0, plainData.Length); cryptoStream.FlushFinalBlock(); } byte[] encryptedData = memoryStream.ToArray(); byte[] result = new byte[encryptedAesKey.Length + encryptedData.Length]; Buffer.BlockCopy(encryptedAesKey, 0, result, 0, encryptedAesKey.Length); Buffer.BlockCopy(encryptedData, 0, result, encryptedAesKey.Length, encryptedData.Length); return result; } } } } static string Decrypt(byte[] encryptedData) { byte[] encryptedAesKey = new byte[128]; // RSA 加密 AES 密钥得到的密文长度为 128 字节 byte[] encryptedDataOnly = new byte[encryptedData.Length - encryptedAesKey.Length]; Buffer.BlockCopy(encryptedData, 0, encryptedAesKey, 0, encryptedAesKey.Length); Buffer.BlockCopy(encryptedData, encryptedAesKey.Length, encryptedDataOnly, 0, encryptedDataOnly.Length); using (var rsa = new RSACryptoServiceProvider()) { rsa.PersistKeyInCsp = false; byte[] aesKey = rsa.Decrypt(encryptedAesKey, true); // 使用 RSA 解密 AES 密钥 using (var aes = new AesCryptoServiceProvider()) { aes.Key = aesKey; aes.IV = encryptedDataOnly.Take(aes.IV.Length).ToArray(); using (var memoryStream = new MemoryStream()) { using (var cryptoStream = new CryptoStream(memoryStream, aes.CreateDecryptor(), CryptoStreamMode.Write)) { cryptoStream.Write(encryptedDataOnly, aes.IV.Length, encryptedDataOnly.Length - aes.IV.Length); cryptoStream.FlushFinalBlock(); } byte[] decryptedData = memoryStream.ToArray(); return Encoding.UTF8.GetString(decryptedData); } } } } static byte[] GenerateAesKey() { using (var aes = new AesCryptoServiceProvider()) { aes.GenerateKey(); return aes.Key; } } } ``` 上面的代码中,首先调用 `GenerateAesKey` 方法生成 AES 密钥,然后使用 RSA 算法加密 AES 密钥。加密时,先将 AES 密钥使用 RSA 加密,然后使用 AES 算法加密数据。具体来说,将 AES 密钥和 IV 都写入 `MemoryStream` 对象中,然后使用 `CryptoStream` 对象将数据写入 `MemoryStream` 对象中。最后将密文和 RSA 加密AES 密钥一起返回。 解密时,先从密文中取出 RSA 加密AES 密钥,然后使用 RSA 算法解密 AES 密钥。解密时,先从密文中取出 AES 的 IV 值,然后使用 `CryptoStream` 对象将数据解密。最后将解密后的文本返回。 注意,上面的示例仅用于演示 RSA + AES 加密的基本原理,实际使用中还需要考虑安全性等因素。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值