问题
I generate 128bit AES/CBC/PKCS5Padding
key using Java javax.crypto
API. Here is the algorithm that I use:
public static String encryptAES(String data, String secretKey) {
try {
byte[] secretKeys = Hashing.sha1().hashString(secretKey, Charsets.UTF_8)
.toString().substring(0, 16)
.getBytes(Charsets.UTF_8);
final SecretKey secret = new SecretKeySpec(secretKeys, "AES");
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
final AlgorithmParameters params = cipher.getParameters();
final byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
final byte[] cipherText = cipher.doFinal(data.getBytes(Charsets.UTF_8));
return DatatypeConverter.printHexBinary(iv) + DatatypeConverter.printHexBinary(cipherText);
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
public static String decryptAES(String data, String secretKey) {
try {
byte[] secretKeys = Hashing.sha1().hashString(secretKey, Charsets.UTF_8)
.toString().substring(0, 16)
.getBytes(Charsets.UTF_8);
// grab first 16 bytes - that's the IV
String hexedIv = data.substring(0, 32);
// grab everything else - that's the cipher-text (encrypted message)
String hexedCipherText = data.substring(32);
byte[] iv = DatatypeConverter.parseHexBinary(hexedIv);
byte[] cipherText = DatatypeConverter.parseHexBinary(hexedCipherText);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secretKeys, "AES"), new IvParameterSpec(iv));
return new String(cipher.doFinal(cipherText), Charsets.UTF_8);
} catch (BadPaddingException e) {
throw new IllegalArgumentException("Secret key is invalid");
}catch (Exception e) {
throw Throwables.propagate(e);
}
}
I can easily encrypt and decrypt messages using secretKey with these methods. Since Java has 128bit AES encryption by default, it generates a hash of the original secret key with SHA1 and takes the first 16-bytes of the hash to use it as secret key in AES. Then it dumps the IV and cipherText in HEX format.
For example encryptAES("test", "test")
generates CB5E759CE5FEAFEFCC9BABBFD84DC80C0291ED4917CF1402FF03B8E12716E44C
and I want to decrypt this key with CryptoJS.
Here is my attempt:
var str = 'CB5E759CE5FEAFEFCC9BABBFD84DC80C0291ED4917CF1402FF03B8E12716E44C';
CryptJS.AES.decrypt(
CryptJS.enc.Hex.parse(str.substring(32)),
CryptJS.SHA1("test").toString().substring(0,16),
{
iv: CryptJS.enc.Hex.parse(str.substring(0,32)),
mode: CryptJS.mode.CBC,
formatter: CryptJS.enc.Hex,
blockSize: 16,
padding: CryptJS.pad.Pkcs7
}).toString()
However it returns an empty string.
回答1:
The problem is that you're using a 64 bit key as a 128 bit. Hashing.sha1().hashString(secretKey, Charsets.UTF_8)
is an instance of HashCode and its toString
method is described as such:
Returns a string containing each byte of asBytes(), in order, as a two-digit unsigned hexadecimal number in lower case.
It is a Hex-encoded string. If you take only 16 characters of that string and use it as a key, you only have 64 bits of entropy and not 128 bits. You really should be using HashCode#asBytes()
directly.
Anyway, the problem with the CryptoJS code is manyfold:
- The ciphertext must be a
CipherParams
object, but it is enough if it contains the ciphertext bytes as a WordArray in theciphertext
property. - The key must be passed in as a WordArray instead of a string. Otherwise, an OpenSSL-compatible (EVP_BytesToKey) key derivation function is used to derive the key and IV from the string (assumed to be a password).
- Additional options are either unnecessary, because they are defaults, or they are wrong, because the blockSize is calculated in words and not bytes.
Here is CryptoJS code that is compatible with your broken Java code:
var str = 'CB5E759CE5FEAFEFCC9BABBFD84DC80C0291ED4917CF1402FF03B8E12716E44C';
console.log("Result: " + CryptoJS.AES.decrypt({
ciphertext: CryptoJS.enc.Hex.parse(str.substring(32))
}, CryptoJS.enc.Utf8.parse(CryptoJS.SHA1("test").toString().substring(0,16)),
{
iv: CryptoJS.enc.Hex.parse(str.substring(0,32)),
}).toString(CryptoJS.enc.Utf8))
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/sha1.js"></script>
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script>
Here is CryptoJS code that is compatible with the fixed Java code:
var str = 'F6A5230232062D2F0BDC2080021E997C6D07A733004287544C9DDE7708975525';
console.log("Result: " + CryptoJS.AES.decrypt({
ciphertext: CryptoJS.enc.Hex.parse(str.substring(32))
}, CryptoJS.enc.Hex.parse(CryptoJS.SHA1("test").toString().substring(0,32)),
{
iv: CryptoJS.enc.Hex.parse(str.substring(0,32)),
}).toString(CryptoJS.enc.Utf8))
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/sha1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>
The equivalent encryption code in CryptoJS would look like this:
function encrypt(plaintext, password){
var iv = CryptoJS.lib.WordArray.random(128/8);
var key = CryptoJS.enc.Hex.parse(CryptoJS.SHA1(password).toString().substring(0,32));
var ct = CryptoJS.AES.encrypt(plaintext, key, { iv: iv });
return iv.concat(ct.ciphertext).toString();
}
console.log("ct: " + encrypt("plaintext", "test"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/sha1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>
来源:https://stackoverflow.com/questions/37368710/decrypt-aes-cbc-pkcs5padding-with-cryptojs