需求&实现思路

工作中遇到一个需求,需要将接口数据加密发送给后台,项目中采用RSA+AES方式,记录一下思路和实现。

一、加密
1、随机生成AES 32位密钥
2、通过AES对传递数据加密
3、通过RSA的公钥Publickey对AES的密钥进行加密
4、通过RSA的私钥Privatekey对数据进行签名

二、解密
1、得到数据拿到sign值,先做验签
2、使用RSA的私钥private_key解密拿到AES的aesKey
3、使用AES解密得到所需数据

下面是具体实现步骤
1、通过opensll生成加解密所需要的公钥和私钥,生成步骤自行百度,这里 不在介绍
2、AES加解密

public static class AESUtils {
        private static final String AES = "AES";
        //偏移量
        private static final String IV_STRING = "";
        /**
         * 密钥长度32字节,256位
         */
        private static final int AES_KEY_LENGTH = 32;

        /**
         * 随机生成32位AES密钥
         */
        public static String getRandomString() {
        	//生成规则自己定义
            String base = "";
            Random random = new Random();
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < AES_KEY_LENGTH; i++) {
                int number = random.nextInt(base.length());
                sb.append(base.charAt(number));
            }
            return sb.toString();
        }

        /**
         * 加密
         *
         * @param content 加密内容
         * @return 密文
         * @throws Exception e
         */
        @RequiresApi(api = Build.VERSION_CODES.O)
        public static String encrypt(String key, String content) throws Exception {
            byte[] encryptedBytes = new byte[0];
            try {
                byte[] byteContent = content.getBytes();
                // 注意,为了能与 iOS 统一
                // 这里的 key 不可以使用 KeyGenerator、SecureRandom、SecretKey 生成
                byte[] enCodeFormat = key.getBytes(StandardCharsets.UTF_8);
                SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, AES);
                byte[] initParam = IV_STRING.getBytes();
                IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
                // 指定加密的算法、工作模式和填充方式
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
                encryptedBytes = cipher.doFinal(byteContent);
                // 同样对加密后数据进行 base64 编码
            } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
                LogUtil.e("AES decrypt Exception,content = {},Exception = {}" + content + Arrays.toString(e.getStackTrace()));

            }


