aaif-goose/goose · error
Failed to create identity from cert and key: {}
Error message
Failed to create identity from cert and key: {} What it means
On the rustls path (default when the native-tls feature is off), load_identity concatenates the cert PEM and key PEM and calls Identity::from_pem. rustls only accepts PKCS#8 keys (a 'PRIVATE KEY' PEM block); if the key is PKCS#1 ('RSA PRIVATE KEY'), SEC1 ('EC PRIVATE KEY'), encrypted, or the cert/key pair is malformed or mismatched, parsing fails with 'Failed to create identity from cert and key: {err}'.
Source
Thrown at crates/goose-providers/src/api_client.rs:100
}
pub fn is_configured(&self) -> bool {
self.client_identity.is_some() || self.ca_cert_path.is_some()
}
#[cfg(any(feature = "rustls-tls", feature = "native-tls"))]
fn load_identity(&self) -> Result<Option<Identity>> {
if let Some(cert_key_pair) = &self.client_identity {
let cert_pem = read_to_string(&cert_key_pair.cert_path)
.map_err(|e| anyhow::anyhow!("Failed to read client certificate: {}", e))?;
let key_pem = read_to_string(&cert_key_pair.key_path)
.map_err(|e| anyhow::anyhow!("Failed to read client private key: {}", e))?;
#[cfg(not(feature = "native-tls"))]
let identity = {
let combined_pem = format!("{}\n{}", cert_pem, key_pem);
Identity::from_pem(combined_pem.as_bytes()).map_err(|e| {
anyhow::anyhow!("Failed to create identity from cert and key: {}", e)
})?
};
#[cfg(feature = "native-tls")]
let identity = {
let pkcs8_key_pem = convert_key_to_pkcs8_pem(&key_pem)?;
Identity::from_pkcs8_pem(cert_pem.as_bytes(), pkcs8_key_pem.as_bytes()).map_err(
|e| anyhow::anyhow!("Failed to create identity from cert and key: {}", e),
)?
};
Ok(Some(identity))
} else {
Ok(None)
}
}
#[cfg(any(feature = "rustls-tls", feature = "native-tls"))]View on GitHub (pinned to 3810898a74)
Solutions
- Convert the key to PKCS#8 PEM: openssl pkey -in key.pem -out key-pkcs8.pem (works for RSA and EC inputs)
- Verify cert and key match: compare openssl x509 -noout -modulus -in cert.pem vs openssl rsa -noout -modulus -in key.pem (or use openssl x509 -checkend and pkey checks)
- If the key is passphrase-protected, decrypt it first: openssl pkey -in key.enc -passin file:pass.txt -out key-pkcs8.pem
- Re-issue a matching cert/key pair if they come from different generations
Example fix
# before
client_identity:
cert_path: /etc/goose/tls/client-cert.pem
key_path: /etc/goose/tls/client-key.pem # '-----BEGIN RSA PRIVATE KEY-----'
# after (shell)
$ openssl pkey -in /etc/goose/tls/client-key.pem -out /etc/goose/tls/client-key-pkcs8.pem
# client_identity.key_path now points at client-key-pkcs8.pem ('-----BEGIN PRIVATE KEY-----') Defensive patterns
Strategy: validation
Validate before calling
// fail fast: rustls needs PKCS#8 keys
let key = std::fs::read_to_string(&key_path)?;
if !key.contains("-----BEGIN PRIVATE KEY-----") {
anyhow::bail!(
"key is not PKCS#8; run: openssl pkey -in {key_path} -out {key_path}.pkcs8.pem"
);
} Type guard
fn is_pkcs8_pem(key_pem: &str) -> bool {
key_pem.contains("-----BEGIN PRIVATE KEY-----")
} Prevention
- Standardize on PKCS#8 PEM keys for all goose client identities
- Automate conversion: openssl pkey -in key.pem -out key-pkcs8.pem during secret provisioning
- Verify pairing after every cert rotation (compare public key hashes of cert and key)
- Never use passphrase-protected keys with rustls identities
When it happens
Trigger: Client mTLS configured with a legacy key format (openssl genrsa default PKCS#1, EC keys in SEC1 form), an encrypted/private password-protected key, or cert and key that do not match (cert's public key differs from key). Fires at client construction, after both files were read successfully.
Common situations: Corporate PKI issuing PKCS#1 keys from Kubernetes secrets; keys from older openssl versions; copy-pasted PEMs missing trailing newlines or with mangled armor; pairing a renewed certificate with an old key.
Related errors
- Failed to parse PEM key: {}
- Failed to encode PKCS#8: {}
- Failed to read client certificate: {}
- Failed to read client private key: {}
- Failed to parse CA certificate bundle: {}
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/f44e313c6c73dfc8.
Report an issue: GitHub.