簡體   English   中英

解密 java 中使用 openssl 加密的文件

[英]Decrypt file in java that was encrypted with openssl

您好 使用以下命令對文件進行加密:

openssl enc -aes-256-cbc -in file.txt -out file_enc.txt -k 1234567812345678

該文件使用以下命令解密:

openssl enc -d -aes-256-cbc -in file_enc.txt -out file.txt -k 1234567812345678

我正在使用以下代碼,但我得到:

線程“主”javax.crypto.BadPaddingException 中的異常:給定最終塊未正確填充。 如果在解密期間使用了錯誤的密鑰,則可能會出現此類問題。:

public static void main(String args[]) 
        throws InvalidKeySpecException, NoSuchAlgorithmException, 
        IllegalBlockSizeException, InvalidKeyException, BadPaddingException, 
        InvalidAlgorithmParameterException, NoSuchPaddingException, IOException {
      String password = "1234567812345678";
    SecretKey key = AESUtil.getKeyFromPassword(password);
    String algorithm = "AES/CBC/PKCS5Padding";
    IvParameterSpec ivParameterSpec = AESUtil.generateIv();
    Resource resource = new ClassPathResource("file_enc.txt");
   File inputFile = resource.getFile();
   File decryptedFile = new File("file.txt");
    AESUtil.decryptFile(
   algorithm, key, ivParameterSpec, inputFile, decryptedFile);

    }

public static SecretKey getKeyFromPassword(String password)
        throws NoSuchAlgorithmException, InvalidKeySpecException {
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec spec = new PBEKeySpec(password.toCharArray());
        SecretKey secret = new SecretKeySpec( password.getBytes(), "AES");
        return secret;
    }


    public static IvParameterSpec generateIv() {
        byte[] iv = new byte[16];
        new SecureRandom().nextBytes(iv);
        return new IvParameterSpec(iv);
    }
public static void decryptFile(String algorithm, SecretKey key, IvParameterSpec iv,
        File encryptedFile, File decryptedFile) throws IOException, NoSuchPaddingException,
        NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException,
        BadPaddingException, IllegalBlockSizeException {
        Cipher cipher = Cipher.getInstance(algorithm);
        cipher.init(Cipher.DECRYPT_MODE, key, iv);
        FileInputStream inputStream = new FileInputStream(encryptedFile);
        FileOutputStream outputStream = new FileOutputStream(decryptedFile);
        byte[] buffer = new byte[64];
        int bytesRead;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            byte[] output = cipher.update(buffer, 0, bytesRead);
            if (output != null) {
                outputStream.write(output);
            }
        }
        byte[] output = cipher.doFinal();
        if (output != null) {
            outputStream.write(output);
        }
        inputStream.close();
        outputStream.close();
    }

您的getKeyFromPassword為 PBKDF2(使用 HmacSHA256)創建了一個SecretkeyFactory ,但不使用它; 相反,您使用密碼作為密鑰,這是錯誤的 - 除非您真的想使用密鑰( -K大寫和十六進制,對於 AES-256 應該是 64 十六進制/32 字節)而不是密碼( -k小寫和任何字符或任何長度)。 (在 IIRC 18 以下, String.getBytes()為您提供了一個依賴於 JVM 的編碼,對於像真實密碼這樣的任意字符串,它可能在不同的系統或環境中有所不同,這將導致使用它的密碼術失敗,但是對於您顯示的示例字符串所有現實的編碼都會給出相同的結果。)

但即使你確實使用了這個工廠,它也不正確,因為openssl enc默認不使用 PBKDF2,它使用一個名為EVP_BytesToKey的 function,它基於但不同於 PBKDF1。 僅在 OpenSSL 1.1.1 和 3.0 中,您可以選擇enc中指定-pbkdf2 (和/或-iter N ),如果您不這樣做,它會給出有關“不推薦使用的密鑰派生”的警告消息,您應該已經注意到了,所以我假設您要么使用過時的 OpenSSL,要么試圖阻止我們准確了解您的情況,從而使其不太可能得到有用的答案。

此外,無論是哪種密鑰派生(舊EVP_BytesToKey或可選的新 PBKDF2), openssl enc默認使用鹽,加密時隨機生成並寫入文件; 解密時,您必須從文件中讀取鹽並使用它。 具體來說,跳過或丟棄前 8 個字節(它們是固定字符Salted__ )並將接下來的 8 個字節作為鹽,然后將文件的其余部分作為密文

根據您想要(或者您的用戶/客戶/等想要)做的事情,有三種可能性:

  1. encrypt with openssl enc -aes-256-cbc -k... with the default derivation (as now) and code the Java to read the salt from the file as above, implement EVP_BytesToKey using the password and that salt, and use its output對於 Java Cipher中的密鑰和 IV(對於 aes-256-cbc,生成 48 個字節並使用前 32 個字節作為密鑰,最后 16 個字節作為 IV)。 EVP_BytesToKey uses a hash which defaults to SHA256 for OpenSSL 1.1.0 up and MD5 for lower versions, so you need to know which version did the encryption for this to work, or else you can specify the hash on the enc command with -md $hash 十多年來,關於這個問題已經有數百個問題了。 搜索EVP_BytesToKey以找到其中的一些。

  2. 使用openssl enc -aes-256-cbc -pbkdf2 -k...加密並編碼 Java 以從上述文件中讀取鹽,並使用您創建的密鑰工廠生成 48 字節的“密鑰”材料,您實際上必須在 Java Cipher中拆分為 key 和 IV 。

  3. 使用openssl enc -aes-256-cbc -K 64hexits -iv 32hexits並編碼 Java 以使用相應的二進制密鑰和 IV 值。

在命令中,我既沒有指定隨機 IV 也沒有指定 PKCS5Padding

當您在openssl enc中使用舊密鑰或新密鑰派生時,它會派生IV 而不是單獨指定它; 僅當您使用顯式鍵( -K大寫)時,您還指定-iv openssl enc始終默認為各種稱為 pkcs5、pkcs7 或 pkcs5/7 的填充,除非不需要填充(流密碼,如 RC4 或 ChaCha 或 stream 模式,如 CTR、OFB、CFB)。

暫無
暫無

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

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