How to destroy SecretKey in Java 14?
21:43 19 Feb 2021

I am trying to clear my Secretkey after decrypting. From what I've read, SecretKeys can be destroyed via the destroy method since Java 8. I am using Java 14 so it should be possible.

However, whenever I use the destroy method on a key, a DestroyFailedException is thrown. I've also seen that people ignore that Exception in their code, however, if I were to do that, I am able to print the Key after calling the destroy method on it.

Here my Decryption method:

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;

}

After calling the destroy method, I am, as said, (assuming I ignore the Exception) able to print the key via String encodedKey = Base64.getEncoder().encodeToString(key.getEncoded());

EDIT: After using my Clear method on the array, I can still print it:

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

Clear Array:

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;
        }

    }

}

getAESKey:

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;

}

Final Edit:

The best solution was to switch frim PBKDF2 to argon2. https://github.com/kosprov/jargon2-api Argon2 allows to use raw Hashes, then you may store that byte array in a SecureKeySpec as mentioned above, since it allows destroying of the Spec, and clear the raw Hash Array.

java encryption destroy secret-key aes-gcm