ory/hydra · error

unable to load X509 key pair: %v

Error message

unable to load X509 key pair: %v

What it means

After both base64 decodes succeed, CertificateFromBase64 calls tls.X509KeyPair to parse the PEM cert and key. This error means the bytes are valid base64 but do not form a valid X.509 key pair: bad PEM blocks, mismatched cert/key, unsupported key format, or expired/malformed certificate.

Source

Thrown at oryx/tlsx/cert.go:84

- ` + prefix + `_KEY: Base64 encoded (without padding) string of the private key (PEM encoded) to be used for HTTP over TLS (HTTPS).
	Example: ` + prefix + `_KEY="-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDg..."
`
}

// CertificateFromBase64 loads a TLS certificate from a base64-encoded string of
// the PEM representations of the cert and key.
func CertificateFromBase64(certBase64, keyBase64 string) (tls.Certificate, error) {
	certPEM, err := base64.StdEncoding.DecodeString(certBase64)
	if err != nil {
		return tls.Certificate{}, fmt.Errorf("unable to base64 decode the TLS certificate: %v", err)
	}
	keyPEM, err := base64.StdEncoding.DecodeString(keyBase64)
	if err != nil {
		return tls.Certificate{}, fmt.Errorf("unable to base64 decode the TLS private key: %v", err)
	}
	cert, err := tls.X509KeyPair(certPEM, keyPEM)
	if err != nil {
		return tls.Certificate{}, fmt.Errorf("unable to load X509 key pair: %v", err)
	}
	return cert, nil
}

// [deprecated] Certificate returns a TLS Certificate by looking at its
// arguments. If both certPEMBase64 and keyPEMBase64 are not empty and contain
// base64-encoded PEM representations of a cert and key, respectively, that key
// pair is returned. Otherwise, if certPath and keyPath point to PEM files, the
// key pair is loaded from those. Returns ErrNoCertificatesConfigured if all
// arguments are empty, and ErrInvalidCertificateConfiguration if the arguments
// are inconsistent.
//
// This function is deprecated. Use CertificateFromBase64 or GetCertificate
// instead.
func Certificate(
	certPEMBase64, keyPEMBase64 string,
	certPath, keyPath string,
) ([]tls.Certificate, error) {

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Convert to PEM if needed (openssl x509 -inform der -outform pem) and ensure key is unencrypted PEM (PKCS#8: openssl pkcs8 -topk8 -nocrypt)
  2. Verify the pair matches: compare `openssl x509 -noout -modulus` of cert with `openssl rsa -noout -modulus` of key
  3. Re-encode the exact PEM files with base64 (avoid double-encoding) and test with `openssl x509` / `openssl pkey` locally
  4. Rotate cert and key together so they stay a matched pair

Example fix

// before
// certPEM was DER binary, key PEM encrypted -> X509KeyPair fails
// after
// openssl x509 -inform der -in cert.der -out cert.pem
// openssl pkcs8 -topk8 -nocrypt -in key.pem -out key.pem
certB64 := base64.StdEncoding.EncodeToString(pemBytes("cert.pem"))
keyB64 := base64.StdEncoding.EncodeToString(pemBytes("key.pem"))
Defensive patterns

Strategy: validation

Validate before calling

func validPair(certPEM, keyPEM []byte) error {
    _, err := tls.X509KeyPair(certPEM, keyPEM)
    return err // run after decoding base64, before use
}

Try / catch

cert, err := tlsx.CertificateFromBase64(certB64, keyB64)
if err != nil && strings.Contains(err.Error(), "X509 key pair") {
    log.WithError(err).Error("cert/key are valid base64 but not a valid matching PEM pair; rotate together")
    return err
}

Prevention

When it happens

Trigger: Calling CertificateFromBase64 where the base64 decodes but the plaintext is not PEM (double-encoded base64, binary DER instead of PEM, missing -----BEGIN/END----- markers), or the key does not correspond to the certificate's public key.

Common situations: Encoding an already-base64 file twice, using a DER-encoded cert, key format not supported by crypto/tls (e.g. certain encrypted or non-PKCS#8 keys), rotated cert without rotating the key, or concatenating multiple certs in a way X509KeyPair rejects.

Related errors


AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03). Data as JSON: /api/errors/b8d62716e65c5c21. Report an issue: GitHub.