You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android(API19+)实现与CryptoJS兼容的AES加解密功能

Android AES加解密与JavaScript CryptoJS实现对齐修复

问题背景

现有JavaScript AES加解密函数(注:加密和解密密钥不同,服务端已适配):

function encryptData(data_to_encrypt){
    var key = CryptoJS.enc.Hex.parse('0123456789abcdef0123456789abcdef');
    var iv  = CryptoJS.enc.Hex.parse('abcdef9876543210abcdef9876543210');
    var encrypted = CryptoJS.AES.encrypt((data_to_encrypt), key, { iv: iv });
    var encrypted_data = encrypted.ciphertext.toString(CryptoJS.enc.Base64);
    return encrypted_data;
}

function decryptData(data_to_decrypt){
    var keyBase64 = CryptoJS.enc.Base64.parse("ITU2NjNhI0tOc2FmZExOTQ==");
    var iv1 = CryptoJS.enc.Base64.parse('AAAAAAAAAAAAAAAAAAAAAA==');
    var bytes = CryptoJS.AES.decrypt(data_to_decrypt, keyBase64, {iv: iv1}, {mode: CryptoJS.mode.CBC});
    var plaintext = bytes.toString(CryptoJS.enc.Utf8);
    return plaintext;
}

在Android API19+中实现对等功能时,编写的代码抛出错误:

javax.crypto.IllegalBlockSizeException: error:1e00007b:Cipher functions:OPENSSL_internal:WRONG_FINAL_BLOCK_LENGTH

原Android测试代码:

String strToEncrypt = "hello world. i am new bee.";
try {
    String strEncrypted = openssl_encrypt(strToEncrypt, "0123456789abcdef0123456789abcdef", "abcdef9876543210abcdef9876543210");
    String strDecrypted = openssl_decrypt(strEncrypted, "0123456789abcdef0123456789abcdef", "abcdef9876543210abcdef9876543210");
    System.out.println(strDecrypted);
} catch (Exception e) {
    e.printStackTrace();
}

private String openssl_encrypt(String data, String strKey, String strIv) throws Exception {
    Cipher ciper = Cipher.getInstance("AES/CBC/PKCS5Padding");
    SecretKeySpec key = new SecretKeySpec(strKey.getBytes(), "AES");
    IvParameterSpec iv = new IvParameterSpec(strIv.getBytes(), 0, ciper.getBlockSize());

    ciper.init(Cipher.ENCRYPT_MODE, key, iv);
    byte[] encryptedCiperBytes = ciper.doFinal(data.getBytes());

    String s = new String(encryptedCiperBytes);
    System.out.println("Ciper : " + s);
    return s;
}

private String openssl_decrypt(String input, String strKey, String strIv) {
    String s = null;
    try {
        Cipher ciper = Cipher.getInstance("AES/CBC/PKCS5Padding");

        SecretKeySpec key = new SecretKeySpec(strKey.getBytes(), "AES");
        IvParameterSpec iv = new IvParameterSpec(strIv.getBytes(), 0, ciper.getBlockSize());

        ciper.init(Cipher.DECRYPT_MODE, key, iv);
        byte[] encryptedCiperBytes = ciper.doFinal(input.getBytes());

        s = new String(encryptedCiperBytes);
        System.out.println("Ciper : " + s);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return s;
}

错误原因分析

  1. 密钥/IV解析错误:JavaScript中加密用Hex.parse解析密钥和IV,Android代码直接取字符串字节,未做Hex解码;解密时JS用Base64.parse解析密钥和IV,原代码同样未做Base64解码。
  2. 加密输出格式错误:JS加密后将密文转为Base64字符串返回,原Android代码直接将加密字节转成普通字符串,会导致字节丢失/乱码,解密时无法正确解析。
  3. 解密输入处理错误:解密时需先将Base64密文解码为字节数组,原代码直接将加密后的字符串转字节,不符合要求。

修复后的Android实现代码

import android.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

// 对应JavaScript的encryptData函数
public String encryptData(String dataToEncrypt) throws Exception {
    // 密钥:Hex字符串转字节数组
    String hexKey = "0123456789abcdef0123456789abcdef";
    byte[] keyBytes = hexStringToByteArray(hexKey);
    // IV:Hex字符串转字节数组
    String hexIv = "abcdef9876543210abcdef9876543210";
    byte[] ivBytes = hexStringToByteArray(hexIv);

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
    IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);

    cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
    byte[] encryptedBytes = cipher.doFinal(dataToEncrypt.getBytes("UTF-8"));
    // 密文转Base64,和JS对齐
    return Base64.encodeToString(encryptedBytes, Base64.NO_WRAP);
}

// 对应JavaScript的decryptData函数
public String decryptData(String dataToDecrypt) throws Exception {
    // 密钥:Base64字符串转字节数组
    String base64Key = "ITU2NjNhI0tOc2FmZExOTQ==";
    byte[] keyBytes = Base64.decode(base64Key, Base64.NO_WRAP);
    // IV:Base64字符串转字节数组
    String base64Iv = "AAAAAAAAAAAAAAAAAAAAAA==";
    byte[] ivBytes = Base64.decode(base64Iv, Base64.NO_WRAP);

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
    IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);

    cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
    // 先将Base64密文解码为字节数组
    byte[] encryptedBytes = Base64.decode(dataToDecrypt, Base64.NO_WRAP);
    byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
    return new String(decryptedBytes, "UTF-8");
}

// Hex字符串转字节数组,兼容API19
private byte[] hexStringToByteArray(String hexStr) {
    int len = hexStr.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2) {
        data[i / 2] = (byte) ((Character.digit(hexStr.charAt(i), 16) << 4)
                + Character.digit(hexStr.charAt(i+1), 16));
    }
    return data;
}

// 测试代码
public void testEncryption() {
    String original = "hello world. i am new bee.";
    try {
        String encrypted = encryptData(original);
        System.out.println("加密结果:" + encrypted);
        // 注意:加密和解密密钥不同,需用JS encryptData生成的密文测试decryptData
        String decrypted = decryptData(encrypted);
        System.out.println("解密结果:" + decrypted);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

关键说明

  • 密钥/IV解析:严格对齐JS的解析方式,加密用Hex转字节,解密用Base64转字节,避免直接取字符串字节。
  • 编码一致性:所有字符串转字节均指定UTF-8编码,避免平台默认编码差异。
  • Base64处理:使用Android自带的Base64类,指定NO_WRAP参数,和CryptoJS的Base64输出格式对齐(无换行)。
  • API兼容性:hexStringToByteArray为自定义实现,兼容API19及以上版本,无需依赖高版本API。

内容的提问来源于stack exchange,提问作者sifr_dot_in

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 12:57:38