If you want to store a password in your deployed application, perhaps for an external service you need to make sure it is saved somewhere safe. Internally at oracle we are never allowed to store a password in plain text, ideally it has to be stored somewhere encoded to prevent a idle HD search from finding it. This made me wonder how I could store a simple character password in a keystore for later use to invoke a remote web service.
Keys are complicated beasties as you can see from the number of sub classes in java. In the most part they are going to be public/private key pair which are the mainstay of the internet. But if you just want to store a simple password you need to create a SecretKey.
Now the complicated this is that you have to create a key spec, which is transparent and create new opaque secret key from it for storage in the keystore. In case you are wonder PBE stands for "Password Based Encryption" which is the simplest type I could find in the security spec that would suit my needs.
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBE"); SecretKey generatedSecret = factory.generateSecret(new PBEKeySpec( password));
It turns out that the default "JKS" KeyStore type will not allow you to store a SecretKey. So instead you need the code to create the right kind. It might look something like this:
KeyStore ks = KeyStore.getInstance("JCEKS"); ks.load(null, keyStorePassword); PasswordProtection keyStorePP = new PasswordProtection(keyStorePassword); SecretKeyFactory factory = SecretKeyFactory.getInstance("PBE"); SecretKey generatedSecret = factory.generateSecret(new PBEKeySpec( password)); ks.setEntry(key, new SecretKeyEntry( generatedSecret), keyStorePP); ks.save(…, // Output stream for keystore keyStorePassword);
Note that you can't just add PBEKeySpec to a keystore because while it is a "Key" it is not of type "SecretKey" so you have to convert. When loading the password out from the keystore you need the SecretKeyFactory again to convert the SecretKey back into the PBEKeySpec that you can extract the password from.
KeyStore ks = KeyStore.getInstance("JCEKS"); ks.load(…, // InputStrweam to keystore keyStorePassword); SecretKeyFactory factory = SecretKeyFactory.getInstance("PBE"); SecretKeyEntry ske = (SecretKeyEntry)ks.getEntry(key, keyStorePP); PBEKeySpec keySpec = (PBEKeySpec)factory.getKeySpec( ske.getSecretKey(), PBEKeySpec.class); char password = keySpec.getPassword();
Now in general use the JCEKS keystore is far more secure than the default JKS format. The reason it is not the default is back in the old days they encryption was treated as a munition. I think this is only a problem for a few black listed countries now - although this restriction seems a bit pointless given the reach of the internet.