apple.security.KeychainStore does not load private key (when called from javaws)
Florian Bruckner (3kraft)
florian.bruckner at 3kraft.com
Tue Jul 15 08:56:40 UTC 2014
To answer my own question:
KeychainStore loads the private key through native code in KeystoreImpl.m. KeystoreImpl.m uses this
osx API to retrieve a PKCS#12 bag:
https://developer.apple.com/library/mac/documentation/security/Reference/keychainservices/Reference/reference.html#jumpTo_63
The API documentation says the PKCS#12 keystore requires a password. This is either supplied by the
caller or the user is prompted (if a flag is set, which is not the case).
KeychainStore then goes on to extract the private key from the returned PKCS#12 store and decrypts
it with the password.
Therefore, the password passed into engineGetKey is actually used to encrypt and decrypt only in the
scope of this method. Therefore, the approach to create a dummy password for this use case is not as
weird as I initially thought.
Please consider these patches to fix this issue:
For jdk7u-dev:
diff -r 35aabd00a534 src/macosx/classes/apple/security/KeychainStore.java
--- a/src/macosx/classes/apple/security/KeychainStore.java Tue Jul 15 02:26:55 2014 +0400
+++ b/src/macosx/classes/apple/security/KeychainStore.java Tue Jul 15 10:52:44 2014 +0200
@@ -134,7 +134,7 @@
* password to recover it.
*
* @param alias the alias name
- * @param password the password for recovering the key
+ * @param password the password for recovering the key. This password is not used to access the
private key in KeyChain, it is used internally only.
*
* @return the requested key, or null if the given alias does not exist
* or does not identify a <i>key entry</i>.
@@ -148,6 +148,16 @@
throws NoSuchAlgorithmException, UnrecoverableKeyException
{
permissionCheck();
+ // An empty password is rejected by MacOS API, no private key data
+ // is exported. If no password is passed (as is the case when
+ // this implementation is used as browser keystore in various
+ // deployment scenarios like webstart, JFX and applets), create
+ // a dummy password to MacOS API is happy.
+ if (password == null || password.length ==0) {
+ // Must not be a char array with only a 0, as this is an empty
+ // string. Therefore use a single character.
+ password = new char[]{'A'}
+ }
Object entry = entries.get(alias.toLowerCase());
For jdk8u-dev:
diff -r baec3649f6c0 src/macosx/classes/apple/security/KeychainStore.java
--- a/src/macosx/classes/apple/security/KeychainStore.java Tue Jul 15 02:00:52 2014 +0400
+++ b/src/macosx/classes/apple/security/KeychainStore.java Tue Jul 15 10:54:45 2014 +0200
@@ -140,7 +140,7 @@
* password to recover it.
*
* @param alias the alias name
- * @param password the password for recovering the key
+ * @param password the password for recovering the key. This password is not used to access the
private key in KeyChain, it is used internally only.
*
* @return the requested key, or null if the given alias does not exist
* or does not identify a <i>key entry</i>.
@@ -154,7 +154,16 @@
throws NoSuchAlgorithmException, UnrecoverableKeyException
{
permissionCheck();
-
+ // An empty password is rejected by MacOS API, no private key data
+ // is exported. If no password is passed (as is the case when
+ // this implementation is used as browser keystore in various
+ // deployment scenarios like webstart, JFX and applets), create
+ // a dummy password to MacOS API is happy.
+ if (password == null || password.length ==0) {
+ // Must not be a char array with only a 0, as this is an empty
+ // string. Therefore use a single character.
+ password = new char[]{'A'}
+ }
Object entry = entries.get(alias.toLowerCase());
if (entry == null || !(entry instanceof KeyEntry)) {
With best regards,
Florian
--
3kraft IT GmbH & Co KG | Wasagasse 26/2 | 1090 Wien | Österreich | FN 333787 p (HG Wien)
Komplementär: 3kraft IT GmbH | Wasagasse 26/2 | 1090 Wien | Österreich | FN 333558 b (HG Wien)
On 14.07.14 18:38, Florian Bruckner (3kraft) wrote:
> Hi,
>
> I've been working on getting client certificates to run on OSX with Apple KeyChain in Java
> Webstart. KeychainStore works fine if it is passed a keystore password. It does not use it, at
> least not when trying to create a KeyStore with a private key entry.
>
> Java Webstart tries to load private keys from KeyChain if the browser keystore is activated.
> Unfortunately it tries to load the private key with an empty password (a char[0]), and
> KeychainStore rejects to load the private key then. It works fine for the trust store, as there
> are no private keys involved.
>
> A simple "fix" for this is to set a dummy password in KeychainStore if no password is passed in
> engineGetKey, e.g. like this:
>
>
> if (password == null || password.length ==0) {
> password = "DUMMY".toCharArray();
> }
>
> Well, not really a fix, but rather a POC that the empty password is in fact the problem.
>
> I've not found out what exactly it is that the private key is not loaded, I just verified that the
> empty password passed by javaws causes it. Before I try to dig into KeychainStore - any idea why
> the private keys are not loaded if the password is empty?
>
> regards,
>
> Florian
>
More information about the macosx-port-dev
mailing list