aaif-goose/goose · error

Failed to encode PKCS#8: {}

Error message

Failed to encode PKCS#8: {}

What it means

Second stage of native-tls key conversion: for a 'RSA PRIVATE KEY' (PKCS#1) PEM, convert_key_to_pkcs8_pem wraps the parsed PKCS#1 bytes in a PKCS8 PrivateKeyInfo and encodes them to DER. If the RSA key payload is structurally invalid (corrupted Base64 body, truncated modulus, zero-length integers), to_der() fails with 'Failed to encode PKCS#8: {err}'.

Source

Thrown at crates/goose-providers/src/api_client.rs:173

/// PKI, etc.) would get a cryptic "Failed to create identity" error and need to manually
/// run `openssl pkey -in key.pem -out key-pkcs8.pem`.
///
/// Note: the rustls code path (`Identity::from_pem`) accepts all formats natively,
/// so this conversion is only needed for native-tls.
#[cfg(feature = "native-tls")]
fn convert_key_to_pkcs8_pem(key_pem_str: &str) -> Result<String> {
    use pkcs8::der::{Decode, Encode};

    let parsed =
        pem::parse(key_pem_str).map_err(|e| anyhow::anyhow!("Failed to parse PEM key: {}", e))?;

    match parsed.tag() {
        "PRIVATE KEY" => Ok(key_pem_str.to_string()),
        "RSA PRIVATE KEY" => {
            let info = pkcs8::PrivateKeyInfo::new(pkcs1::ALGORITHM_ID, parsed.contents());
            let der_bytes = info
                .to_der()
                .map_err(|e| anyhow::anyhow!("Failed to encode PKCS#8: {}", e))?;
            Ok(pem::encode(&pem::Pem::new("PRIVATE KEY", der_bytes)))
        }
        "EC PRIVATE KEY" => {
            let ec_key = sec1::EcPrivateKey::from_der(parsed.contents())
                .map_err(|e| anyhow::anyhow!("Failed to parse EC key: {}", e))?;
            let curve_oid = ec_key
                .parameters
                .and_then(|p| p.named_curve())
                .ok_or_else(|| {
                    anyhow::anyhow!(
                        "EC key missing curve parameters. Convert to PKCS#8: \
                         openssl pkey -in key.pem -out key-pkcs8.pem"
                    )
                })?;
            let algorithm = pkcs8::AlgorithmIdentifierRef {
                oid: sec1::ALGORITHM_OID,
                parameters: Some((&curve_oid).into()),
            };

View on GitHub (pinned to 3810898a74)

Solutions

  1. Validate the key independently: openssl rsa -in key.pem -check -noout
  2. Re-copy the key preserving line breaks, or re-transfer the file (checksum it)
  3. If validation fails, regenerate the key pair and reissue the certificate
  4. Or convert on a trusted machine with openssl pkey and ship the resulting PKCS#8 PEM ('PRIVATE KEY' tag) which bypasses this conversion path

Example fix

# before
client_identity:
  key_path: /etc/goose/tls/client-key.pem   # 'RSA PRIVATE KEY', corrupted Base64

# after (shell): validate then convert to PKCS#8 on a trusted host
$ openssl rsa -in client-key.pem -check -noout
$ openssl pkey -in client-key.pem -out client-key-pkcs8.pem
# key_path: /etc/goose/tls/client-key-pkcs8.pem
Defensive patterns

Strategy: validation

Validate before calling

let key_text = std::fs::read_to_string(&key_path)?;
if key_text.contains("-----BEGIN RSA PRIVATE KEY-----") {
    let ok = std::process::Command::new("openssl")
        .args(["rsa", "-in", &key_path, "-check", "-noout"])
        .status().map(|s| s.success()).unwrap_or(false);
    anyhow::ensure!(ok, "RSA key is corrupt; regenerate or re-transfer it");
}

Prevention

When it happens

Trigger: The key file has correct PEM armor and tag 'RSA PRIVATE KEY', but the enclosed PKCS#1 structure is malformed — truncated file, mangled Base64 from copy-paste, or a key produced by a broken generator. pem::parse succeeded (error 258 did not fire); the DER-level re-encode is what fails.

Common situations: Keys pasted into YAML/JSON with line-wrap corruption; CI redacting chunks of long Base64 lines; partially overwritten secret files; keys truncated by editor line-length limits.

Related errors


AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16). Data as JSON: /api/errors/1defaf85b888aa8b. Report an issue: GitHub.