欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android Java iOS 三端RSA和AES加密处理

程序员文章站 2024-03-14 15:34:10
...

Android Java iOS 三端RSA和AES加密处理

  • Github

  • CSDN

  • 资源地址

  • 之前写的一篇博客关于 RSA和AES双向加密(Android 和 Java) 当时没有考虑iOS端,之后在协调过程中在AES解密上出现问题,用这种方式互相解出来的不一致,导致解密失败

  • 查阅资料发现 AES加密时不能用KeyGenerator、SecureRandom、SecretKey 生成,需要自己写一个方法生成16位的字符串

  • 还需要指定加密的算法、工作模式和填充方式

  • 需要自己生成AES**和AES偏移量

  • 最后放上代码


 	private static final String AES = "AES";
    private static final String STRING_FORMAT = "UTF-8";
    private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
    public static String CRYPT_KEY = getRandomString(16);
    public static String IV_STRING = getRandomString(16);

    /**
     * 加密
     *
     * @param aesKey  AESkey
     * @param ivKey   AES偏移量
     * @param content 明文
     * @return 密文
     */
    public static String encrypt(String aesKey, String ivKey, String content) {
        try {
            byte[] byteContent = content.getBytes(STRING_FORMAT);
            // 注意,为了能与 iOS 统一 这里的 key 不可以使用 KeyGenerator、SecureRandom、SecretKey 生成
            byte[] enCodeFormat = aesKey.getBytes();
            SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, AES);
            byte[] initParam = ivKey.getBytes();
            IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
            // 指定加密的算法、工作模式和填充方式
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
            byte[] encryptedBytes = cipher.doFinal(byteContent);
            // 同样对加密后数据进行 base64 编码
            return Base64Util.byte2Base64(encryptedBytes);
        } catch (Exception e) {
            return "";
        }
    }

    /**
     * 解密
     *
     * @param aesKey  AESkey
     * @param ivKey   AES偏移量
     * @param content 密文
     * @return 解密后的明文
     */
    public static String decrypt(String aesKey, String ivKey, String content) {
        try {
            byte[] encryptedBytes = Base64Util.base642Byte(content);
            byte[] enCodeFormat = aesKey.getBytes();
            SecretKeySpec secretKey = new SecretKeySpec(enCodeFormat, AES);
            byte[] initParam = ivKey.getBytes();
            IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
            byte[] result = cipher.doFinal(encryptedBytes);
            return new String(result, STRING_FORMAT);
        } catch (Exception e) {
            return "";
        }
    }

    /**
     * 随机生成字符串
     *
     * @param length 需要生成的位数
     * @return String
     */
    public static String getRandomString(int length) {
        String base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }

与RSA结合使用方法

/**
 * APP端加密请求内容
 *
 * @param content 需要加密的内容
 * @return 返回map
 * @throws Exception
 */
public static Map<String, String> appEncrypt1(String content) throws Exception {
    LinkedHashMap<String, String> map = new LinkedHashMap<>();
    //将Base64编码后的(server端)公钥转换成PublicKey对象
    PublicKey serverPublicKey = RSAUtil.string2PublicKey(KeyUtil.SERVER_PUBLIC_KEY.replaceAll("\n", ""));
    //随机生成AES秘钥 AES偏移量
    String aesKeyStr = AESUtil2.CRYPT_KEY;
    String ivKeyStr = AESUtil2.IV_STRING;
    //用(server端)公钥加密AES秘钥 AES偏移量
    byte[] encryptAesKey = RSAUtil.publicEncrypt(aesKeyStr.getBytes("utf-8"), serverPublicKey);
    byte[] encryptAesIvKey = RSAUtil.publicEncrypt(ivKeyStr.getBytes("utf-8"), serverPublicKey);
    //用AES秘钥加密请求内容
    String encryptContentRequest = AESUtil2.encrypt(aesKeyStr, ivKeyStr, content);
    map.put("ak", Base64Util.byte2Base64(encryptAesKey));//加密后的aesKey
    map.put("iv", Base64Util.byte2Base64(encryptAesIvKey));//加密后的AES偏移量
    map.put("data", encryptContentRequest);//加密内容
    LogUtils.e("----AES加密秘钥 ----" + aesKeyStr);
    LogUtils.e("----AES加密秘钥偏移量 ----" + ivKeyStr);
    LogUtils.e("----AES加密数据 ---- ak==" + Base64Util.byte2Base64(encryptAesKey));
    LogUtils.e("----AES加密数据 ---- iv==" + Base64Util.byte2Base64(encryptAesIvKey));
    LogUtils.e("----AES加密数据 ---- data==" + encryptContentRequest);
    return map;
}


