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

  1. Inspect the cause attached to the KeyStoreException — it holds the real error from the JCA provider.
  2. 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.
  3. Check the JVM's java.security and installed security providers; restore the default JDK configuration if it was modified.
  4. 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

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


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/02dd899354940fbc. Report an issue: GitHub.