//            return Base64.getUrlEncoder().encodeToString(encryptedBytes);
            return Base64Util.encode(encryptedBytes);
        }

        /**
         * 解密
         *
         * @param content 密文
         * @return 明文
         * @throws Exception e
         */
        @RequiresApi(api = Build.VERSION_CODES.O)
        public static String decrypt(String content, String aesKey) {
            // base64 解码
            try {
                byte[] encryptedBytes = Base64Util.decode(content);
                byte[] enCodeFormat = aesKey.getBytes();
                SecretKeySpec secretKey = new SecretKeySpec(enCodeFormat, AES);
                byte[] initParam = IV_STRING.getBytes();
                IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
                byte[] result = cipher.doFinal(encryptedBytes);
                return new String(result, "UTF-8");
            } catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {

                LogUtil.e("AES decrypt Exception,content = {},Exception = {}" + content + Arrays.toString(e.getStackTrace()));
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

    }

2、RSA加解密,opensll生成公钥和私钥的pem文件放在项目assets下

public static class RSAUtils {
        /**
         * 算法名字
         */
        private static final String RSA_ALGORITHM = "RSA";

        /**
         * 消息摘要算法
         */
        private static final String MD_ALGORITHM = "SHA256withRSA";

        /**
         * RSA最大加密明文大小
         */
        private static final int MAX_ENCRYPT_BLOCK = 117;

        /**
         * RSA最大解密密文大小
         */
        private static final int MAX_DECRYPT_BLOCK = 128;

        /**
         * 制作公钥
         *
         * @return
         */
        @RequiresApi(api = Build.VERSION_CODES.O)
        public static PublicKey getPublicKey() {

            PublicKey public_Key = null;
            try {
                assert MyApplication.instance != null;
                InputStream in = MyApplication.instance.getResources().getAssets().open("public_key.pem");
                BufferedReader br = new BufferedReader(new InputStreamReader(in));
                String readLine = null;
                StringBuilder sb = new StringBuilder();
                while ((readLine = br.readLine()) != null) {
                    if (readLine.charAt(0) == '-') {
                        continue;
                    } else {
                        sb.append(readLine);
                    }
                }
                KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
                byte[] buffer = Base64Util.decode(sb.toString());
                EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
                public_Key = keyFactory.generatePublic(keySpec);
                return public_Key;
            } catch (Exception e) {
            }
            return null;
        }
       
        /**
         * 制作私钥
         *
         * @return
         */
        @RequiresApi(api = Build.VERSION_CODES.O)
        public static PrivateKey getPrivateKey() {

            PrivateKey privateKey = null;
            try {
                assert MyApplication.instance != null;
                InputStream in = MyApplication.instance.getResources().getAssets().open("private_key.pem");
                BufferedReader br = new BufferedReader(new InputStreamReader(in));
                String readLine = null;
                StringBuilder sb = new StringBuilder();
                while ((readLine = br.readLine()) != null) {
                    if (readLine.charAt(0) == '-') {
                        continue;
                    } else {
                        sb.append(readLine);
                    }
                }
                in.close();
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                byte[] buffer = Base64Util.decode(sb.toString());
                EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(buffer);
                privateKey = keyFactory.generatePrivate(privateKeySpec);
                return privateKey;

            } catch (Exception e) {
            }
            return null;
        }


        /**
         * RSA加密
         * <p>
         * //         * @param data      待加密数据
         *
         * @param publicKey 公钥
         * @return
         */
        @RequiresApi(api = Build.VERSION_CODES.O)
        public static String encrypt(String aesKey, PublicKey publicKey) {
            //RSA加密
            try {
                if (publicKey == null) {
                    return null;
                }
                Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
                cipher.init(Cipher.ENCRYPT_MODE, publicKey);
                int inputLen = aesKey.getBytes().length;
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                int offset = 0;
                byte[] cache;
                int i = 0;
                // 对数据分段加密
                while (inputLen - offset > 0) {
                    if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
                        cache = cipher.doFinal(aesKey.getBytes(), offset, MAX_ENCRYPT_BLOCK);
                    } else {
                        cache = cipher.doFinal(aesKey.getBytes(), offset, inputLen - offset);
                    }
                    out.write(cache, 0, cache.length);
                    i++;
                    offset = i * MAX_ENCRYPT_BLOCK;
                }
                byte[] encryptedData = out.toByteArray();
                out.close();
                // 获取加密内容使用base64进行编码,并以UTF-8为标准转化成字符串
                // 加密后的字符串
                return Base64Util.encode(encryptedData);
            } catch (Exception e) {
//                log.warn("encrypt error, ex:{}", e.getMessage());
            }
            return null;
        }

        /**
         * RSA解密
         *
         * @param data       待解密数据
         * @param privateKey 私钥
         * @return
         */
        @RequiresApi(api = Build.VERSION_CODES.O)
        public static String decrypt(String data, PrivateKey privateKey) {
            try {
                if (privateKey == null) {
                    return null;
                }
                Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
                cipher.init(Cipher.DECRYPT_MODE, privateKey);
                byte[] dataBytes = Base64Util.decode(data);
                int inputLen = dataBytes.length;
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                int offset = 0;
                byte[] cache;
                int i = 0;
                // 对数据分段解密
                while (inputLen - offset > 0) {
                    if (inputLen - offset > MAX_DECRYPT_BLOCK) {
                        cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
                    } else {
                        cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
                    }
                    out.write(cache, 0, cache.length);
                    i++;
                    offset = i * MAX_DECRYPT_BLOCK;
                }
                out.close();
                // 解密后的内容
                return out.toString("UTF-8");
            } catch (Exception e) {
//                log.warn("decrypt error, ex:{}", e.getMessage());
            }
            return null;
        }

        /**
         * 签名
         *
         * @param data       待签名数据
         * @param privateKey 私钥
         * @return 签名
         */
        @RequiresApi(api = Build.VERSION_CODES.O)
        public static String sign(String data, PrivateKey privateKey) {
            try {
                byte[] keyBytes = privateKey.getEncoded();
                PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
                KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
                PrivateKey key = keyFactory.generatePrivate(keySpec);
                Signature signature = Signature.getInstance(MD_ALGORITHM);
                signature.initSign(key);
                signature.update(data.getBytes());
                return Base64Util.encode(signature.sign());
            } catch (Exception e) {
//                log.warn("RSA sign error, ex:{}", e.getMessage());
            }
            return null;
        }

        /**
         * 验签
         *
         * @param srcData   原始字符串
         * @param publicKey 公钥
         * @param sign      签名
         * @return 是否验签通过
         */
        @RequiresApi(api = Build.VERSION_CODES.O)
        public static boolean verify(String srcData, PublicKey publicKey, String sign) {
            try {
                byte[] keyBytes = publicKey.getEncoded();
                X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
                KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
                PublicKey key = keyFactory.generatePublic(keySpec);
                Signature signature = Signature.getInstance(MD_ALGORITHM);
                signature.initVerify(key);
                signature.update(srcData.getBytes());
                return signature.verify(Base64Util.decode(sign));
            } catch (Exception e) {
            }
            return false;
        }

    }

