用 CryptoJS 解密 AES/CBC/PKCS5Padding
Decrypt AES/CBC/PKCS5Padding with CryptoJS
我使用 Java javax.crypto
API 生成 128 位 AES/CBC/PKCS5Padding
密钥。这是我使用的算法:
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);
}
}
我可以通过这些方法使用 secretKey 轻松加密和解密消息。由于 Java 默认使用 128 位 AES 加密,它使用 SHA1 生成原始密钥的哈希值,并将哈希值的前 16 个字节用作 AES 中的密钥。然后它以 HEX 格式转储 IV 和密文。
例如 encryptAES("test", "test")
生成 CB5E759CE5FEAFEFCC9BABBFD84DC80C0291ED4917CF1402FF03B8E12716E44C
我想用 CryptoJS 解密这个密钥。
这是我的尝试:
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()
但是它 returns 是一个空字符串。
问题是您将 64 位密钥用作 128 位密钥。 Hashing.sha1().hashString(secretKey, Charsets.UTF_8)
是 HashCode
的实例,其 toString
方法描述如下:
Returns a string containing each byte of asBytes(), in order, as a two-digit unsigned hexadecimal number in lower case.
这是一个十六进制编码的字符串。如果您只取该字符串的 16 个字符并将其用作密钥,则您只有 64 位的熵,而不是 128 位。你真的应该直接使用 HashCode#asBytes()
。
无论如何,CryptoJS 代码的问题是多方面的:
- 密文必须是
CipherParams
对象,但如果它包含密文字节作为ciphertext
属性. 中的WordArray就足够了
- 密钥必须作为 WordArray 而不是字符串传入。否则,使用与 OpenSSL 兼容的 (EVP_BytesToKey) 密钥派生函数从字符串(假定为密码)派生密钥和 IV。
- 其他选项要么是不必要的,因为它们是默认值,要么是错误的,因为 blockSize 是以字而不是字节计算的。
这是与您损坏的 Java 代码兼容的 CryptoJS 代码:
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>
这是与固定 Java 代码兼容的 CryptoJS 代码:
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>
CryptoJS 中的等效加密代码如下所示:
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>
这个非常适合我
import * as CryptoJS from 'crypto-js';
const SECRET_CREDIT_CARD_KEY = '1231231231231231' // 16 digits key
decrypt(cipherText) {
const iv = CryptoJS.enc.Hex.parse(this.SECRET_CREDIT_CARD_KEY);
const key = CryptoJS.enc.Utf8.parse(this.SECRET_CREDIT_CARD_KEY);
const result = CryptoJS.AES.decrypt(cipherText, key,
{
iv,
mode: CryptoJS.mode.ECB,
}
)
const final = result.toString(CryptoJS.enc.Utf8)
return final
}
console.log(decrypt('your encrypted text'))
在 Angular 8 中使用此库
https://www.npmjs.com/package/crypto-js
我使用 Java javax.crypto
API 生成 128 位 AES/CBC/PKCS5Padding
密钥。这是我使用的算法:
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);
}
}
我可以通过这些方法使用 secretKey 轻松加密和解密消息。由于 Java 默认使用 128 位 AES 加密,它使用 SHA1 生成原始密钥的哈希值,并将哈希值的前 16 个字节用作 AES 中的密钥。然后它以 HEX 格式转储 IV 和密文。
例如 encryptAES("test", "test")
生成 CB5E759CE5FEAFEFCC9BABBFD84DC80C0291ED4917CF1402FF03B8E12716E44C
我想用 CryptoJS 解密这个密钥。
这是我的尝试:
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()
但是它 returns 是一个空字符串。
问题是您将 64 位密钥用作 128 位密钥。 Hashing.sha1().hashString(secretKey, Charsets.UTF_8)
是 HashCode
的实例,其 toString
方法描述如下:
Returns a string containing each byte of asBytes(), in order, as a two-digit unsigned hexadecimal number in lower case.
这是一个十六进制编码的字符串。如果您只取该字符串的 16 个字符并将其用作密钥,则您只有 64 位的熵,而不是 128 位。你真的应该直接使用 HashCode#asBytes()
。
无论如何,CryptoJS 代码的问题是多方面的:
- 密文必须是
CipherParams
对象,但如果它包含密文字节作为ciphertext
属性. 中的WordArray就足够了
- 密钥必须作为 WordArray 而不是字符串传入。否则,使用与 OpenSSL 兼容的 (EVP_BytesToKey) 密钥派生函数从字符串(假定为密码)派生密钥和 IV。
- 其他选项要么是不必要的,因为它们是默认值,要么是错误的,因为 blockSize 是以字而不是字节计算的。
这是与您损坏的 Java 代码兼容的 CryptoJS 代码:
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>
这是与固定 Java 代码兼容的 CryptoJS 代码:
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>
CryptoJS 中的等效加密代码如下所示:
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>
这个非常适合我
import * as CryptoJS from 'crypto-js';
const SECRET_CREDIT_CARD_KEY = '1231231231231231' // 16 digits key
decrypt(cipherText) {
const iv = CryptoJS.enc.Hex.parse(this.SECRET_CREDIT_CARD_KEY);
const key = CryptoJS.enc.Utf8.parse(this.SECRET_CREDIT_CARD_KEY);
const result = CryptoJS.AES.decrypt(cipherText, key,
{
iv,
mode: CryptoJS.mode.ECB,
}
)
const final = result.toString(CryptoJS.enc.Utf8)
return final
}
console.log(decrypt('your encrypted text'))
在 Angular 8 中使用此库 https://www.npmjs.com/package/crypto-js