Question

I am finding that when I create a password-protected PKCS12 file that also contains a password-protected private key and its associated public key / certificate, I am able to decrypt the private key regardless of the password provided to getKey() (internal encrypted content) as long as the correct password is provided to KeyStore's initial load(). Is this a known issue or has anyone else seen this? It really looks like the private key password is being unused or ignored all together. I am using Android with BouncyCastle as the provider. I am also curious if this problem is true for JKS and not just BouncyCastle? Error checking in the code below has been removed for clarity.

When I create my PKCS12 file I use the following code (privateKey is an RSAPrivateKey and signedCert is an X509Certificate):

KeyStore store;
store = KeyStore.getInstance( "PKCS12", "BC" );
store.load( null, null );

X509Certificate[] chain = new X509Certificate[1];
chain[0] = signedCert;

store.setKeyEntry( pkcs12Alias, privateKey, p12PkeyPass.toCharArray(), chain );

FileOutputStream fos;
File outputDir = appContext.getFilesDir();
File pkcs12File = new File( outputDir, p12Filename );
fos = new FileOutputStream( pkcs12File );

store.store( fos, p12Pass.toCharArray() );
fos.flush();
fos.close();

When I go to load the PKCS12 contents, no matter what I put for the private key password, the extracted private keys are all correctly loaded and all the same (pkey1 == pkey2 == pkey3 tested with the .equals()).

FileInputStream fis;
KeyStore store;
File pkcs12File = new File( activity.getFilesDir(), p12Filename );
fis = new FileInputStream( pkcs12File );
store = KeyStore.getInstance( "PKCS12", "BC" );
store.load( fis, p12Pass.toCharArray() );

X509Certificate signedCert = (X509Certificate) store.getCertificate( pkcs12Alias );

// try to get the private key with different passwords - result is the same
RSAPrivateKey pkey1 = (RSAPrivateKey) store.getKey( pkcs12Alias, p12PkeyPass.toCharArray() );
RSAPrivateKey pkey2 = (RSAPrivateKey) store.getKey( pkcs12Alias, "".toCharArray() );
RSAPrivateKey pkey3 = (RSAPrivateKey) store.getKey( pkcs12Alias, "something completely different".toCharArray() );

fis.close();

Thanks in advance!

Was it helpful?

Solution

Bouncy Castle, like many other providers ignores the key password for PKCS#12. PKCS#12 is a standard concerning personal credentials exchange, one result of this is that most implementations assume only one password is required for both the keystore and the keys.

It is possible to have keys encrypted in a PKCS#12 file with a different password to that used to seal the keystore, however if you do this it is unlikely that you will find another application that can read the file. Or put another way, trying this is not a crazy idea, it's just that the KeyStore API and PKCS#12 are similar but different, so you will find idiosyncrasies exist - in some ways PKCS#12 is more fully featured as it allows the attachment of attributes to the objects stored in the file in a much more general way than the KeyStore API does.

We (as in Bouncy Castle) have recently attempted to provide a more general way to deal with this in a separate API for PKCS#12 which is in the current beta for 1.49. It will allow you to use different encryption passwords if you really want, but I would recommend against it if you wish the files you produce to be understood by anything else. The PKCS#12 API does allow you to make better use of attributes, but whether that is something you really need to be able to do (often you don't) I will leave to you to decide.

You will find that other formats like JKS allow for different passwords for keys and keystores. As I mentioned earlier though, the difference here is that JKS was not defined as a "personal" storage mechanism, unlike PKCS#12.

Regards,

David

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top