3、Base64Util编解码工具类 ,需要依赖javabase64-1.3.1.jar

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import it.sauronsoftware.base64.Base64;

public class Base64Util {
    /** */
    /**
     * 文件读取缓冲区大小
     */
    private static final int CACHE_SIZE = 1024;

    /** */
    /**
     * <p>
     * BASE64字符串解码为二进制数据
     * </p>
     *
     * @param base64
     * @return
     * @throws Exception
     */
    public static byte[] decode(String base64) throws Exception {
        return Base64.decode(base64.getBytes());
    }

    /** */
    /**
     * <p>
     * 二进制数据编码为BASE64字符串
     * </p>
     *
     * @param bytes
     * @return
     * @throws Exception
     */
    public static String encode(byte[] bytes) throws Exception {
        return new String(Base64.encode(bytes));
    }

    /** */
    /**
     * <p>
     * 将文件编码为BASE64字符串
     * </p>
     * <p>
     * 大文件慎用,可能会导致内存溢出
     * </p>
     *
     * @param filePath
     *            文件绝对路径
     * @return
     * @throws Exception
     */
    public static String encodeFile(String filePath) throws Exception {
        byte[] bytes = fileToByte(filePath);
        return encode(bytes);
    }

    /** */
    /**
     * <p>
     * BASE64字符串转回文件
     * </p>
     *
     * @param filePath
     *            文件绝对路径
     * @param base64
     *            编码字符串
     * @throws Exception
     */
    public static void decodeToFile(String filePath, String base64)
            throws Exception {
        byte[] bytes = decode(base64);
        byteArrayToFile(bytes, filePath);
    }

    /** */
    /**
     * <p>
     * 文件转换为二进制数组
     * </p>
     *
     * @param filePath
     *            文件路径
     * @return
     * @throws Exception
     */
    public static byte[] fileToByte(String filePath) throws Exception {
        byte[] data = new byte[0];
        File file = new File(filePath);
        if (file.exists()) {
            FileInputStream in = new FileInputStream(file);
            ByteArrayOutputStream out = new ByteArrayOutputStream(2048);
            byte[] cache = new byte[CACHE_SIZE];
            int nRead = 0;
            while ((nRead = in.read(cache)) != -1) {
                out.write(cache, 0, nRead);
                out.flush();
            }
            out.close();
            in.close();
            data = out.toByteArray();
        }
        return data;
    }

    /** */
    /**
     * <p>
     * 二进制数据写文件
     * </p>
     *
     * @param bytes
     *            二进制数据
     * @param filePath
     *            文件生成目录
     */
    public static void byteArrayToFile(byte[] bytes, String filePath)
            throws Exception {
        InputStream in = new ByteArrayInputStream(bytes);
        File destFile = new File(filePath);
        if (!destFile.getParentFile().exists()) {
            destFile.getParentFile().mkdirs();
        }
        destFile.createNewFile();
        OutputStream out = new FileOutputStream(destFile);
        byte[] cache = new byte[CACHE_SIZE];
        int nRead = 0;
        while ((nRead = in.read(cache)) != -1) {
            out.write(cache, 0, nRead);
            out.flush();
        }
        out.close();
        in.close();
    }

}

4、下面是项目中世界运用

一、加密
			
 			//获取AES签名
            val aesKey = CipherUtils.AESUtils.getRandomString()
            //加密数据
            val data = CipherUtils.AESUtils.encrypt(aesKey, "需要加密的数据")
            //RSA加密
            val key = CipherUtils.RSAUtils.encrypt(aesKey, publicKey)
            //RSA签名
            val sign = CipherUtils.RSAUtils.sign(data, privateKey)
二、解密
				//RSA解密
                val aesKey = CipherUtils.RSAUtils.decrypt("后台返回的AES的key值", privateKey)
                //AES解密
                val data = CipherUtils.AESUtils.decrypt("后台返回的加密数据", aesKey)