Using the API

The root of the API is the KeyManagerMapLocator.SINGLETON which maps key classes to their respective key manager instances. The following method returns this singleton as an instance of the KeyManagerMap interface:

KeyManagerMap getKeyManagerMap() { return KeyManagerMapLocator.SINGLETON; }

Getting a password for writing an encrypted file

The next step is to call this method and use the returned key manager map to obtain a KeyManager for the desired type of keys:

KeyManager<AesPbeParameters> manager = getKeyManagerMap()
        .manager(AesPbeParameters.class);

I am asking for the key type AesPbeParameters here because it provides the properties for password based encryption (PBE) with the Advanced Encryption Standard (AES), i.e. a password and a key strength (128/192/256 bits). I suppose this is what most applications would want to use, too.

Note that there are no constraints on the type of the keys, so you could even ask for a key manager for char arrays. However, a plug-in must be present on the class path which provides a mapping for the key type you want to use or otherwise you will get a java.util.ServiceConfigurationError when asking for the key manager.

There are predefined plug-ins available which use different interfaces to prompt the user for a password, e.g. the system console or a Swing dialog. However, these plug-ins only support AesPbeParameters, so this is another good reason to use this key type.

The next step is to ask the key manager for a KeyProvider>

File file = new File("encrypted").getCanonicalFile();
KeyProvider<AesPbeParameters> provider = manager.provider(file.toURI());

I am using the URI for the canonical path for the file here in order to make sure that I always get the same provider for the same file, no matter which path is used to address the file.

Key managers provide a number of methods for managing the life cycle of their key providers:

  • provider is the only method which returns a key provider.
  • move and delete notify the key manager that a client has moved or deleted a protected resource - in this case an encrypted file.
  • release notifies the key manager that a client doesn't need anymore access to the protected resource. Depending on the implementation and its state, the key manager may then perform some steps to reset or dispose the associated key provider. Calling this method is optional - see below.

The next step is to tell the key provider that I want to write the encrypted file:

AesPbeParameters param = provider.getKeyForWriting();

Depending on the implementation of the key provider, this call may prompt the user for a password. If this fails for any reason, e.g. because the user cancels the dialog, then an UnknownKeyException gets thrown. Otherwise, the key provider returns the key, i.e. the AES PBE parameters as defined by the user.

And finally, I use the AesPbeParameters to obtain the password and write the encrypted file:

char[] password = param.getPassword();
try {
    // Now write the file using the password.
    // [...]
} finally {
    Arrays.fill(password, (char) 0); // wipe the password memory
}

Note that I nullify the array with the password characters after use in order to wipe the password from heap memory. If you don' do this, then an attacker may be able to obtain the password by dumping and inspecting a snapshot of the heap.

As you can see, I haven't called manager.release(file.toURI() after writing the file. This is because I want to preserve the state of the key provider in case I want to read or overwrite the encrypted file later. Even if the user had cancelled the prompting, then I would not want to prompt her again for the same file.

Getting a password for reading an encrypted file

The procedure for reading an encrypted file is very similar to writing, therefore I show it all at once and discuss the differences below:

File file = new File("encrypted").getCanonicalFile();
KeyProvider<AesPbeParameters> provider = getKeyManagerMap()
        .manager(AesPbeParameters.class)
        .provider(file.toURI());
boolean invalid = false;
do {
    AesPbeParameters param = provider.getKeyForReading(invalid);
    char [] password = param.getPassword();
    try {
        // Now read the file and verify the password.
        // [...]
        // new String(char[]) copies the character array, so don't do
        // that in a real application!
        invalid = !"top secret".equals(new String(password));
    } finally {
        Arrays.fill(password, (char) 0); // wipe the password memory
    }
} while (invalid);

Like before, I start with looking up the key provider. This time however, I use a fluent style for conciseness.

The main difference to writing an encrypted file is that I need to tell the key provider that I want to read the encrypted file. This needs to be done in loop where the key gets validated by the client in each iteration until the user provides the correct key or cancels the prompting, in which case an UnknownKeyException gets thrown.

The boolean variable invalid is used to signal to the key provider an invalid previous attempt. There is no feedback if the key was valid or not, so the key provider will assume that the key is valid until it gets called again with invalid set to true.

Documentation

For more information, please refer to the Javadoc for the packages net.java.truecommons.key.spec and net.java.truecommons.key.spec.sl.