问题描述
背景:我正在处理的应用程序应该可以脱机工作.我应该在java服务器端使用密码作为密钥来加密一些文本数据.加密的数据被传递到 HTML5 页面,在客户端使用 crypto-js 库,服务器加密的数据应该被解密.
Background:The application that I am working on is supposed to work offline. I should encrypt some text data using a password as a key at the java server side. The encrypted data is passed to the HTML5 page and at the client side using crypto-js library the server encrypted data should be decrypted.
我的问题:为了以客户端可以使用 crypt-js 解密的方式加密我的消息(使用用户输入的密码),我需要知道 crypto-js 在加密消息时期望的确切步骤.
My issue:In order to encrypt my message in such a way that the client can decrypt it with crypt-js (using a user entered password), I need to know the exact steps that crypto-js expects while encrypting a message.
我需要知道的:我有以下加密代码,它使用 crypto-js 在客户端对消息进行加密.
What I need to know:I have the following encryption code which does the encryption of a message at the client side using crypto-js.
var message = "my message text";
var password = "user password";
var encrypted = CryptoJS.AES.encrypt( message ,password );
console.log(encrypted.toString());
我需要知道 CryptoJS 在加密消息时使用的 AES 参数(不确定它们是什么,但听起来像:密钥大小(256)、填充(pkcs5)、模式(CBC)、PBE 算法(PBKDF2), salt (random), 迭代次数 (100)) .如果有人能证实,那将是一个很大的帮助......我最近几天一直在努力解开这个谜团?.
I need to know the AES parameters used by CryptoJS while encrypting a message(Not sure what they are, but it sounds like: key size (256), padding (pkcs5), mode (CBC), PBE algorithm (PBKDF2), salt (random), iteration count (100)) . It would be a great help if some one could confirm it...I been trying to solve this mystery for the last few days?.
我需要知道 CryptoJS 在 AES 加密消息时执行的不同步骤
I need to know the different steps performed by CryptoJS while AES encrypting a message
推荐答案
CryptoJS 使用 非标准化 OpenSSL KDF 进行密钥派生 (EvpKDF) 以 MD5 作为散列算法和 1 次迭代.IV 也是从密码派生的,这意味着在 Java 端解密它只需要实际的密文、密码和盐.
CryptoJS uses the non-standardized OpenSSL KDF for key derivation (EvpKDF) with MD5 as the hashing algorithm and 1 iteration. The IV is also derived from the password which means that only the actual ciphertext, the password and the salt are needed to decrypt this on Java side.
也就是说,在 CryptoJS 的密码模式下,PBKDF2 不用于密钥派生.默认情况下,AES-256 在 CBC 模式下使用 PKCS5 填充(这是 与 PKCS7 填充 相同).请记住,您可能需要 JCE Unlimited Strength Jurisdiction Policy Files.另请参阅 为什么存在限制关于使用超过特定长度的密钥进行加密?
In other words, PBKDF2 is not used for key derivation in password mode of CryptoJS. By default AES-256 is used in CBC mode with PKCS5 padding (which is the same as PKCS7 padding). Keep in mind that you might need the JCE Unlimited Strength Jurisdiction Policy Files. See also Why there are limitations on using encryption with keys beyond certain length?
以下代码在 Java 中重新创建了 KDF(keySize
和 ivSize
分别为 8,AES-256 为 4,来自 ).
The following code recreates the KDF in Java (keySize
and ivSize
are 8 respectively 4 for AES-256 and come from ).
public static byte[] evpKDF(byte[] password, int keySize, int ivSize, byte[] salt, int iterations, String hashAlgorithm, byte[] resultKey, byte[] resultIv) throws NoSuchAlgorithmException {
int targetKeySize = keySize + ivSize;
byte[] derivedBytes = new byte[targetKeySize * 4];
int numberOfDerivedWords = 0;
byte[] block = null;
MessageDigest hasher = MessageDigest.getInstance(hashAlgorithm);
while (numberOfDerivedWords < targetKeySize) {
if (block != null) {
hasher.update(block);
}
hasher.update(password);
block = hasher.digest(salt);
hasher.reset();
// Iterations
for (int i = 1; i < iterations; i++) {
block = hasher.digest(block);
hasher.reset();
}
System.arraycopy(block, 0, derivedBytes, numberOfDerivedWords * 4,
Math.min(block.length, (targetKeySize - numberOfDerivedWords) * 4));
numberOfDerivedWords += block.length/4;
}
System.arraycopy(derivedBytes, 0, resultKey, 0, keySize * 4);
System.arraycopy(derivedBytes, keySize * 4, resultIv, 0, ivSize * 4);
return derivedBytes; // key + iv
}
这里是完整的类供参考:
Here is the complete class for reference:
public class RecreateEVPkdfFromCryptoJS {
public static void main(String[] args) throws UnsupportedEncodingException, GeneralSecurityException {
String msg = "hello";
String password = "mypassword";
String ivHex = "aab7d6aca0cc6ffc18f9f5909753aa5f";
int keySize = 8; // 8 words = 256-bit
int ivSize = 4; // 4 words = 128-bit
String keyHex = "844a86d27d96acf3147aa460f535e20e989d1f8b5d79c0403b4a0f34cebb093b";
String saltHex = "ca35168ed6b82778";
String openSslFormattedCipherTextString = "U2FsdGVkX1/KNRaO1rgneK9S3zuYaYZcdXmVKJGqVqk=";
String cipherTextHex = "af52df3b9869865c7579952891aa56a9";
String padding = "PKCS5Padding";
byte[] key = hexStringToByteArray(keyHex);
byte[] iv = hexStringToByteArray(ivHex);
byte[] salt = hexStringToByteArray(saltHex);
byte[] cipherText = hexStringToByteArray(cipherTextHex);
byte[] javaKey = new byte[keySize * 4];
byte[] javaIv = new byte[ivSize * 4];
evpKDF(password.getBytes("UTF-8"), keySize, ivSize, salt, javaKey, javaIv);
System.out.println(Arrays.equals(key, javaKey) + " " + Arrays.equals(iv, javaIv));
Cipher aesCipherForEncryption = Cipher.getInstance("AES/CBC/PKCS5Padding"); // Must specify the mode explicitly as most JCE providers default to ECB mode!!
IvParameterSpec ivSpec = new IvParameterSpec(javaIv);
aesCipherForEncryption.init(Cipher.DECRYPT_MODE, new SecretKeySpec(javaKey, "AES"), ivSpec);
byte[] byteMsg = aesCipherForEncryption.doFinal(cipherText);
System.out.println(Arrays.equals(byteMsg, msg.getBytes("UTF-8")));
}
public static byte[] evpKDF(byte[] password, int keySize, int ivSize, byte[] salt, byte[] resultKey, byte[] resultIv) throws NoSuchAlgorithmException {
return evpKDF(password, keySize, ivSize, salt, 1, "MD5", resultKey, resultIv);
}
public static byte[] evpKDF(byte[] password, int keySize, int ivSize, byte[] salt, int iterations, String hashAlgorithm, byte[] resultKey, byte[] resultIv) throws NoSuchAlgorithmException {
int targetKeySize = keySize + ivSize;
byte[] derivedBytes = new byte[targetKeySize * 4];
int numberOfDerivedWords = 0;
byte[] block = null;
MessageDigest hasher = MessageDigest.getInstance(hashAlgorithm);
while (numberOfDerivedWords < targetKeySize) {
if (block != null) {
hasher.update(block);
}
hasher.update(password);
block = hasher.digest(salt);
hasher.reset();
// Iterations
for (int i = 1; i < iterations; i++) {
block = hasher.digest(block);
hasher.reset();
}
System.arraycopy(block, 0, derivedBytes, numberOfDerivedWords * 4,
Math.min(block.length, (targetKeySize - numberOfDerivedWords) * 4));
numberOfDerivedWords += block.length/4;
}
System.arraycopy(derivedBytes, 0, resultKey, 0, keySize * 4);
System.arraycopy(derivedBytes, keySize * 4, resultIv, 0, ivSize * 4);
return derivedBytes; // key + iv
}
/**
* Copied from http://stackoverflow.com/a/140861
* */
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
}
以及用于在 Java 代码中生成值的 JavaScript 代码:
and the JavaScript code which was used for the generation of the values in the Java code:
var msg = "hello";
var password = "mypassword"; // must be present on the server
var encrypted = CryptoJS.AES.encrypt( msg, password );
var ivHex = encrypted.iv.toString();
var ivSize = encrypted.algorithm.ivSize; // same as the blockSize
var keySize = encrypted.algorithm.keySize;
var keyHex = encrypted.key.toString();
var saltHex = encrypted.salt.toString(); // must be sent as well
var openSslFormattedCipherTextString = encrypted.toString(); // not used
var cipherTextHex = encrypted.ciphertext.toString(); // must be sent
这篇关于使用密码加密消息时,crypto-js 使用了哪些 AES 参数以及在内部执行的步骤是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!