用CryptoJS解密AES / CBC / PKCS5Padding

时间:2016-05-21 22:42:12

标签: java cryptography aes cryptojs javax.crypto

我使用Java AES/CBC/PKCS5Padding API生成128位javax.crypto密钥。这是我使用的算法:

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和cipherText。

例如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()

但是它返回一个空字符串。

2 个答案:

答案 0 :(得分:5)

问题是您使用64位密钥作为128位。 Hashing.sha1().hashString(secretKey, Charsets.UTF_8)HashCode的一个实例,其toString方法描述如下:

  

按顺序返回包含asBytes()每个字节的字符串,作为小写的两位无符号十六进制数。

这是一个十六进制编码的字符串。如果只使用该字符串的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>

答案 1 :(得分:0)

这个完美地适合我

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