Hello,

I am currently working on a project that uses JDK's crypto libs to encrypt 
passwords using PBKDF2. During development I noticed that the PBKDF2KeyImpl 
class (which implements the SecretKey interface) retains a copy of the 
plaintext password char[] array. It seems to be impossible to clear the 
plaintext password after use. In my opinion, this is a security issue as an 
attacker could easily retrieve plaintext passwords that have not yet been 
garbage-collected, e.g. by analyzing a heap dump.

I would have expected PBKDF2KeyImpl's destroy() method to overwrite the passwd 
char[], but instead PBKDF2KeyImpl does not override the default implementation 
from the Destroyable interface. This results in DestroyFailedExceptions being 
thrown whenever the destroy() method is called.

In my opinion, JDK should probably include a way to clear the plaintext 
password. My suggestion would be to just provide an implementation of 
destroy(). This has the advantage of not having to add any new API. However, 
PBKDF2KeyImpl's getPassword() method would probably stop working as expected.

I am new to JDK development (in fact this e-mail is my first involvement in 
it), but I have been using Java and the JDK for well over a decade. Therefore, 
I'd be happy to help in finding a solution and to offer my time in helping to 
implement it. Since I'm sure there's some process involved, I could probably 
use some guidance on how I could contribute to this issue.

Here's the code that I currently have that made me aware of the issue. When 
debugging this code, it's obvious that secretKey's passwd field still contains 
the plaintext password, even after the DestroyFailedException is thrown.

--------------------------------------------------------------------
SecretKeyFactory secretKeyFactory =
    SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
PBEKeySpec spec = null;
SecretKey secretKey = null;

try {
    spec = new PBEKeySpec(plaintext, salt, iterations, keySizeBits);
    secretKey = secretKeyFactory.generateSecret(spec);
    return secretKey.getEncoded();
} catch (InvalidKeySpecException e) {
    throw new Error("Could not create valid key spec.", e);
} finally {
    // Destroy the secret key because it might still contain the
    // plaintext password.
    if (secretKey != null && !secretKey.isDestroyed()) {
        try {
            secretKey.destroy();
        } catch (DestroyFailedException e) {
            throw new Error("Could not destroy secret key after use.", e);
        }
    }

    // Clear the password from the key spec.
    if (spec != null) {
        spec.clearPassword();
    }
}
--------------------------------------------------------------------

Kind regards,
Sebastian Boschert

Reply via email to