{"record":{"id":"1defaf85b888aa8b","repo":"aaif-goose/goose","slug":"failed-to-encode-pkcs-8","errorCode":null,"errorMessage":"Failed to encode PKCS#8: {}","messagePattern":"Failed to encode PKCS#8: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/goose-providers/src/api_client.rs","lineNumber":173,"sourceCode":"/// PKI, etc.) would get a cryptic \"Failed to create identity\" error and need to manually\n/// run `openssl pkey -in key.pem -out key-pkcs8.pem`.\n///\n/// Note: the rustls code path (`Identity::from_pem`) accepts all formats natively,\n/// so this conversion is only needed for native-tls.\n#[cfg(feature = \"native-tls\")]\nfn convert_key_to_pkcs8_pem(key_pem_str: &str) -> Result<String> {\n    use pkcs8::der::{Decode, Encode};\n\n    let parsed =\n        pem::parse(key_pem_str).map_err(|e| anyhow::anyhow!(\"Failed to parse PEM key: {}\", e))?;\n\n    match parsed.tag() {\n        \"PRIVATE KEY\" => Ok(key_pem_str.to_string()),\n        \"RSA PRIVATE KEY\" => {\n            let info = pkcs8::PrivateKeyInfo::new(pkcs1::ALGORITHM_ID, parsed.contents());\n            let der_bytes = info\n                .to_der()\n                .map_err(|e| anyhow::anyhow!(\"Failed to encode PKCS#8: {}\", e))?;\n            Ok(pem::encode(&pem::Pem::new(\"PRIVATE KEY\", der_bytes)))\n        }\n        \"EC PRIVATE KEY\" => {\n            let ec_key = sec1::EcPrivateKey::from_der(parsed.contents())\n                .map_err(|e| anyhow::anyhow!(\"Failed to parse EC key: {}\", e))?;\n            let curve_oid = ec_key\n                .parameters\n                .and_then(|p| p.named_curve())\n                .ok_or_else(|| {\n                    anyhow::anyhow!(\n                        \"EC key missing curve parameters. Convert to PKCS#8: \\\n                         openssl pkey -in key.pem -out key-pkcs8.pem\"\n                    )\n                })?;\n            let algorithm = pkcs8::AlgorithmIdentifierRef {\n                oid: sec1::ALGORITHM_OID,\n                parameters: Some((&curve_oid).into()),\n            };","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose-providers/src/api_client.rs#L155-L191","documentation":"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}'.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Validate the key independently: openssl rsa -in key.pem -check -noout","Re-copy the key preserving line breaks, or re-transfer the file (checksum it)","If validation fails, regenerate the key pair and reissue the certificate","Or convert on a trusted machine with openssl pkey and ship the resulting PKCS#8 PEM ('PRIVATE KEY' tag) which bypasses this conversion path"],"exampleFix":"# before\nclient_identity:\n  key_path: /etc/goose/tls/client-key.pem   # 'RSA PRIVATE KEY', corrupted Base64\n\n# after (shell): validate then convert to PKCS#8 on a trusted host\n$ openssl rsa -in client-key.pem -check -noout\n$ openssl pkey -in client-key.pem -out client-key-pkcs8.pem\n# key_path: /etc/goose/tls/client-key-pkcs8.pem","handlingStrategy":"validation","validationCode":"let key_text = std::fs::read_to_string(&key_path)?;\nif key_text.contains(\"-----BEGIN RSA PRIVATE KEY-----\") {\n    let ok = std::process::Command::new(\"openssl\")\n        .args([\"rsa\", \"-in\", &key_path, \"-check\", \"-noout\"])\n        .status().map(|s| s.success()).unwrap_or(false);\n    anyhow::ensure!(ok, \"RSA key is corrupt; regenerate or re-transfer it\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Checksum TLS material after every copy (sha256sum on both ends)","Avoid pasting keys through channels that wrap or truncate lines; use file transfer","Convert to PKCS#8 once with openssl pkey and store that — the 'PRIVATE KEY' path needs no runtime conversion","Regenerate and reissue on any checksum mismatch instead of hand-repairing Base64"],"tags":["rust","tls","native-tls","pem","rsa","pkcs8"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}