簡體   English   中英

使用 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 中的密鑰。 然后它以十六進制格式轉儲 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()

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

問題是您將 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>

這個非常適合我

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 中使用這個庫

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM