簡體   English   中英

如何銷毀 Java 14 中的 SecretKey?

[英]How to destroy SecretKey in Java 14?

我試圖在解密后清除我的Secretkey 根據我的閱讀,自 Java 8 以來,可以通過destroy方法銷毀SecretKeys 。我使用的是 Java 14,所以應該可以。

但是,每當我在鍵上使用 destroy 方法時,都會拋出DestroyFailedException 我還看到人們在他們的代碼中忽略了那個異常,但是,如果我這樣做,我可以在調用它的destroy方法后打印 Key。

這是我的解密方法:

private byte[] decrypt(byte[] encryptedText, char[] password) throws InvalidKeyException,
        InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException,
        InvalidKeySpecException, IllegalBlockSizeException, BadPaddingException, DestroyFailedException {

    ByteBuffer bb = ByteBuffer.wrap(encryptedText);

    byte[] iv = new byte[ivLengthByte];
    bb.get(iv);

    byte[] salt = new byte[saltLengthByte];
    bb.get(salt);

    byte[] cipherText = new byte[bb.remaining()];
    bb.get(cipherText);

    SecretKey key;
    key = crypto.getAESKeyFromPassword(password, salt);

    Cipher cipher;
    cipher = Cipher.getInstance(algorithm);

    cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(tagLengthBit, iv));

    byte[] plainText = cipher.doFinal(cipherText);

    Main.clearArray(password, null);
    Main.clearArray(null, iv);
    Main.clearArray(null, salt);
    Main.clearArray(null, cipherText);

    key.destroy();

    cipher = null;

    return plainText;

}

在調用了destroy方法之后,如上所述,我(假設我忽略了異常)能夠通過 String encodedKey = Base64.getEncoder().encodeToString(key.getEncoded()); 打印密鑰。

編輯:在數組上使用我的 Clear 方法后,我仍然可以打印它:

byte[] temp = key.getEncoded();
        Main.clearArray(null, temp);

清除數組:

protected static void clearArray(char[] chars, byte[] bytes) {
    if (chars != null) {
        for (int i = 0; i < chars.length; i++) {
            chars[i] = '\0';
        }

    }
    if (bytes != null) {
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = 0;
        }

    }

}

獲取AES密鑰:

protected SecretKey getAESKeyFromPassword(char[] password, byte[] salt)
        throws NoSuchAlgorithmException, InvalidKeySpecException {

    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");

    KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
    SecretKey secret = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");

    return secret;

}

最終編輯:

最好的解決方案是將 frim PBKDF2 切換到 argon2。 https://github.com/kosprov/jargon2-api Argon2 允許使用原始哈希,然后您可以將該字節數組存儲在上述 SecureKeySpec 中,因為它允許破壞規范,並清除原始 Hash 數組。

實際上,沒有簡單的解決方案。 問題是destroy方法是一種“可選”方法。 並非所有SecretKey的實現都實現了它。 如果您使用的是未實現該方法的SecretKey類型,則會出現此異常並且沒有簡單的解決方案。

不幸的是,您不能只自己實現該方法,因為(通常)它所屬的 class 由 Java SE 庫提供。

即使您確實知道如何銷毀密鑰,也存在包含密碼1String的問題。 (而且這個問題更多的是安全風險,因為搜索包含密碼的String可能比搜索未知字節序列更容易。)

選項:

  1. 忘記問題。 不要破壞他們在 memory 中的密鑰/密碼。 (請參閱下文,了解為什么這不像聽起來那么糟糕。)

  2. 尋找替代 JSSE 加密庫,其中 AES 密鑰的 SecretKey 實現確實實現了destroy 我猜 Bouncy Castle 圖書館可能。 (如果他們不這樣做,您總是可以選擇下載源代碼並修補它們。)

  3. 討厭的反射。 您可以找出哪個實際的 class 實現了密鑰,並查看其代碼以了解它在內部如何表示密鑰。 然后,您可以使用反射來打破抽象並訪問其私有 state 並...在密鑰上寫零。


為什么不破壞密鑰不是災難?

所以一些安全專家可能不同意這一點,但我仍然認為這是一個有效的觀點。

當您將 memory 中的密鑰或密碼歸零時,您(表面上)可以防止以下類型的攻擊:

  • 將 Java 調試器附加到 JVM 進程並使用它來定位和讀取密鑰。
  • 讀取 JVM 進程 memory。
  • 讀取已寫入磁盤的 memory 頁面。

這些攻擊有多容易? 前兩個要求黑客已經進入主機並升級到(可能)root權限。 在第三種情況下,您可以這樣做,但黑客也可以竊取寫入交換頁面的硬盤驅動器。

在所有情況下,黑客都必須找到密鑰。 與(例如)C / C++ 程序不同,密鑰不會存儲在固定位置。 相反,黑客必須通過模式匹配或查找參考鏈來找到它。 (A Java debugger would make it easier, provided that the key object is still reachable.) And the flip-side is once the key has been garbage collected, the copy in memory will be gone, and the copy in swap will go the next操作系統寫出密鑰 object 曾經存在的(現在)臟頁的時間。 在那之后......出於所有實際目的,它已經“消失”了。

所以倒帶一點。 我說為了發動這種攻擊,黑客已經需要root權限了。 (或者硬盤驅動器,這很可能相當於同一件事。)現在,如果他們擁有它,他們還有其他方法可以竊取密鑰。 例如:

  • 使用調試器在(比如說) destroy方法上設置一個斷點,並在它被銷毀之前抓住它。

  • 在創建密鑰之前使用調試器捕獲密碼。

  • 竊取服務器的 SSL 證書(或其他)的私鑰,以便他們可以從網絡流量中獲取密碼。

  • 安裝軟件擊鍵記錄器。

  • 將您的應用程序代碼替換為通過某個側通道泄露密鑰或密碼的版本。

當然,他們可以安裝后門等。簡而言之,如果黑客已經將系統破壞到了對 JVM 發起“從內存中竊取內容”攻擊所需的程度,那可能是您最不擔心的。

現在,安全專家可能會說,對黑客進行分層防御是“最佳實踐”。 這是有道理的。 但是,如果安全對您來說很重要,您應該進行適當的安全分析(而不僅僅是“打勾”審計)並找出真正的風險是什么。 這將(可能是2 )告訴您,與其擔心某人(具有 root 權限)是否可以從 memory 中竊取密鑰,不如專注於確保系統安全。


1 - 雖然不是你的情況,因為我看到你正在使用char[] ... 可以清除。 除了這仍然容易受到我所說的所有其他攻擊。
2 - 或者它不會。 但是你需要做分析!

您必須自己實現destroy 方法。 文檔對此進行了解釋。

https://docs.oracle.com/javase/8/docs/api/javax/crypto/SecretKey.html

我可能已經找到了一個解決方案,我嘗試使用這個: https://github.com/dbsystel/SecureSecretKeySpec

唯一的問題是密鑰必須是一個字節數組,並且這樣做:

protected SecureSecretKeySpec getAESKeyFromPassword(char[] password, byte[] salt)
        throws NoSuchAlgorithmException, InvalidKeySpecException {

    SecretKeyFactory factory= SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");

    KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);

    byte[] temp = factory.generateSecret(spec).getEncoded();

    SecureSecretKeySpec sec= new SecureSecretKeySpec(temp, "AES");

    Main.clearArray(null, temp);

    return sec;

}

可能不好,因為在SecretKey上有一個getEncoded調用,所以Memory中可能有一個SecretKey?

暫無
暫無

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

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