nautechsystems/nautilus_trader · error

Failed to parse PEM: {e}

Error message

Failed to parse PEM: {e}

What it means

`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.

Source

Thrown at crates/cryptography/src/signing.rs:50

    let tag = hmac::sign(&key, data.as_bytes());
    Ok(hex::encode(tag.as_ref()))
}

/// Signs `data` using RSA PKCS#1 v1.5 SHA-256 with the provided private key in PEM format.
///
/// # Errors
///
/// Returns an error if:
/// - `data` is empty.
/// - `private_key_pem` is not a valid PEM-encoded PKCS#8 RSA private key or cannot be parsed.
/// - Signature generation fails due to key or cryptographic errors.
pub fn rsa_signature(private_key_pem: &str, data: &str) -> anyhow::Result<String> {
    if data.is_empty() {
        anyhow::bail!("Query string cannot be empty");
    }

    // Remove PEM headings and decode to DER bytes using the `pem` crate
    let pem = pem::parse(private_key_pem.trim())
        .map_err(|e| anyhow::anyhow!("Failed to parse PEM: {e}"))?;

    // Ensure this is a private key
    if !pem.tag().ends_with("PRIVATE KEY") {
        anyhow::bail!("PEM does not contain a private key");
    }

    // Construct RSA key pair from PKCS#8 DER bytes
    let key_pair = KeyPair::from_pkcs8(pem.contents())
        .map_err(|_| anyhow::anyhow!("Failed to decode RSA private key"))?;

    // Prepare RNG and output buffer (signature length = modulus length)
    let rng = lc_rand::SystemRandom::new();
    let mut signature = vec![0u8; key_pair.public_modulus_len()];

    key_pair
        .sign(
            &lc_signature::RSA_PKCS1_SHA256,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the key text starts with '-----BEGIN PRIVATE KEY-----' and ends with '-----END PRIVATE KEY-----' with real newlines
  2. If stored in an env var or JSON, convert escaped '\n' to actual newlines before passing
  3. Ensure the file being read is the PEM private key, not DER or a certificate
  4. Read the pem crate error in the message to pinpoint whether it's base64 or structure

Example fix

// before
let sig = rsa_signature(&env_key, query)?; // env_key contains "-----BEGIN\n..." escaped
// after
let pem_text = env_key.replace("\\n", "\n");
let sig = rsa_signature(&pem_text, query)?;
Defensive patterns

Strategy: validation

Validate before calling

fn looks_like_pem(s: &str) -> bool {
    s.trim_start().starts_with("-----BEGIN") && s.contains("-----END")
}
if !looks_like_pem(&key) {
    // fix loading/escaping before calling rsa_signature
}

Type guard

fn is_private_key_pem(s: &str) -> bool {
    let t = s.trim();
    t.starts_with("-----BEGIN PRIVATE KEY-----")
        || t.starts_with("-----BEGIN RSA PRIVATE KEY-----")
}

Try / catch

match rsa_signature(&pem, query) {
    Ok(sig) => use(sig),
    Err(e) if e.to_string().starts_with("Failed to parse PEM") => {
        tracing::error!("bad key material: {e}"); // check newlines/escaping
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: 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).

Common situations: 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).

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/d110771297b17091. Report an issue: GitHub.