t8y2/dbx · error

JKS private key entry has no certificate chain

Error message

JKS private key entry has no certificate chain

What it means

loadClientKeyStore extracts a client certificate chain from a JKS keystore for mutual TLS. After appending each chain certificate, if none were collected, it returns this error: the PrivateKeyEntry exists but carries no certificate chain, so a tls.Certificate cannot be constructed.

Source

Thrown at agents/drivers/argo-go/zookeeper_tls.go:165

				continue
			}
			entry, getErr := store.GetPrivateKeyEntry(alias, passwordBytes)
			if getErr != nil {
				return tls.Certificate{}, getErr
			}
			privateKey, parseErr := parsePrivateKey(entry.PrivateKey)
			if parseErr != nil {
				return tls.Certificate{}, parseErr
			}
			result := tls.Certificate{PrivateKey: privateKey}
			for index, certificate := range entry.CertificateChain {
				result.Certificate = append(result.Certificate, certificate.Content)
				if index == 0 {
					result.Leaf, _ = x509.ParseCertificate(certificate.Content)
				}
			}
			if len(result.Certificate) == 0 {
				return tls.Certificate{}, errors.New("JKS private key entry has no certificate chain")
			}
			return result, nil
		}
		return tls.Certificate{}, errors.New("JKS keystore contains no private key entry")
	default:
		return tls.Certificate{}, fmt.Errorf("unsupported store type %q", storeType)
	}
}

func normalizedStoreType(storeType, path string) string {
	value := strings.ToUpper(strings.TrimSpace(storeType))
	switch value {
	case "P12", "PFX", "PKCS#12":
		return "PKCS12"
	case "X509", "X.509":
		return "PEM"
	case "":
		switch strings.ToLower(filepath.Ext(path)) {

View on GitHub (pinned to c0390bff16)

Solutions

  1. Regenerate/repair the entry so the chain is stored: keytool -genkeypair then -importcert the full chain, or use -importkeystore from a PKCS12 that includes the chain
  2. Verify with keytool -list -v -keystore keystore.jks that the entry is PrivateKeyEntry with a chain length >= 1
  3. If only a key without certs is needed, switch to a PEM keystore with cert+key files
  4. Check the store password so the private key entry is fully decrypted

Example fix

// before: key without chain
# keytool -genkeypair -alias client -keystore client.jks -dname cn=client
// after: include full chain
# keytool -certreq ... && keytool -importcert -file chain.p7b -keystore client.jks -alias client
Defensive patterns

Strategy: validation

Validate before calling

out, _ := exec.Command("keytool", "-list", "-v", "-keystore", keystorePath,
    "-storepass", password).Output()
if !strings.Contains(string(out), "PrivateKeyEntry") ||
    !strings.Contains(string(out), "Certificate chain length:") {
    return fmt.Errorf("keystore %s lacks a private key with chain", keystorePath)
}

Try / catch

cert, err := buildTLSConfig(...)
if errors.Is(err, errNoCertChain) { /* regenerate keystore */ }

Prevention

When it happens

Trigger: Building the ZooKeeper TLS config with a keystore whose private key entry has an empty certificate chain, e.g. keytool entries imported without a -chain or generated keypairs whose certs were deleted.

Common situations: Keystore created with 'keytool -genkeypair' in a environment where the self-signed chain was truncated; a JKS holding only trusted (TrustedCertEntry) certificates but no PrivateKeyEntry chain; corrupted keystore after migration between JDK versions.

Understand the failure class

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/218e06c1a505c2e2. Report an issue: GitHub.