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; }
错误原因分析
- 密钥/IV解析错误:JavaScript中加密用
Hex.parse解析密钥和IV,Android代码直接取字符串字节,未做Hex解码;解密时JS用Base64.parse解析密钥和IV,原代码同样未做Base64解码。 - 加密输出格式错误:JS加密后将密文转为Base64字符串返回,原Android代码直接将加密字节转成普通字符串,会导致字节丢失/乱码,解密时无法正确解析。
- 解密输入处理错误:解密时需先将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
相关产品推荐
相关产品推荐

