【发布时间】:2021-02-20 01:23:48
【问题描述】:
我试图在解密后清除我的Secretkey。
根据我的阅读,SecretKeys 自 Java 8 以来可以通过 destroy 方法销毁。
我使用的是 Java 14,所以应该可以。
但是,每当我在键上使用 destroy 方法时,都会抛出 DestroyFailedException。
我还看到人们在他们的代码中忽略了该异常,但是,如果我这样做,我可以在调用 destroy 方法后打印密钥。
这里是我的解密方法:
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;
}
}
}
获取AESKey:
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 中,因为它允许销毁 Spec,并清除原始哈希数组。
【问题讨论】:
-
我们需要粘贴您的加密类 - 特别是
getAESKeyFromPassword方法。 -
此外,这是在毫无意义的安全剧院中进行的练习。只有两个选项:[1] 您信任您的进程内存,在这种情况下这无关紧要,或者 [2] 您不信任,在这种情况下,任何时候的任何解密都是安全漏洞,您应该确保您的进程内存是安全的。如果这不是一个选项,请使用安全飞地(这在 Java 应用程序中很难做到)。这可以防止“我的进程内存现在是安全的,但以后可能不会”。给我写一个詹姆斯邦德式的情节,你就是这样被黑的,我敢打赌这完全不现实。
-
据我所知
key.getEncoded返回一个新数组,清除它毫无意义。可能需要清除密码数组和getAESKeyFromPassword方法创建的数组 -
关键是它仍然按原样存储字节,在该步骤中没有混淆。您可以尽快清理它,但任何内存转储确实会清楚地显示您的密钥。默认情况下,它也将保留在内存中,直到下一次 GC。不可能从内存转储中隐藏它,并且密钥很容易找到,只需搜索那些方法名称。在这一点上,你可能会通过混淆代码获得更快的解决方法,而不是尝试实现不可能的事情。
-
我添加了getAESKeyFromPassword,它不返回一个数组,而是一个SecretKey
标签: java encryption destroy secret-key aes-gcm