/**
 * APP解密服务器的响应内容
 *
 * @param content 需要解密的内容
 * @return 明文
 * @throws Exception
 */
public static String appDecrypt1(String content) throws Exception {
    JSONObject result = new JSONObject(content);
    String decryptAesKey = (String) result.get("ak");//加密后的aesKey
    String decryptAesIvKey = (String) result.get("iv");//加密后的AES偏移量
    String decryptContent = (String) result.get("data");//内容
    //将Base64编码后的APP私钥转换成PrivateKey对象
    PrivateKey appPrivateKey = RSAUtil.string2PrivateKey(KeyUtil.APP_PRIVATE_KEY.replace("\n", ""));
    //用APP私钥解密AES秘钥 AES偏移量
    byte[] aesKeyBytes = RSAUtil.privateDecrypt(Base64Util.base642Byte(decryptAesKey), appPrivateKey);
    byte[] aesIvKeyBytes = RSAUtil.privateDecrypt(Base64Util.base642Byte(decryptAesIvKey), appPrivateKey);
    //用AES秘钥解密请求内容
    String aesKey = new String(aesKeyBytes);
    String aesIvKey = new String(aesIvKeyBytes);
    String response = AESUtil2.decrypt(aesKey, aesIvKey, decryptContent);
    LogUtils.e("----AES解密秘钥 ----" + aesKey + "--- aesIvKey ---" + aesIvKey + "--- response ---" + response);
    return response;
}

在代码加密过程后,在retrofit设置拦截器里要替换为加密后的数据,由于默认会转义特殊字符,导致后台无法解密数据,报错,解决的方法如下:

  • FormBody.Builder 在加参数时 builder.addEncoded(URLEncoder.encode(key, “UTF-8”), URLEncoder.encode(value, “UTF-8”));

 private final Interceptor mInterceptor = new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            Request.Builder newBuilder = request.newBuilder();
            RequestBody requestBody = request.body();
            if (HTTP_SWITCH) {
                //接口加密处理
                JSONObject jsonObject = new JSONObject();
                try {
                    jsonObject.put("curLoginAcc", StringUtils.null2Length0(UserPreferences.getInstance(MyApplication.getContext()).getUserAccount()));
                    FormBody body = (FormBody) requestBody;
                    if (body != null && body.size() > 0) {
                        for (int i = 0; i < body.size(); i++) {
                            jsonObject.put(body.encodedName(i), body.encodedValue(i));
                        }
                    }
                    Map<String, String> map = HttpEncryptUtil.appEncrypt1(jsonObject.toString());
                    FormBody.Builder builder = new FormBody.Builder();
                    if (map != null && !map.isEmpty()) {
                        Set<Map.Entry<String, String>> entrySet = map.entrySet();
                        if (entrySet != null && entrySet.size() > 0) {
                            Iterator<Map.Entry<String, String>> iterator = entrySet.iterator();
                            while (iterator.hasNext()) {
                                Map.Entry<String, String> entry = iterator.next();
                                if (entry != null) {
                                    String key = entry.getKey();
                                    String value = entry.getValue();
                        
									//处理转义的问题关键在这 URLEncoder.encode()

                                    builder.addEncoded(URLEncoder.encode(key, "UTF-8"), URLEncoder.encode(value, "UTF-8"));
                                }
                            }

                        }
                    }
                    newBuilder.method(request.method(), builder.build());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                if (requestBody instanceof FormBody) {
                    FormBody.Builder builder = new FormBody.Builder();
                    FormBody body = (FormBody) requestBody;
                    if (body != null && body.size() > 0) {
                        for (int i = 0; i < body.size(); i++) {
                            builder.addEncoded(body.encodedName(i), body.encodedValue(i));
                        }
                    }
                    //加公共参数
                    builder.add("curLoginAcc", StringUtils.null2Length0(UserPreferences.getInstance(MyApplication.getContext()).getUserAccount()));
                    newBuilder.method(request.method(), builder.build());
                }
            }
            Request build = newBuilder.build();
            return chain.proceed(build);
        }
    };