【问题标题】:How to decode data encrypted using AES-256 in CBC mode and PKCS7 padding?如何在 CBC 模式和 PKCS7 填充中解码使用 AES-256 加密的数据?
【发布时间】:2020-09-17 13:41:22
【问题描述】:

我们正在使用下面的 java 代码来解密在 CBC 模式下使用 AES-256 和 PKCS7 填充加密的数据。

Java 代码:

import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import java.security.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;


public class AES256 {

    private static byte[] initVector = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

    public String decrypt (String encryptedDataBase64, String keyBase64)
    {
       try {
           Security.setProperty("crypto.policy", "unlimited");
           IvParameterSpec ivSpec = new IvParameterSpec(initVector);  // Get the init vector

           // Get the Base64-encoded key
           byte[] key = Base64.decodeBase64(keyBase64.getBytes("UTF-8"));
           Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");  // AES / CBC / PKCS5 padding
           SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
           cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec);
           byte[] encryptedData = Base64.decodeBase64(encryptedDataBase64.getBytes("UTF-8"));
           byte[] decryptedData = cipher.doFinal(encryptedData);

           return new String(decryptedData);
       }
       catch (Exception e) {
           logger.error("AES256 Decrypt: Decryption exception: "+ e.getMessage());
           return null;
       }
    }
}

现在我们需要将此解密逻辑转换为 Javascript,因为我们的应用程序在从服务器请求 index.html 的同时发送标头中的加密数据。我尝试使用 Crypto.js 解密,但我将解码的字符串设为空。下面是我尝试使用的 Javascript 代码。

var key = CryptoJS.enc.Base64.parse(keyBase64);
var data = CryptoJS.enc.Base64.parse(encryptedDataBase64);
var dec_data = CryptoJS.AES.decrypt(data, key);

dec_data 为空。

我在阅读了一些堆栈溢出问题后尝试了这个。如果代码有任何错误,谁能告诉我。

【问题讨论】:

    标签: javascript java cryptojs


    【解决方案1】:

    很遗憾,您没有提供任何示例数据来测试您的环境,因此我设置了自己的值。使用这个值:

    ciphertext = rjygE0TjIqiQ4ETnpszoieRWzaSD+9oINf1c748VcL/3zD5AazSFomx4paeanihz
    keyBase64 = MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=
    ivBase =  AAAAAAAAAAAAAAAAAAAAAA==
    

    我在 Java 上得到了这个解密结果:

    The quick brown fox jumps over the lazy dog
    

    在 Javascript 端,我的代码收到相同的结果:

    decrypted (str): The quick brown fox jumps over the lazy dog
    

    可以在此处进行实时测试 - Java:https://paiza.io/projects/e/dHG73CRgJojOOfLxtvJjtg 和 Javascript:https://playcode.io/672463

    请注意,我的代码没有异常处理,仅用于教育目的。此外 - 您的代码是不安全的,因为它使用静态(固定)密钥和初始化向量。

    Java 代码(在使用 Java 11 内置 Base64 编码器时略有更改):

    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    import java.io.UnsupportedEncodingException;
    import java.security.Security;
    import java.util.Base64;
    
    public class Main {
    
        private static byte[] initVector = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    
        public static void main(String[] args) throws UnsupportedEncodingException {
            System.out.println("How to decode data encrypted using AES-256 in CBC mode and PKCS7 padding");
            String plaintext = "The quick brown fox jumps over the lazy dog";
            byte[] key = "12345678901234567890123456789012".getBytes("UTF-8");
            String keyBase64 = Base64.getEncoder().encodeToString(key);
            System.out.println("keyBase64: " + keyBase64);
            String ivBase64 = Base64.getEncoder().encodeToString(initVector);
            System.out.println("initVector: " + ivBase64);
            String ciphertext = "rjygE0TjIqiQ4ETnpszoieRWzaSD+9oINf1c748VcL/3zD5AazSFomx4paeanihz";
            System.out.println("ciphertext: " + ciphertext);
            String decryptedtext = decrypt(ciphertext, Base64.getEncoder().encodeToString(key));
            System.out.println("decryptedtext: " + decryptedtext);
        }
    
        public static String decrypt (String encryptedDataBase64, String keyBase64)
        {
            try {
                Security.setProperty("crypto.policy", "unlimited");
                IvParameterSpec ivSpec = new IvParameterSpec(initVector);  // Get the init vector
                // Get the Base64-encoded key
                byte[] key = Base64.getDecoder().decode(keyBase64.getBytes("UTF-8"));
                //byte[] key = Base64.decodeBase64(keyBase64.getBytes("UTF-8"));
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");  // AES / CBC / PKCS5 padding
                SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
                cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec);
                byte[] encryptedData = Base64.getDecoder().decode(encryptedDataBase64.getBytes("UTF-8"));
                //byte[] encryptedData = Base64.decodeBase64(encryptedDataBase64.getBytes("UTF-8"));
                byte[] decryptedData = cipher.doFinal(encryptedData);
                return new String(decryptedData);
            }
            catch (Exception e) {
                System.out.println("AES256 Decrypt: Decryption exception: "+ e.getMessage());
                //logger.error("AES256 Decrypt: Decryption exception: "+ e.getMessage());
                return null;
            }
        }
    }
    

    Javascript 代码:

    let keyBase64 = 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=';
    let ivBase64 = 'AAAAAAAAAAAAAAAAAAAAAA==';
    let iv = CryptoJS.enc.Base64.parse(ivBase64);
    let secret_key = CryptoJS.enc.Base64.parse(keyBase64);
    ciphertext = 'rjygE0TjIqiQ4ETnpszoieRWzaSD+9oINf1c748VcL/3zD5AazSFomx4paeanihz';
    console.log('ciphertext: ', ciphertext);
    var decrypted = CryptoJS.AES.decrypt(ciphertext, secret_key,{
      iv: iv,
      padding: CryptoJS.pad.Pkcs7,
      mode: CryptoJS.mode.CBC,
    });
    console.log('decrypted (str): ', CryptoJS.enc.Utf8.stringify(decrypted).toString());
    

    【讨论】:

    • 非常感谢迈克尔。我可以知道什么是 ivBase64 吗?因为我们没有在 Java 代码中使用它。我们正在使用 initVector = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    • 代码正在使用我的密钥和数据。我只是想了解为什么 ivBase64 用 'AAAAAAAAAAAAAAAAAAAAAA==' 初始化。
    • 我只是懒得找到一种方法来使用具有 16 x0 的 initvector。正如您在 Java 代码中看到的,您的“无效”iv 被简单地编码为 Base64 字符串。我在 Javascript 端使用这个字符串作为 init,所以我确信我正在使用这个值(通过 Base64 解码)。还有其他方法可以直接创建一个 16 字节长的“空”变量 :-)
    猜你喜欢
    • 2017-08-29
    • 2017-10-11
    • 2013-08-11
    • 2017-01-31
    • 1970-01-01
    • 1970-01-01
    • 2017-09-28
    • 2010-12-31
    • 2015-08-22
    相关资源
    最近更新 更多