JDK-6672015 : Microsoft CryptoAPI KeyStore can have aliases duplicates
  • Type: Bug
  • Component: security-libs
  • Sub-Component: javax.crypto
  • Affected Version: 6
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2008-03-06
  • Updated: 2011-02-16
  • Resolved: 2008-04-30
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_02"
Java(TM) SE Runtime Environment (build 1.6.0_02-b06)
Java HotSpot(TM) Client VM (build 1.6.0_02-b06, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [version 5.1.2600]

EXTRA RELEVANT SYSTEM CONFIGURATION :
Microsoft CryptoAPI device used: Aladdin eToken
KeyStore affected: "Windows-MY" and "Windows-ROOT"

A DESCRIPTION OF THE PROBLEM :
Microsoft Cryptography API KeyStore instanciated and loaded on Microsoft Windows using Java 1.6.0_02 can hold many aliases with the same name.

So if you have two certificates (one for crypting, the other for signing) with the name "Foo Bar" in the Windows CAPI native store, the "Windows-MY" or "Windows-ROOT" type of java.security.KeyStore will return two aliases with exactly the same "Foo Bar" string when using the ".aliases()" method.

Unfortunately, it will be impossible to get both certificates from the KeyStore then using the "getCertificate(String)" or "getEntry(String, KeyStore.ProtectionParameter)" methods. Only the first certificate machin the duplicated name in the entries collection will be returned.

In this case, one of the certificates will always be unreachable.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Make sure you have at least two certificates identified with the same name in the native Microsoft Cryptography API certificates store;
2. Then instanciate a Java KeyStore of "Windows-MY" type;
3. Load it using both "load(null, null)" call;
4. Check all aliases available on the loaded keystore: two of then should have the same name;
5. Try to retreive all two certificates described above using "getCertificate(String)" method: it's impossible, because both have the same alias.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
It would be better to keep track of existing aliases on keystore load, and add a uniq suffix to duplicated alias.
ACTUAL -
If more than one certificate have the same name in the Microsoft Cryptography API certificates store, they'll use the same alias on Java keytore load.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
KeyStore keyStore = KeyStore.getInstance("Windows-MY");
keyStore.load(null, null);

String aliases = Collections.list(keyStore.aliases());
String uniqAliases = new HashSet(aliases);

if(aliases.size() > uniqAliases.size()) {
    System.err.println("Warning: duplicated aliases used in " + keyStore.type() + " key store: " + aliases);
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
The following dirty method add a unique suffix directly to the aliases in the key store instance to fix the duplicated:

	private static void _fixAliases(KeyStore keyStore) {
		Field field;
		KeyStoreSpi keyStoreVeritable;

		try {
			field = keyStore.getClass().getDeclaredField("keyStoreSpi");
			field.setAccessible(true);
			keyStoreVeritable = (KeyStoreSpi)field.get(keyStore);

			if("sun.security.mscapi.KeyStore$MY".equals(keyStoreVeritable.getClass().getName())) {
				Collection entries;
				String alias, hashCode;
				X509Certificate[] certificates;

				field = keyStoreVeritable.getClass().getEnclosingClass().getDeclaredField("entries");
				field.setAccessible(true);
				entries = (Collection)field.get(keyStoreVeritable);

				for(Object entry : entries) {
					field = entry.getClass().getDeclaredField("certChain");
					field.setAccessible(true);
					certificates = (X509Certificate[])field.get(entry);

					hashCode = Integer.toString(certificates[0].hashCode());

					field = entry.getClass().getDeclaredField("alias");
					field.setAccessible(true);
					alias = (String)field.get(entry);

					if(!alias.equals(hashCode)) {
						field.set(entry, alias.concat(" - ").concat(hashCode));
					} // if
				} // for
			} // if
		} catch(Exception exception) {
			System.err.println(exception);
			exception.printStackTrace();
		} // catch
	} // _fixAliases