Merge pull request #272 from cconlon/WolfSSLKeyX509_CachedEntries

JSSE: cache KeyStore entries in X509ExtendedKeyManager
master
JacobBarthelmeh 2025-06-11 10:06:08 -06:00 committed by GitHub
commit 1dc7d5a741
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 1012 additions and 68 deletions

View File

@ -469,6 +469,7 @@ and used by wolfSSL JNI/JSSE.
| wolfjsse.enabledSignatureAlgorithms | | String | Restricts enabled signature algorithms |
| wolfjsse.keystore.type.required | | String | Restricts KeyStore type |
| wolfjsse.clientSessionCache.disabled | | "true" | Disables client session cache |
| wolfjsse.X509KeyManager.disableCache | "false" | "true" | Disables X509KeyManager KeyStore entry caching |
**wolfssl.readWriteByteBufferPool.disabled (String)** - Can be used to disable
the static per-thread ByteBuffer pool used in com.wolfssl.WolfSSLSession
@ -554,6 +555,18 @@ cache. The Java client cache is enabled by default.
wolfjsse.clientSessionCache.disabled=true
```
**wolfjsse.X509KeyManager.disableCache (String)** - Can be used to disable
KeyStore entry caching in the WolfSSLKeyX509 (X509ExtendedKeyManager) implementation.
When set to "true", the X509KeyManager will revert to the original behavior of
calling KeyStore methods directly for each operation instead of using cached
entries. This can be useful for debugging, compatibility testing, or when
KeyStore contents may change dynamically. Caching is enabled by default for
performance. This should be set to the String "true" to disable caching:
```
wolfjsse.X509KeyManager.disableCache=true
```
If there are other Security properties you would like to use with wolfJSSE,
please contact support@wolfssl.com.

View File

@ -1081,7 +1081,6 @@ public class WolfSSLImplementSSLSession extends ExtendedSSLSession {
throws UnsupportedOperationException {
byte[] sniRequestArr = null;
List<SNIServerName> sniNames = new ArrayList<>(1);
if (this.ssl == null) {
return Collections.emptyList();
@ -1103,6 +1102,7 @@ public class WolfSSLImplementSSLSession extends ExtendedSSLSession {
}
if (sniRequestArr != null) {
List<SNIServerName> sniNames = new ArrayList<>(1);
SNIHostName sniName = new SNIHostName(sniRequestArr);
sniNames.add(sniName);

View File

@ -222,8 +222,14 @@ public class WolfSSLKeyManager extends KeyManagerFactorySpi {
"initialized before use, please call init()");
}
KeyManager[] km = {new WolfSSLKeyX509(this.store, this.pswd)};
return km;
try {
KeyManager[] km = { new WolfSSLKeyX509(this.store, this.pswd) };
return km;
} catch (KeyStoreException e) {
throw new IllegalStateException(
"Failed to create WolfSSLKeyX509: " + e.getMessage(), e);
}
}
}

View File

@ -26,10 +26,16 @@ import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.Arrays;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedKeyManager;
import com.wolfssl.WolfSSLDebug;
@ -43,71 +49,257 @@ import com.wolfssl.WolfSSLDebug;
*/
public class WolfSSLKeyX509 extends X509ExtendedKeyManager {
private KeyStore store;
private char[] password;
/* Security property to control caching behavior. When set to "true",
* disables KeyStore entry caching and reverts to calling KeyStore
* methods directly for each operation. */
private static final String DISABLE_CACHE_PROPERTY =
"wolfjsse.X509KeyManager.disableCache";
/* Reference to original KeyStore for non-cached operations */
private final KeyStore keyStore;
private final char[] keyStorePassword;
/* Cache behavior determined once at construction time */
private final boolean cacheDisabled;
/* Cache for KeyStore entries to avoid concurrent access issues. Prior
* to the addition of these caches, WolfSSLKeyX509 called down directly
* to the underlying KeyStore for each operation. Since the KeyStore
* operations are synchronized, concurrent threads accessing this
* KeyManager can queue up in that scenario and hurt performance.
* These caches are only used if caching has not been disabled via
* Security property. */
private final Map<String, X509Certificate> certificateCache;
private final Map<String, X509Certificate[]> certificateChainCache;
private final Map<String, PrivateKey> privateKeyCache;
private final Set<String> aliasSet;
/**
* Create new WolfSSLKeyX509 object
*
* @param in input KeyStore to use with this object
* @param store input KeyStore to cache entries from
* @param password input KeyStore password
* @throws KeyStoreException if unable to populate cache from KeyStore
*/
public WolfSSLKeyX509(KeyStore in, char[] password) {
this.store = in;
this.password = password;
public WolfSSLKeyX509(KeyStore store, char[] password)
throws KeyStoreException {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "creating new WolfSSLKeyX509 object");
/* Check Security property once at construction time */
this.cacheDisabled = "true".equalsIgnoreCase(
Security.getProperty(DISABLE_CACHE_PROPERTY));
if (this.cacheDisabled) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "KeyStore caching disabled via " +
DISABLE_CACHE_PROPERTY +
" Security property, using direct KeyStore access");
this.keyStore = store;
this.keyStorePassword =
(password != null) ? password.clone() : null;
this.certificateCache = null;
this.certificateChainCache = null;
this.privateKeyCache = null;
this.aliasSet = null;
} else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "KeyStore caching enabled, populating caches");
this.keyStore = null;
this.keyStorePassword = null;
/* Initialize cache data structures */
this.certificateCache = new HashMap<String, X509Certificate>();
this.certificateChainCache =
new HashMap<String, X509Certificate[]>();
this.privateKeyCache = new HashMap<String, PrivateKey>();
this.aliasSet = new LinkedHashSet<String>();
/* Populate caches from KeyStore */
populateCache(store, password);
}
}
/**
* Return array of aliases from current KeyStore that matches provided
* type and issuers array.
* Populate internal caches with all entries from KeyStore
*
* @param store KeyStore to read entries from
* @param password KeyStore password to access private keys
* @throws KeyStoreException if unable to read from KeyStore
*/
private void populateCache(KeyStore store, char[] password)
throws KeyStoreException {
if (store == null) {
return;
}
Enumeration<String> aliases = store.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
aliasSet.add(alias);
try {
/* Cache individual certificate */
Certificate cert = store.getCertificate(alias);
if (cert instanceof X509Certificate) {
certificateCache.put(alias, (X509Certificate)cert);
}
/* Cache certificate chain */
Certificate[] certChain = store.getCertificateChain(alias);
if (certChain != null) {
int x509Cnt = 0;
/* Count X509Certificate entries */
for (int i = 0; i < certChain.length; i++) {
if (certChain[i] instanceof X509Certificate) {
x509Cnt++;
}
}
/* Store X509Certificate chain */
if (x509Cnt > 0) {
int idx = 0;
X509Certificate[] x509Chain =
new X509Certificate[x509Cnt];
for (int i = 0; i < certChain.length; i++) {
if (certChain[i] instanceof X509Certificate) {
x509Chain[idx++] = (X509Certificate)certChain[i];
}
}
certificateChainCache.put(alias, x509Chain);
}
}
/* Cache private key */
PrivateKey key = (PrivateKey)store.getKey(alias, password);
if (key != null) {
privateKeyCache.put(alias, key);
}
} catch (Exception e) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR,
() -> "Error caching entry for alias: " + alias + ", " + e);
/* Continue processing other aliases */
}
}
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "Cached " + aliasSet.size() + " aliases from KeyStore");
}
/**
* Return array of aliases from KeyStore that matches provided
* type and issuers array (non-cached version).
*
* Returns:
* null - if current KeyStore is null, error getting aliases from store,
* or no alias mathes found in current KeyStore.
* null - if no alias matches found in KeyStore.
* String[] - aliases, if found that match type and/or issuers
*/
private String[] getAliases(String type, Principal[] issuers) {
Enumeration<String> aliases = null;
private String[] getAliasesFromKeyStore(String type, Principal[] issuers)
throws KeyStoreException {
int i;
ArrayList<String> ret = new ArrayList<String>();
if (store == null) {
if (keyStore == null) {
return null;
}
try {
aliases = this.store.aliases();
} catch (KeyStoreException ex) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR,
() -> "Error getting aliases from current KeyStore");
return null;
}
Enumeration<String> aliases = keyStore.aliases();
/* loop through each alias in KeyStore */
/* loop through each KeyStore alias */
while (aliases.hasMoreElements()) {
String current = aliases.nextElement();
X509Certificate cert = null;
try {
cert = (X509Certificate)this.store.getCertificate(current);
} catch (KeyStoreException ex) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR,
() -> "Error getting certificate from KeyStore " +
"for alias: " + current + ", continuing to next alias");
Certificate cert = keyStore.getCertificate(current);
if (!(cert instanceof X509Certificate)) {
continue;
}
X509Certificate x509cert = (X509Certificate)cert;
if (type != null &&
!x509cert.getPublicKey().getAlgorithm().equals(type)) {
/* different public key type, skip */
continue;
}
/* if issuers is null then it does not matter which issuer */
if (issuers == null) {
ret.add(current);
}
else {
/* search through issuers for matching issuer name */
for (i = 0; i < issuers.length; i++) {
String certIssuer = x509cert.getIssuerDN().getName();
String issuerName = issuers[i].getName();
/* normalize spaces after commas, needed on some JDKs */
certIssuer = certIssuer.replaceAll(", ", ",");
issuerName = issuerName.replaceAll(", ", ",");
if (certIssuer.equals(issuerName)) {
/* matched issuer, add alias and continue on */
ret.add(current);
break;
}
}
}
}
if (ret.size() == 0) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "No aliases found in KeyStore that match type " +
"and/or issuer");
return null;
}
return ret.toArray(new String[0]);
}
/**
* Return array of aliases that matches provided type and issuers array.
* Uses either cached entries or direct KeyStore access based on Security
* property configuration.
*
* Returns:
* null - if no alias matches found.
* String[] - aliases, if found that match type and/or issuers
*/
private String[] getAliases(String type, Principal[] issuers) {
/* Check if caching is disabled, use direct KeyStore access */
if (this.cacheDisabled) {
try {
return getAliasesFromKeyStore(type, issuers);
} catch (KeyStoreException e) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR,
() -> "Error accessing KeyStore directly: " +
e.getMessage());
return null;
}
}
/* Use cached entries */
int i;
ArrayList<String> ret = new ArrayList<String>();
/* loop through each cached alias */
for (String current : aliasSet) {
X509Certificate cert = certificateCache.get(current);
if (type != null && cert != null &&
!cert.getPublicKey().getAlgorithm().equals(type)) {
/* free native memory early if X509Certificate is WolfSSLX509 */
if (cert instanceof WolfSSLX509) {
((WolfSSLX509)cert).free();
}
cert = null;
/* different public key type, skip */
continue;
}
@ -135,11 +327,11 @@ public class WolfSSLKeyX509 extends X509ExtendedKeyManager {
}
}
}
} /* end while */
}
if (ret.size() == 0) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "No aliases found in KeyStore that match type " +
() -> "No aliases found in cache that match type " +
"and/or issuer");
return null;
}
@ -244,59 +436,140 @@ public class WolfSSLKeyX509 extends X509ExtendedKeyManager {
@Override
public X509Certificate[] getCertificateChain(String alias) {
X509Certificate[] ret = null;
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "entered getCertificateChain(), alias: " + alias);
if (store == null || alias == null) {
if (alias == null) {
return null;
}
try {
Certificate[] certs = this.store.getCertificateChain(alias);
if (certs != null) {
int x509Cnt = 0;
/* Check if caching is disabled, use direct KeyStore access */
if (this.cacheDisabled) {
try {
if (keyStore == null) {
return null;
}
/* count up X509Certificate type in certs[] */
for (int i = 0; i < certs.length; i++) {
if (certs[i] instanceof X509Certificate) {
Certificate[] certChain = keyStore.getCertificateChain(alias);
if (certChain == null) {
return null;
}
/* Convert to X509Certificate array */
int x509Cnt = 0;
for (int i = 0; i < certChain.length; i++) {
if (certChain[i] instanceof X509Certificate) {
x509Cnt++;
}
}
/* store into X509Certificate array */
ret = new X509Certificate[x509Cnt];
for (int i = 0; i < certs.length; i++) {
if (certs[i] instanceof X509Certificate) {
ret[i] = (X509Certificate)certs[i];
if (x509Cnt == 0) {
return null;
}
X509Certificate[] x509Chain = new X509Certificate[x509Cnt];
int idx = 0;
for (int i = 0; i < certChain.length; i++) {
if (certChain[i] instanceof X509Certificate) {
x509Chain[idx++] = (X509Certificate)certChain[i];
}
}
}
} catch (KeyStoreException ex) {
return null;
return x509Chain;
} catch (KeyStoreException e) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR,
() -> "Error accessing certificate chain from KeyStore: " +
e.getMessage());
return null;
}
}
return ret;
/* Return cached certificate chain */
return certificateChainCache.get(alias);
}
@Override
public PrivateKey getPrivateKey(String alias) {
PrivateKey key = null;
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "entered getPrivateKey(), alias: " + alias);
try {
key = (PrivateKey)store.getKey(alias, password);
} catch (Exception e) {
/* @TODO unable to get key */
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "failed to load private key: " + e);
if (alias == null) {
return null;
}
/* Check if caching is disabled, use direct KeyStore access */
if (this.cacheDisabled) {
try {
if (keyStore == null || keyStorePassword == null) {
return null;
}
return (PrivateKey)keyStore.getKey(alias, keyStorePassword);
} catch (Exception e) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR,
() -> "Error accessing private key from KeyStore: " +
e.getMessage());
return null;
}
}
/* Return cached private key */
return privateKeyCache.get(alias);
}
/**
* Clear sensitive data when object is garbage collected
*/
@Override
protected void finalize() throws Throwable {
try {
/* Clear KeyStore password if present */
if (keyStorePassword != null) {
Arrays.fill(keyStorePassword, (char)0);
}
/* Clear cached private keys */
if (privateKeyCache != null) {
privateKeyCache.clear();
}
/* Free WolfSSLX509 certificates if present */
if (certificateCache != null) {
for (X509Certificate cert : certificateCache.values()) {
if (cert instanceof WolfSSLX509) {
((WolfSSLX509)cert).free();
}
}
certificateCache.clear();
}
/* Free WolfSSLX509 certificate chains if present */
if (certificateChainCache != null) {
for (X509Certificate[] chain : certificateChainCache.values()) {
if (chain != null) {
for (X509Certificate cert : chain) {
if (cert instanceof WolfSSLX509) {
((WolfSSLX509)cert).free();
}
}
}
}
certificateChainCache.clear();
}
if (aliasSet != null) {
aliasSet.clear();
}
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "WolfSSLKeyX509 finalized, sensitive data cleared");
} finally {
super.finalize();
}
return key;
}
}

View File

@ -25,6 +25,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.security.KeyStore;
import java.security.Principal;
import java.security.Provider;
import java.security.Security;
@ -459,6 +460,657 @@ public class WolfSSLKeyX509Test {
pass("\t... passed");
}
@Test
public void testConstructorWithInvalidKeyStore()
throws NoSuchAlgorithmException, KeyStoreException,
KeyManagementException, CertificateException, IOException,
NoSuchProviderException, UnrecoverableKeyException {
System.out.print("\tTesting with invalid KeyStore");
/* Test with null KeyStore - should not throw exception */
try {
com.wolfssl.provider.jsse.WolfSSLKeyX509 km =
new com.wolfssl.provider.jsse.WolfSSLKeyX509(null, null);
/* Should succeed with empty cache */
} catch (Exception e) {
error("\t... failed");
fail("Constructor should handle null KeyStore gracefully: " + e);
}
pass("\t... passed");
}
@Test
public void testCacheConsistencyWithKeyStore()
throws NoSuchAlgorithmException, KeyStoreException,
KeyManagementException, CertificateException, IOException,
NoSuchProviderException, UnrecoverableKeyException {
KeyManager[] list;
X509KeyManager km;
String[] aliases;
System.out.print("\tTesting cache consistency");
/* Create KeyManager with cached WolfSSLKeyX509 */
list = tf.createKeyManager("SunX509", tf.allJKS, provider);
km = (X509KeyManager) list[0];
/* Test that cached aliases match what we expect */
aliases = km.getClientAliases("RSA", null);
if (aliases == null || aliases.length == 0) {
error("\t... failed");
fail("No RSA client aliases found in cache");
}
/* Test certificate chain consistency */
for (String alias : aliases) {
if (alias != null) {
X509Certificate[] chain = km.getCertificateChain(alias);
if (chain == null) {
error("\t... failed");
fail("Certificate chain missing from cache for alias: " + alias);
}
}
}
/* Test private key consistency */
for (String alias : aliases) {
if (alias != null) {
/* Private key may be null for some aliases, that's expected */
km.getPrivateKey(alias);
}
}
pass("\t... passed");
}
@Test
public void testEmptyKeyStoreCache()
throws NoSuchAlgorithmException, KeyStoreException,
KeyManagementException, CertificateException, IOException,
NoSuchProviderException, UnrecoverableKeyException {
System.out.print("\tTesting empty KeyStore cache");
try {
/* Create empty KeyStore */
KeyStore emptyStore = KeyStore.getInstance("JKS");
emptyStore.load(null, null);
/* Create WolfSSLKeyX509 with empty KeyStore */
com.wolfssl.provider.jsse.WolfSSLKeyX509 km =
new com.wolfssl.provider.jsse.WolfSSLKeyX509(emptyStore, null);
/* Test methods with empty cache */
String[] aliases = km.getClientAliases("RSA", null);
if (aliases != null) {
error("\t... failed");
fail("Expected null aliases from empty KeyStore");
}
aliases = km.getServerAliases("RSA", null);
if (aliases != null) {
error("\t... failed");
fail("Expected null server aliases from empty KeyStore");
}
String alias = km.chooseClientAlias(new String[] {"RSA"}, null, null);
if (alias != null) {
error("\t... failed");
fail("Expected null client alias from empty KeyStore");
}
alias = km.chooseServerAlias("RSA", null, null);
if (alias != null) {
error("\t... failed");
fail("Expected null server alias from empty KeyStore");
}
} catch (Exception e) {
error("\t... failed");
fail("Empty KeyStore test failed: " + e);
}
pass("\t... passed");
}
@Test
public void testNullKeyStoreCache()
throws NoSuchAlgorithmException, KeyStoreException,
KeyManagementException, CertificateException, IOException,
NoSuchProviderException, UnrecoverableKeyException {
System.out.print("\tTesting null KeyStore cache");
try {
/* Create WolfSSLKeyX509 with null KeyStore */
com.wolfssl.provider.jsse.WolfSSLKeyX509 km =
new com.wolfssl.provider.jsse.WolfSSLKeyX509(null, null);
/* Test methods with null KeyStore */
String[] aliases = km.getClientAliases("RSA", null);
if (aliases != null) {
error("\t... failed");
fail("Expected null aliases from null KeyStore");
}
aliases = km.getServerAliases("RSA", null);
if (aliases != null) {
error("\t... failed");
fail("Expected null server aliases from null KeyStore");
}
String alias = km.chooseClientAlias(new String[] {"RSA"}, null, null);
if (alias != null) {
error("\t... failed");
fail("Expected null client alias from null KeyStore");
}
alias = km.chooseServerAlias("RSA", null, null);
if (alias != null) {
error("\t... failed");
fail("Expected null server alias from null KeyStore");
}
X509Certificate[] chain = km.getCertificateChain("nonexistent");
if (chain != null) {
error("\t... failed");
fail("Expected null certificate chain from null KeyStore");
}
java.security.PrivateKey key = km.getPrivateKey("nonexistent");
if (key != null) {
error("\t... failed");
fail("Expected null private key from null KeyStore");
}
} catch (Exception e) {
error("\t... failed");
fail("Null KeyStore test failed: " + e);
}
pass("\t... passed");
}
@Test
public void testCertificateChainCaching()
throws NoSuchAlgorithmException, KeyStoreException,
KeyManagementException, CertificateException, IOException,
NoSuchProviderException, UnrecoverableKeyException {
KeyManager[] list;
X509KeyManager km;
X509Certificate[] chain1, chain2;
System.out.print("\tTesting cert chain caching");
list = tf.createKeyManager("SunX509", tf.allJKS, provider);
km = (X509KeyManager) list[0];
/* Get certificate chain twice to test caching */
chain1 = km.getCertificateChain("client");
chain2 = km.getCertificateChain("client");
if (chain1 == null) {
error("\t... failed");
fail("Certificate chain should not be null for 'client' alias");
}
if (chain2 == null) {
error("\t... failed");
fail("Second certificate chain retrieval should not be null");
}
/* Test that both retrievals return the same cached object */
if (chain1 != chain2) {
error("\t... failed");
fail("Certificate chain caching failed - different objects returned");
}
/* Test with non-existent alias */
X509Certificate[] nullChain = km.getCertificateChain("nonexistent");
if (nullChain != null) {
error("\t... failed");
fail("Expected null certificate chain for non-existent alias");
}
/* Test with null alias */
nullChain = km.getCertificateChain(null);
if (nullChain != null) {
error("\t... failed");
fail("Expected null certificate chain for null alias");
}
pass("\t... passed");
}
@Test
public void testPrivateKeyCaching()
throws NoSuchAlgorithmException, KeyStoreException,
KeyManagementException, CertificateException, IOException,
NoSuchProviderException, UnrecoverableKeyException {
KeyManager[] list;
X509KeyManager km;
java.security.PrivateKey key1, key2;
System.out.print("\tTesting private key caching");
list = tf.createKeyManager("SunX509", tf.allJKS, provider);
km = (X509KeyManager) list[0];
/* Get private key twice to test caching */
key1 = km.getPrivateKey("client");
key2 = km.getPrivateKey("client");
if (key1 == null) {
error("\t... failed");
fail("Private key should not be null for 'client' alias");
}
if (key2 == null) {
error("\t... failed");
fail("Second private key retrieval should not be null");
}
/* Test that both retrievals return the same cached object */
if (key1 != key2) {
error("\t... failed");
fail("Private key caching failed - different objects returned");
}
/* Test with non-existent alias */
java.security.PrivateKey nullKey = km.getPrivateKey("nonexistent");
if (nullKey != null) {
error("\t... failed");
fail("Expected null private key for non-existent alias");
}
/* Test with null alias */
nullKey = km.getPrivateKey(null);
if (nullKey != null) {
error("\t... failed");
fail("Expected null private key for null alias");
}
pass("\t... passed");
}
@Test
public void testCacheDisabledSecurityProperty()
throws NoSuchAlgorithmException, KeyStoreException,
KeyManagementException, CertificateException, IOException,
NoSuchProviderException, UnrecoverableKeyException {
System.out.print("\tTesting cache disabled");
/* Save original property value */
String originalValue =
Security.getProperty("wolfjsse.X509KeyManager.disableCache");
try {
/* Test with caching disabled */
Security.setProperty(
"wolfjsse.X509KeyManager.disableCache", "true");
/* Create KeyManager with caching disabled */
KeyStore ks = KeyStore.getInstance("JKS");
java.io.FileInputStream fis =
new java.io.FileInputStream(tf.allJKS);
ks.load(fis, "wolfSSL test".toCharArray());
fis.close();
com.wolfssl.provider.jsse.WolfSSLKeyX509 km =
new com.wolfssl.provider.jsse.WolfSSLKeyX509(
ks, "wolfSSL test".toCharArray());
/* Test that operations work with caching disabled */
String[] aliases = km.getClientAliases("RSA", null);
if (aliases == null || aliases.length == 0) {
error("\t\t... failed");
fail("No RSA client aliases found with caching disabled");
}
/* Test certificate chain retrieval */
X509Certificate[] chain = km.getCertificateChain("client");
if (chain == null) {
error("\t\t... failed");
fail("Certificate chain should not be null with " +
"caching disabled");
}
/* Test private key retrieval */
java.security.PrivateKey key = km.getPrivateKey("client");
if (key == null) {
error("\t\t... failed");
fail("Private key should not be null with caching disabled");
}
/* Test alias selection */
String selectedAlias =
km.chooseClientAlias(new String[] {"RSA"}, null, null);
if (selectedAlias == null) {
error("\t\t... failed");
fail("Should be able to choose client alias with " +
"caching disabled");
}
} finally {
/* Restore original property value */
if (originalValue != null) {
Security.setProperty(
"wolfjsse.X509KeyManager.disableCache", originalValue);
} else {
/* Remove property if it wasn't set originally */
Security.setProperty(
"wolfjsse.X509KeyManager.disableCache", "");
}
}
pass("\t\t... passed");
}
@Test
public void testCacheEnabledSecurityProperty()
throws NoSuchAlgorithmException, KeyStoreException,
KeyManagementException, CertificateException, IOException,
NoSuchProviderException, UnrecoverableKeyException {
System.out.print("\tTesting cache enabled");
/* Save original property value */
String originalValue =
Security.getProperty("wolfjsse.X509KeyManager.disableCache");
try {
/* Test with caching explicitly enabled */
Security.setProperty(
"wolfjsse.X509KeyManager.disableCache", "false");
/* Create KeyManager with caching enabled */
KeyStore ks = KeyStore.getInstance("JKS");
java.io.FileInputStream fis =
new java.io.FileInputStream(tf.allJKS);
ks.load(fis, "wolfSSL test".toCharArray());
fis.close();
com.wolfssl.provider.jsse.WolfSSLKeyX509 km =
new com.wolfssl.provider.jsse.WolfSSLKeyX509(
ks, "wolfSSL test".toCharArray());
/* Test that operations work with caching enabled */
String[] aliases = km.getClientAliases("RSA", null);
if (aliases == null || aliases.length == 0) {
error("\t\t... failed");
fail("No RSA client aliases found with caching enabled");
}
/* Test certificate chain retrieval and caching */
X509Certificate[] chain1 = km.getCertificateChain("client");
X509Certificate[] chain2 = km.getCertificateChain("client");
if (chain1 == null || chain2 == null) {
error("\t\t... failed");
fail("Certificate chains should not be null " +
"with caching enabled");
}
/* With caching enabled, should return same cached object */
if (chain1 != chain2) {
error("\t\t... failed");
fail("Certificate chain caching failed - different " +
"objects returned");
}
/* Test private key retrieval and caching */
java.security.PrivateKey key1 = km.getPrivateKey("client");
java.security.PrivateKey key2 = km.getPrivateKey("client");
if (key1 == null || key2 == null) {
error("\t\t... failed");
fail("Private keys should not be null with caching enabled");
}
/* With caching enabled, should return same cached object */
if (key1 != key2) {
error("\t\t... failed");
fail("Private key caching failed - different objects returned");
}
} finally {
/* Restore original property value */
if (originalValue != null) {
Security.setProperty(
"wolfjsse.X509KeyManager.disableCache", originalValue);
} else {
/* Remove property if it wasn't set originally */
Security.setProperty(
"wolfjsse.X509KeyManager.disableCache", "");
}
}
pass("\t\t... passed");
}
@Test
public void testDefaultCachingBehavior()
throws NoSuchAlgorithmException, KeyStoreException,
KeyManagementException, CertificateException, IOException,
NoSuchProviderException, UnrecoverableKeyException {
System.out.print("\tTesting default cache behavior");
/* Save original property value */
String originalValue = Security.getProperty(
"wolfjsse.X509KeyManager.disableCache");
try {
/* Clear property to test default behavior */
Security.setProperty("wolfjsse.X509KeyManager.disableCache", "");
/* Create KeyManager with default behavior
* (should be caching enabled) */
KeyStore ks = KeyStore.getInstance("JKS");
java.io.FileInputStream fis =
new java.io.FileInputStream(tf.allJKS);
ks.load(fis, "wolfSSL test".toCharArray());
fis.close();
com.wolfssl.provider.jsse.WolfSSLKeyX509 km =
new com.wolfssl.provider.jsse.WolfSSLKeyX509(
ks, "wolfSSL test".toCharArray());
/* Test that operations work with default behavior */
String[] aliases = km.getClientAliases("RSA", null);
if (aliases == null || aliases.length == 0) {
error("\t... failed");
fail("No RSA client aliases found with default behavior");
}
/* Test that caching works by default (same objects returned) */
X509Certificate[] chain1 = km.getCertificateChain("client");
X509Certificate[] chain2 = km.getCertificateChain("client");
if (chain1 == null || chain2 == null) {
error("\t... failed");
fail("Certificate chains should not be null " +
"with default behavior");
}
/* Default behavior should be caching enabled */
if (chain1 != chain2) {
error("\t... failed");
fail("Default behavior should enable caching - " +
"different objects returned");
}
} finally {
/* Restore original property value */
if (originalValue != null) {
Security.setProperty(
"wolfjsse.X509KeyManager.disableCache", originalValue);
} else {
/* Remove property if it wasn't set originally */
Security.setProperty(
"wolfjsse.X509KeyManager.disableCache", "");
}
}
pass("\t... passed");
}
@Test
public void testCaseInsensitiveSecurityProperty()
throws NoSuchAlgorithmException, KeyStoreException,
KeyManagementException, CertificateException, IOException,
NoSuchProviderException, UnrecoverableKeyException {
System.out.print("\tCase insensitive cache disable");
/* Save original property value */
String originalValue = Security.getProperty(
"wolfjsse.X509KeyManager.disableCache");
try {
/* Test different case variations of "true" */
String[] trueVariations = {"true", "TRUE", "True", "tRuE"};
for (String trueValue : trueVariations) {
Security.setProperty(
"wolfjsse.X509KeyManager.disableCache", trueValue);
KeyStore ks = KeyStore.getInstance("JKS");
java.io.FileInputStream fis =
new java.io.FileInputStream(tf.allJKS);
ks.load(fis, "wolfSSL test".toCharArray());
fis.close();
com.wolfssl.provider.jsse.WolfSSLKeyX509 km =
new com.wolfssl.provider.jsse.WolfSSLKeyX509(
ks, "wolfSSL test".toCharArray());
/* Should work with any case variation of "true" */
String[] aliases = km.getClientAliases("RSA", null);
if (aliases == null || aliases.length == 0) {
error("\t... failed");
fail("No RSA client aliases found with '" +
trueValue + "'");
}
}
/* Test values that should NOT disable caching */
String[] falseVariations = {"false", "FALSE", "False", "0",
"no", "disabled", "random"};
for (String falseValue : falseVariations) {
Security.setProperty(
"wolfjsse.X509KeyManager.disableCache", falseValue);
KeyStore ks = KeyStore.getInstance("JKS");
java.io.FileInputStream fis =
new java.io.FileInputStream(tf.allJKS);
ks.load(fis, "wolfSSL test".toCharArray());
fis.close();
com.wolfssl.provider.jsse.WolfSSLKeyX509 km =
new com.wolfssl.provider.jsse.WolfSSLKeyX509(
ks, "wolfSSL test".toCharArray());
/* Should have caching enabled (same objects returned) */
X509Certificate[] chain1 = km.getCertificateChain("client");
X509Certificate[] chain2 = km.getCertificateChain("client");
if (chain1 == null || chain2 == null) {
error("\t... failed");
fail("Certificate chains should not be null with '" +
falseValue + "'");
}
if (chain1 != chain2) {
error("\t... failed");
fail("Caching should be enabled with '" +
falseValue + "' - different objects returned");
}
}
} finally {
/* Restore original property value */
if (originalValue != null) {
Security.setProperty(
"wolfjsse.X509KeyManager.disableCache", originalValue);
} else {
/* Remove property if it wasn't set originally */
Security.setProperty(
"wolfjsse.X509KeyManager.disableCache", "");
}
}
pass("\t... passed");
}
@Test
public void testNullKeyStoreWithCachingDisabled()
throws NoSuchAlgorithmException, KeyStoreException,
KeyManagementException, CertificateException, IOException,
NoSuchProviderException, UnrecoverableKeyException {
System.out.print("\tnull KeyStore with no caching");
/* Save original property value */
String originalValue = Security.getProperty(
"wolfjsse.X509KeyManager.disableCache");
try {
/* Test with caching disabled and null KeyStore */
Security.setProperty(
"wolfjsse.X509KeyManager.disableCache", "true");
com.wolfssl.provider.jsse.WolfSSLKeyX509 km =
new com.wolfssl.provider.jsse.WolfSSLKeyX509(null, null);
/* Test that all operations return null gracefully */
String[] aliases = km.getClientAliases("RSA", null);
if (aliases != null) {
error("\t... failed");
fail("Expected null aliases with null KeyStore and " +
"caching disabled");
}
X509Certificate[] chain = km.getCertificateChain("client");
if (chain != null) {
error("\t... failed");
fail("Expected null certificate chain with null " +
"KeyStore and caching disabled");
}
java.security.PrivateKey key = km.getPrivateKey("client");
if (key != null) {
error("\t... failed");
fail("Expected null private key with null KeyStore " +
"and caching disabled");
}
String alias = km.chooseClientAlias(new String[] {"RSA"},
null, null);
if (alias != null) {
error("\t... failed");
fail("Expected null alias with null KeyStore and " +
"caching disabled");
}
} finally {
/* Restore original property value */
if (originalValue != null) {
Security.setProperty(
"wolfjsse.X509KeyManager.disableCache", originalValue);
} else {
/* Remove property if it wasn't set originally */
Security.setProperty(
"wolfjsse.X509KeyManager.disableCache", "");
}
}
pass("\t... passed");
}
private void pass(String msg) {
WolfSSLTestFactory.pass(msg);
}