HarmonyOS 鸿蒙Next ArkTs使用CryptoJS开源库的AES加密结果与原生不一致
HarmonyOS 鸿蒙Next ArkTs使用CryptoJS开源库的AES加密结果与原生不一致 想请教下各位关于加解密的方面,为什么两端加密结果不一致,寻求各位大神指点!Harmony这边的代码逻辑是:
try {
let key = CryptoJS.enc.Utf8.parse('fxkyVote2023#!05');
let iv = CryptoJS.enc.Utf8.parse('0000000000000000');
let encrypted = CryptoJS.AES.encrypt('hello', key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}).toString()
// 转换为字符串(Base64编码)
let encryptedString = encrypted.toString();
console.log(encryptedString)// 加密结果为:y0JqIt6SSw7stjXc6qw9Rw==
} catch (e) {
console.log("")
}
Android原生这边加使用的填充模式为AES/CBC/PKCS7Padding
加盐为:private static final byte[] ivBytes = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
密结果为:gOWnT8QIub2Ej3AhxxEDZw==
代码如下:
public final class AESCrypt {
private static final String TAG = "AESCrypt";
//AESCrypt-ObjC uses CBC and PKCS7Padding
private static final String AES_MODE = "AES/CBC/PKCS7Padding";
private static final String CHARSET = "UTF-8";
//AESCrypt-ObjC uses SHA-256 (and so a 256-bit key)
private static final String HASH_ALGORITHM = "SHA-256";
//AESCrypt-ObjC uses blank IV (not the best security, but the aim here is compatibility)
private static final byte[] ivBytes = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
//togglable log option (please turn off in live!)
public static boolean DEBUG_LOG_ENABLED = false;
/**
* Generates SHA256 hash of the password which is used as key
*
* @param password used to generated key
* @return SHA256 of the password
*/
private static SecretKeySpec generateKey(final String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {
final MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
byte[] bytes = password.getBytes("UTF-8");
digest.update(bytes, 0, bytes.length);
byte[] key = digest.digest();
log("SHA-256 key ", key);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
return secretKeySpec;
}
/**
* Encrypt and encode message using 256-bit AES with key generated from password.
*
* @param password used to generated key
* @param message the thing you want to encrypt assumed String UTF-8
* @return Base64 encoded CipherText
* @throws GeneralSecurityException if problems occur during encryption
*/
public static String encrypt(final String password, String message)
throws GeneralSecurityException {
try {
final SecretKeySpec key = generateKey(password);
log("message", message);
byte[] cipherText = encrypt(key, ivBytes, message.getBytes(CHARSET));
//NO_WRAP is important as was getting \n at the end
String encoded = Base64.encodeToString(cipherText, Base64.NO_WRAP);
log("Base64.NO_WRAP", encoded);
return encoded;
} catch (UnsupportedEncodingException e) {
if (DEBUG_LOG_ENABLED)
Loger.e(TAG, "UnsupportedEncodingException ", e);
throw new GeneralSecurityException(e);
}
}
/**
* More flexible AES encrypt that doesn't encode
*
* @param key AES key typically 128, 192 or 256 bit
* @param iv Initiation Vector
* @param message in bytes (assumed it's already been decoded)
* @return Encrypted cipher text (not encoded)
* @throws GeneralSecurityException if something goes wrong during encryption
*/
public static byte[] encrypt(final SecretKeySpec key, final byte[] iv, final byte[] message)
throws GeneralSecurityException {
final Cipher cipher = Cipher.getInstance(AES_MODE);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] cipherText = cipher.doFinal(message);
log("cipherText", cipherText);
return cipherText;
}
/**
* Decrypt and decode ciphertext using 256-bit AES with key generated from password
*
* @param password used to generated key
* @param base64EncodedCipherText the encrpyted message encoded with base64
* @return message in Plain text (String UTF-8)
* @throws GeneralSecurityException if there's an issue decrypting
*/
public static String decrypt(final String password, String base64EncodedCipherText)
throws GeneralSecurityException {
try {
final SecretKeySpec key = generateKey(password);
log("base64EncodedCipherText", base64EncodedCipherText);
byte[] decodedCipherText = Base64.decode(base64EncodedCipherText, Base64.NO_WRAP);
log("decodedCipherText", decodedCipherText);
byte[] decryptedBytes = decrypt(key, ivBytes, decodedCipherText);
log("decryptedBytes", decryptedBytes);
String message = new String(decryptedBytes, CHARSET);
log("message", message);
return message;
} catch (UnsupportedEncodingException e) {
if (DEBUG_LOG_ENABLED)
Loger.e(TAG, "UnsupportedEncodingException ", e);
throw new GeneralSecurityException(e);
}
}
/**
* More flexible AES decrypt that doesn't encode
*
* @param key AES key typically 128, 192 or 256 bit
* @param iv Initiation Vector
* @param decodedCipherText in bytes (assumed it's already been decoded)
* @return Decrypted message cipher text (not encoded)
* @throws GeneralSecurityException if something goes wrong during encryption
*/
public static byte[] decrypt(final SecretKeySpec key, final byte[] iv, final byte[] decodedCipherText)
throws GeneralSecurityException {
final Cipher cipher = Cipher.getInstance(AES_MODE);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] decryptedBytes = cipher.doFinal(decodedCipherText);
log("decryptedBytes", decryptedBytes);
return decryptedBytes;
}
private static void log(String what, byte[] bytes) {
if (DEBUG_LOG_ENABLED)
Loger.d(TAG, what + "[" + bytes.length + "] [" + bytesToHex(bytes) + "]");
}
private static void log(String what, String value) {
if (DEBUG_LOG_ENABLED)
Loger.d(TAG, what + "[" + value.length() + "] [" + value + "]");
}
/**
* Converts byte array to hexidecimal useful for logging and fault finding
*
* @param bytes
* @return
*/
private static String bytesToHex(byte[] bytes) {
final char[] hexArray = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'A', 'B', 'C', 'D', 'E', 'F'};
char[] hexChars = new char[bytes.length * 2];
int v;
for (int j = 0; j < bytes.length; j++) {
v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
private AESCrypt() {
}
}
//////////////////////调用方式:
try {
String sign = AESCrypt.encrypt("fxkyVote2023#!05", "hello");
Log.d("", sign);
// 输出结果为:gOWnT8QIub2Ej3AhxxEDZw==
} catch (Exception e) {
e.printStackTrace();
}
更多关于HarmonyOS 鸿蒙Next ArkTs使用CryptoJS开源库的AES加密结果与原生不一致的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
该问题已解决,并含工具类源码(Harmony原生API实现),帖子为:https://developer.huawei.com/consumer/cn/forum/topic/0202156009282962090?fid=0101587866109860105
更多关于HarmonyOS 鸿蒙Next ArkTs使用CryptoJS开源库的AES加密结果与原生不一致的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
排查发现,JAVA端的代码,生成key的流程是:
- 将key转为长度为32位的字节数组
- 再将第一步的字节数组update为SHA-256类型的字节数组(32字节,并非64字节)
- 最后生成AES的key
目前Harmony这边第一步正常,卡在了第二步,不知Harmony这边如何实现,求大佬指点。
向量不对吧?0在Uint8Array里面是0x30而不是0x00
您好,根据当前代码,能提供下修改建议吗?
let iv = CryptoJS.enc.Utf8.parse(new Array(16).fill(String.fromCharCode(0)).join(’’));
感谢答复。我刚发现Android原生这边的AES加密生成的密钥是32byte的,并非16byte,我感觉和他有关系,正在排查。
在HarmonyOS(鸿蒙)Next ArkTs环境中使用CryptoJS开源库的AES加密结果与原生不一致的问题,可能源于以下几个因素:
-
密钥管理:确保在ArkTs和原生环境中使用的密钥完全一致,包括密钥的长度、格式以及任何可能的填充或截断处理。
-
加密模式与填充方式:检查AES加密使用的模式(如CBC、ECB等)和填充方式(如PKCS7、NoPadding等)是否一致。不同的模式或填充方式会导致加密结果不同。
-
初始化向量(IV):如果使用的是CBC等需要IV的模式,确保IV在ArkTs和原生环境中相同,且正确传递。
-
数据编码:加密前的数据编码方式(如Base64、Hex等)应保持一致,编码不一致也会影响最终加密结果。
-
库版本差异:CryptoJS开源库的不同版本可能存在算法实现上的差异,确保ArkTs中使用的CryptoJS版本与原生环境中使用的版本一致。
-
环境差异:鸿蒙Next ArkTs环境与原生环境可能存在底层实现上的差异,这可能导致即使是相同的输入和配置,加密结果也会有所不同。
如果以上因素均已确认无误,但问题依旧存在,请考虑查看CryptoJS在ArkTs中的具体实现细节,或检查是否有已知的兼容性问题。如果问题依旧没法解决请联系官网客服, 官网地址是 https://www.itying.com/category-93-b0.html,