apache/pulsar · critical · KeyStoreException
KeyStore creation error
Error message
KeyStore creation error
What it means
KeyStoreHolder's constructor creates an in-memory KeyStore via JcaKeyStores and loads it. Any GeneralSecurityException or IOException during creation/load (other than the provider-specific KeyStoreException, which is rethrown unwrapped with an actionable message) is wrapped as KeyStoreException("KeyStore creation error") with the original as cause.
Source
Thrown at pulsar-common/src/main/java/org/apache/pulsar/common/util/KeyStoreHolder.java:76
* empty entry password are used, exactly as before)
* @throws KeyStoreException if the store cannot be created or the pinned provider supplies no usable type
*/
public KeyStoreHolder(Provider jcaProvider) throws KeyStoreException {
// Backward compatibility: only the opt-in pinned-provider path changes the entry password. This class is
// public and unrelocated, so callers that still pass "".toCharArray() to KeyManagerFactory.init() must
// keep working on the default path.
this.entryPassword = jcaProvider == null ? new char[0] : JcaKeyStores.newInMemoryPassword();
try {
String storeType = JcaKeyStores.inMemoryStoreType(jcaProvider, KeyStore.getDefaultType());
keyStore = JcaKeyStores.keyStore(storeType, jcaProvider);
keyStore.load(null, null);
} catch (KeyStoreException e) {
// JcaKeyStores raises this with an actionable message naming the pinned provider and the store
// types it does register; wrapping it in a generic "KeyStore creation error" would bury exactly
// the text the operator needs, since only the cause would carry it.
throw e;
} catch (GeneralSecurityException | IOException e) {
throw new KeyStoreException("KeyStore creation error", e);
}
}
public KeyStore getKeyStore() {
return keyStore;
}
/**
* @return the password this holder's key entries are stored under; a {@code KeyManagerFactory} reading
* them must be initialized with it. A fresh copy is returned on each call and is owned by the
* caller, who should zero it once the factory has consumed it (as
* {@code JdkSslContexts.setupKeyManager} does) rather than leaving the plaintext password
* reachable.
*/
public char[] getEntryPassword() {
return Arrays.copyOf(entryPassword, entryPassword.length);
}
View on GitHub (pinned to 820761864e)
Solutions
- Inspect the cause attached to the KeyStoreException — it holds the real error from the JCA provider.
- If pinning a provider, verify it registers an in-memory store type (PKCS12/BCFKS); the provider-specific KeyStoreException is propagated unwrapped with details naming supported types.
- Check the JVM's java.security and installed security providers; restore the default JDK configuration if it was modified.
- Construct the holder early at startup (fail fast) rather than lazily during TLS handshake setup.
Example fix
// before
KeyStoreHolder holder = new KeyStoreHolder(pinnedProvider); // "KeyStore creation error"
// after
if (pinnedProvider != null && Security.getProvider(pinnedProvider.getName()) == null) {
throw new IllegalStateException("pinned provider not registered: " + pinnedProvider.getName());
}
KeyStoreHolder holder = new KeyStoreHolder(pinnedProvider); Defensive patterns
Strategy: try-catch
Validate before calling
static void checkProvidersReady(Provider pinned) {
if (pinned != null && Security.getProvider(pinned.getName()) == null) {
throw new IllegalStateException("pinned JCA provider not registered: " + pinned.getName());
}
// sanity: default store type must be creatable
try {
KeyStore.getInstance(KeyStore.getDefaultType());
} catch (KeyStoreException e) {
throw new IllegalStateException("default KeyStore type unavailable: " + KeyStore.getDefaultType(), e);
}
} Try / catch
try {
KeyStoreHolder holder = new KeyStoreHolder(pinnedProvider);
} catch (KeyStoreException e) {
// cause holds the real JCA/IO error (store type unavailable, provider broken, JVM security config)
throw new RuntimeException("in-memory keystore init failed: " + e.getCause(), e);
} Prevention
- Verify java.security providers config is unmodified in production images
- Confirm the pinned provider registers an in-memory store type before enabling PIP-478 pinning
- Create the holder at startup to fail fast, not during first TLS use
- Always log e.getCause() — the wrapper message is generic by design
When it happens
Trigger: new KeyStoreHolder() or new KeyStoreHolder(provider) throwing during KeyStore.getInstance/store-type resolution or keyStore.load(null, null) — e.g. IOException or a non-KeyStoreException security error while instantiating the store type for the pinned provider.
Common situations: JVM misconfiguration where the default keystore type (e.g. PKCS12/JKS) provider is unavailable or broken (tampered java.security file, FIPS-only environment without BCFKS support); pinning a jcaProvider that does not support an in-memory store type.
Related errors
- Configured truststore 'trustStorePath' holds no X.509 certif
- Failed to set the certificate
- Failed to set the private key
- Issuer URL does not use https, but must:
- Failed to acquire TLS material for purpose ${purpose}
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/02dd899354940fbc.
Report an issue: GitHub.