Encryption
AES-GCM加解密疑惑
我在我的項目中使用 AES-GCM 模式,我有一些疑問。
public String encrypt(String plainText, String authenticationTag) throws GeneralSecurityException, IOException { Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING); final int blockSize = cipher.getBlockSize(); SecretKey secretKey = readKeyFile(...); byte[] plainTextByte = plainText.trim().getBytes(Charset.forName("UTF-8")); byte[] ivBytes = generateIV(); cipher.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes)); cipher.updateAAD(authenticationTag.getBytes(Charset.forName("UTF-8"))); byte[] encryptedMessage = cipher.doFinal(plainTextByte); byte iv_and_encryptedMessage[] = concatIvAndCipher(ivBytes, encryptedMessage, blockSize); Base64.Encoder encoder = Base64.getEncoder().withoutPadding(); String iv_and_encryptedText = encoder.encodeToString(iv_and_encryptedMessage); return iv_and_encryptedText; } public String decrypt(String iv_and_encryptedTextBase64, String authenticationTag) throws GeneralSecurityException, IOException { Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING); final int blockSize = cipher.getBlockSize(); Base64.Decoder decoder = Base64.getDecoder(); byte[] iv_and_encryptedMessage = decoder.decode(iv_and_encryptedTextBase64.trim()); byte[] ivBytes = extractIvFrom(iv_and_encryptedMessage, blockSize); byte[] encryptedTextBytes = extractEncryptedMessageFrom(iv_and_encryptedMessage, blockSize); SecretKey secretKey = readKeyFile(...); cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(GCM_TAG_LENGTH, ivBytes)); cipher.updateAAD(authenticationTag.getBytes(Charset.forName("UTF-8"))); byte[] decryptedByte = cipher.doFinal(encryptedTextBytes); String decryptedMessage = new String(decryptedByte, Charset.forName("UTF-8")); return decryptedMessage; }
問題:
- 我使用 GUID 作為身份驗證標籤,它綁定到敏感資訊所屬的使用者。加密文本和標籤儲存在不同位置的不同數據庫中。這個可以嗎?
- 從我讀過的內容來看,AES-GCM 接受(純文字、IV、密鑰、關聯數據)和輸出(密文、身份驗證標籤)。關聯的數據和身份驗證標籤是否相同,因為當我傳遞相同的值時,我的加密和解密實現工作
cipher.updateAAD(...)
?加密後如何獲取認證標籤?- 我應該使用 Bouncy Castle 而不是使用預設的 Java 實現嗎?
- 如果要加密的數據是 16 字節,我應該什麼時候為 enc/dec 生成一個新的 DEK?
- 所有 DEK 的一個主 KEK?對於加密 DEK,我應該使用相同的 AES 實現嗎?如果沒有,那用什麼?
- 我使用 GUID 作為身份驗證標籤,它綁定到敏感資訊所屬的使用者。加密文本和標籤儲存在不同位置的不同數據庫中。這個可以嗎?
您沒有將 GUID 用作身份驗證標籤,而是將其用作附加的經過身份驗證的數據,這沒關係,尤其是因為您已經共享了一個密鑰。身份驗證標籤 - 當使用有點被破解的 Java API 時 - 輸出在密文的末尾。
2)從我讀過的內容來看,AES-GCM 接收(純文字、IV、密鑰、相關數據)和輸出(密文、身份驗證標籤)。關聯的數據和身份驗證標籤是否相同,因為當我在 cipher.updateAAD(…) 中傳遞相同的值時,我的加密和解密實現工作?加密後如何獲取認證標籤?
不,見上文。如果您想獲取身份驗證標籤,您可能希望將其從密文的末尾剝離。
- 我應該使用 BouncyCastle 而不是使用預設的 Java 實現。
除非您想放棄 AES-NI 和 GCM 優化,否則不會。對於 OpenJDK / Oracle 執行時提供的 API 中已經存在的實現,我會遠離 Bouncy Castle。
- 如果要加密的數據是 16 字節,我應該什麼時候為 enc/dec 生成一個新的 DEK。
請參閱 NIST GCM 規範;一般來說,它取決於身份驗證標籤的大小,因此可能有多個答案。
常量值
GCM_TAG_LENGTH
不會顯示在問題的程式碼中,因此在您的特定情況下不清楚身份驗證標籤大小是多少。在評論中提到了 128 位的身份驗證標籤大小。NIST GCM 規範對此並不完全清楚,所以我詢問了這個問題中的尺寸,這個問題後來得到了很多回答。
- 一個主 KEK 用於所有 DEK?對於加密 DEK,我應該使用相同的 AES 實現嗎?如果沒有,那用什麼?
這取決於協議,最終取決於案例和威脅模型。但總的來說,我會使用諸如 HKDF 之類的 KDF 來派生密鑰,而不是包裝(加密)它們。現在*,*您可能想要使用 Bouncy Castle,因為 Java 本身沒有任何好的 KDF 實現。