javascript - 如何在 Java 中实现 CryptoJS.AES.encrypt 函数?
问题描述
我正在尝试在java中实现以下来自crypto-js的代码以进行加密
let toEncrypt= "my data";
cryptoJs.AES.encrypt(toEncrypt,"apasswordblabla").toString();
这是我的实现(AES/CBC/PKCS7Padding):
public String encrypt(Map<String,Object> param){
try {
String toEncrypt= objectMapper.writeValueAsString(param);
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] saltData = Arrays.copyOfRange(stringToEncrypt.getBytes(StandardCharsets.UTF_8), 8, 16);
final byte[][] keyAndIV = generateKeyAndIV(32, 16, 1, saltData, "apasswordblabla".getBytes(StandardCharsets.UTF_8), md5);
SecretKeySpec skeySpec = new SecretKeySpec(keyAndIV[0], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]);
Cipher cipher;
cipher = Cipher.getInstance("AES/CBC/PKCS7Padding",BouncyCastleProvider.PROVIDER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec,iv);
byte[] base64Encoded = Base64.getEncoder().encode(cipher.doFinal(toEncrypt.getBytes(StandardCharsets.UTF_8)));
return new String(base64Encoded);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException
| BadPaddingException | InvalidKeyException
| JsonProcessingException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
}
generateIV(int ,int ,int ,byte[] , byte[], MessageDigest) 的实现:
- 此方法等效于OpenSSL 的 EVP_BytesToKey函数,正如我在 cryptojs 源代码(文件 cipher-core.js 第 658 行上的 crypto-js@4.1.1)中看到的,AES 的默认格式化程序是 OpenSSLFormatter
public static byte[][] generateKeyAndIV(int keyLength, int ivLength, int iterations, byte[] salt, byte[] password, MessageDigest md) {
int digestLength = md.getDigestLength();
int requiredLength = (keyLength + ivLength + digestLength - 1) / digestLength * digestLength;
byte[] generatedData = new byte[requiredLength];
int generatedLength = 0;
try {
md.reset();
// Repeat process until sufficient data has been generated
while (generatedLength < keyLength + ivLength) {
// Digest data (last digest if available, password data, salt if available)
if (generatedLength > 0)
md.update(generatedData, generatedLength - digestLength, digestLength);
md.update(password);
if (salt != null)
md.update(salt, 0, 8);
md.digest(generatedData, generatedLength, digestLength);
// additional rounds
for (int i = 1; i < iterations; i++) {
md.update(generatedData, generatedLength, digestLength);
md.digest(generatedData, generatedLength, digestLength);
}
generatedLength += digestLength;
}
// Copy key and IV into separate byte arrays
byte[][] result = new byte[2][];
result[0] = Arrays.copyOfRange(generatedData, 0, keyLength);
if (ivLength > 0)
result[1] = Arrays.copyOfRange(generatedData, keyLength, keyLength + ivLength);
return result;
} catch (DigestException e) {
throw new RuntimeException(e);
} finally {
// Clean out temporary data
Arrays.fill(generatedData, (byte)0);
}
}
然后我尝试在这里 使用解密它
let toDecrypt="[MY ENCRYPTED DATA IN BASE64]"
let decrypted = cryptoJs.AES.decrypt(toDecrypt, "apasswordblabla").toString(cryptoJs.enc.Utf8).toString();
console.log(decrypted);
而且它总是无法解密,并出现错误"Malformed utf-8 data"。因此,我的 java 实现是错误的
我在这里做错了什么?或者如果有任何现成的库来解决这个问题,请建议
解决方案
OpenSSL 和 CryptoJS 使用的格式原来是base64("Salted__" + <salt 8 bytes> + <encrypted data>)
这是产生这种格式结果的代码(generateKeyAndIV
从上面重用方法,无需任何修改):
public static void main(String args[]) {
Security.addProvider(new BouncyCastleProvider());
System.out.println(encrypt());
}
public static String encrypt() {
try {
String stringToEncrypt = "Hello world 12345";
String password = "apasswordblabla";
SecureRandom sr = new SecureRandom();
byte[] salt = new byte[8];
sr.nextBytes(salt);
final byte[][] keyAndIV = generateKeyAndIV(32, 16, 1, salt, password.getBytes(StandardCharsets.UTF_8),
MessageDigest.getInstance("MD5"));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", BouncyCastleProvider.PROVIDER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyAndIV[0], "AES"), new IvParameterSpec(keyAndIV[1]));
byte[] encryptedData = cipher.doFinal(stringToEncrypt.getBytes(StandardCharsets.UTF_8));
byte[] prefixAndSaltAndEncryptedData = new byte[16 + encryptedData.length];
// Copy prefix (0-th to 7-th bytes)
System.arraycopy("Salted__".getBytes(StandardCharsets.UTF_8), 0, prefixAndSaltAndEncryptedData, 0, 8);
// Copy salt (8-th to 15-th bytes)
System.arraycopy(salt, 0, prefixAndSaltAndEncryptedData, 8, 8);
// Copy encrypted data (16-th byte and onwards)
System.arraycopy(encryptedData, 0, prefixAndSaltAndEncryptedData, 16, encryptedData.length);
return Base64.getEncoder().encodeToString(prefixAndSaltAndEncryptedData);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
产生的示例结果是:U2FsdGVkX1/O8jV2bfcM/06DM106oLAzdf7z66/JakGNefts4MftzXquopkxPaDo
使用此代码在 JSFiddle 中解码:
console.log(CryptoJS.AES.decrypt("U2FsdGVkX1/O8jV2bfcM/06DM106oLAzdf7z66/JakGNefts4MftzXquopkxPaDo", "apasswordblabla").toString(CryptoJS.enc.Utf8));
在浏览器控制台中产生所需的输出:
Hello world 12345
推荐阅读
- swift4 - 数组(字符串内部)转换,Swift 4
- sql - 在特定列 postgres 中选择具有最大值的组中的行
- java - 在检查数据类型时用 Java 解析数据
- reactjs - 用嵌套的变量反应集合状态
- python - [Flask - ElasticBeansatalk]错误:chown /var/app/staging/himasui_app/himasui_venv/bin/python:没有这样的文件或目录
- python - Download dependencies using serverless-python-requirements plugin on GitHub Actions throws "cannot find Python 3.7"
- javascript - 将子数组连接到父数组
- jquery - 使用 bootstrap4-toggle 和 localStorage 为深色模式切换 CSS
- javascript - 如果 Javascript 中的键相同,如何比较两个不同的对象键并更新它的值?
- jquery - 在 iframe 中获取 Visio SharePoint WebPart 的 css