{"record":{"id":"f51e9682e932d898","repo":"aaif-goose/goose","slug":"ec-key-missing-curve-parameters-convert-to-pkcs-8","errorCode":null,"errorMessage":"EC key missing curve parameters. Convert to PKCS#8: openssl pkey -in key.pem -out key-pkcs8.pem","messagePattern":"EC key missing curve parameters\\. Convert to PKCS#8: openssl pkey -in key\\.pem -out key-pkcs8\\.pem","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/goose-providers/src/api_client.rs","lineNumber":183,"sourceCode":"        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            };\n            let info = pkcs8::PrivateKeyInfo::new(algorithm, 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        tag => Err(anyhow::anyhow!(\n            \"Unsupported key format '{}'. Expected PKCS#8, PKCS#1, or SEC1. \\\n             Convert with: openssl pkey -in key.pem -out key-pkcs8.pem\",\n            tag","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose-providers/src/api_client.rs#L165-L201","documentation":"The SEC1 EC private key parsed successfully, but ec_key.parameters.named_curve() returned None, meaning the key does not identify its curve by a standard named-curve OID (prime256v1, secp384r1, ...). goose's EC-to-PKCS#8 re-wrapper needs a named curve to build the PKCS#8 AlgorithmIdentifier, so it refuses the key and tells you to convert it with openssl pkey.","triggerScenarios":"A '-----BEGIN EC PRIVATE KEY-----' mTLS key whose parameters use an explicit/encoded curve (generated by very old OpenSSL, BouncyCastle, or an HSM export) or omit the curve entirely, supplied via TlsConfig::with_client_cert_and_key under the native-tls feature.","commonSituations":"Corporate PKI or IoT device certificates issuing keys with explicit parameters for curve agility; keys moved between crypto libraries that re-encode parameters; exotic curves (brainpool via explicit encoding) that have no registered named-curve OID in the sec1 crate's table.","solutions":["Convert the key so the curve becomes a named OID: openssl pkey -in key.pem -out key-pkcs8.pem (PKCS#8 also bypasses this whole conversion path)","If openssl also refuses, regenerate the key on a standard named curve: openssl ecparam -name prime256v1 -genkey -noout -out key.pem","Verify the curve is representable: openssl ec -in key.pem -noout -text should show 'ASN1 OID: prime256v1' (or secp384r1/secp521r1)"],"exampleFix":"# before: parameters omitted or explicit\nopenssl ec -in old-key.pem -noout -text   # shows no 'ASN1 OID' line\n\n# after\nopenssl pkey -in old-key.pem -out key-pkcs8.pem   # named curve + PKCS#8 wrapper","handlingStrategy":"validation","validationCode":"fn has_named_curve(path: &str) -> anyhow::Result<()> {\n    let out = std::process::Command::new(\"openssl\")\n        .args([\"ec\", \"-in\", path, \"-noout\", \"-text\"])\n        .output()?;\n    let text = String::from_utf8_lossy(&out.stdout);\n    anyhow::ensure!(out.status.success() && text.contains(\"ASN1 OID:\"),\n        \"key lacks a named curve; openssl pkey -in {path} -out {path}.pkcs8\");\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"match build_client_with_mtls(cert, key) {\n    Err(e) if e.to_string().contains(\"missing curve parameters\") => {\n        // auto-remediate once: convert with `openssl pkey`, retry, else surface the hint\n    }\n    r => r?,\n}","preventionTips":["Prefer named curves (prime256v1/secp384r1) when generating keys for mTLS","Standardize on PKCS#8 output so the SEC1 conversion path never executes","Document in your onboarding that HSM/exotic exports must be re-wrapped via openssl pkey before use"],"tags":["tls","crypto","ec-key","pem","native-tls"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}