{"record":{"id":"d110771297b17091","repo":"nautechsystems/nautilus_trader","slug":"failed-to-parse-pem-e","errorCode":null,"errorMessage":"Failed to parse PEM: {e}","messagePattern":"Failed to parse PEM: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/cryptography/src/signing.rs","lineNumber":50,"sourceCode":"    let tag = hmac::sign(&key, data.as_bytes());\n    Ok(hex::encode(tag.as_ref()))\n}\n\n/// Signs `data` using RSA PKCS#1 v1.5 SHA-256 with the provided private key in PEM format.\n///\n/// # Errors\n///\n/// Returns an error if:\n/// - `data` is empty.\n/// - `private_key_pem` is not a valid PEM-encoded PKCS#8 RSA private key or cannot be parsed.\n/// - Signature generation fails due to key or cryptographic errors.\npub fn rsa_signature(private_key_pem: &str, data: &str) -> anyhow::Result<String> {\n    if data.is_empty() {\n        anyhow::bail!(\"Query string cannot be empty\");\n    }\n\n    // Remove PEM headings and decode to DER bytes using the `pem` crate\n    let pem = pem::parse(private_key_pem.trim())\n        .map_err(|e| anyhow::anyhow!(\"Failed to parse PEM: {e}\"))?;\n\n    // Ensure this is a private key\n    if !pem.tag().ends_with(\"PRIVATE KEY\") {\n        anyhow::bail!(\"PEM does not contain a private key\");\n    }\n\n    // Construct RSA key pair from PKCS#8 DER bytes\n    let key_pair = KeyPair::from_pkcs8(pem.contents())\n        .map_err(|_| anyhow::anyhow!(\"Failed to decode RSA private key\"))?;\n\n    // Prepare RNG and output buffer (signature length = modulus length)\n    let rng = lc_rand::SystemRandom::new();\n    let mut signature = vec![0u8; key_pair.public_modulus_len()];\n\n    key_pair\n        .sign(\n            &lc_signature::RSA_PKCS1_SHA256,","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/cryptography/src/signing.rs#L32-L68","documentation":"`rsa_signature` first parses `private_key_pem` with the `pem` crate to extract DER bytes. This error means the input is not syntactically valid PEM (missing BEGIN/END headers, bad base64 body, stray whitespace/characters) so parsing failed. The underlying `pem` crate error is included in the message.","triggerScenarios":"Calling `rsa_signature`/`py_rsa_signature` with a string that is not valid PEM: truncated key, JSON-escaped '\\n' instead of real newlines, base64 corruption, or a completely different format (raw DER, SSH format, PGP block).","commonSituations":"Reading the key from an env var where newlines were flattened to spaces or '\\n' literals; copying the key from a web console and losing formatting; passing a .der file's contents instead of PEM; wrong file read (public cert instead of key).","solutions":["Verify the key text starts with '-----BEGIN PRIVATE KEY-----' and ends with '-----END PRIVATE KEY-----' with real newlines","If stored in an env var or JSON, convert escaped '\\n' to actual newlines before passing","Ensure the file being read is the PEM private key, not DER or a certificate","Read the pem crate error in the message to pinpoint whether it's base64 or structure"],"exampleFix":"// before\nlet sig = rsa_signature(&env_key, query)?; // env_key contains \"-----BEGIN\\n...\" escaped\n// after\nlet pem_text = env_key.replace(\"\\\\n\", \"\\n\");\nlet sig = rsa_signature(&pem_text, query)?;","handlingStrategy":"validation","validationCode":"fn looks_like_pem(s: &str) -> bool {\n    s.trim_start().starts_with(\"-----BEGIN\") && s.contains(\"-----END\")\n}\nif !looks_like_pem(&key) {\n    // fix loading/escaping before calling rsa_signature\n}","typeGuard":"fn is_private_key_pem(s: &str) -> bool {\n    let t = s.trim();\n    t.starts_with(\"-----BEGIN PRIVATE KEY-----\")\n        || t.starts_with(\"-----BEGIN RSA PRIVATE KEY-----\")\n}","tryCatchPattern":"match rsa_signature(&pem, query) {\n    Ok(sig) => use(sig),\n    Err(e) if e.to_string().starts_with(\"Failed to parse PEM\") => {\n        tracing::error!(\"bad key material: {e}\"); // check newlines/escaping\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Store keys as files, not env vars, to avoid newline mangling","Unescape '\\\\n' when keys pass through JSON/env layers","Validate PEM headers before invoking crypto"],"tags":["rust","cryptography","pem","format"],"backgroundTag":"invalid-argument-format","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}