risingwavelabs/risingwave · error · PsqlError

Failed to parse client key

Error message

Failed to parse client key

What it means

The client key bytes were read fine but PrivateKeyDer::from_pem_slice failed to decode them as a PEM-encoded private key (RSA, PKCS8, EC, etc.). This StartupError indicates the key material is not parseable by rustls, so the client-auth TLS config cannot be built and connection establishment stops.

Source

Thrown at src/utils/pgwire/src/ldap_auth.rs:156

                        .into(),
                )
            })?;
            let client_key_bytes = fs::read(key).map_err(|e| {
                PsqlError::StartupError(anyhow!(e).context("Failed to read client key").into())
            })?;
            let client_certs = CertificateDer::pem_slice_iter(&client_cert_bytes)
                .collect::<Result<Vec<_>, _>>()
                .map_err(|e| {
                    PsqlError::StartupError(
                        anyhow!(e)
                            .context("Failed to parse client certificate")
                            .into(),
                    )
                })?;

            let client_private_key =
                PrivateKeyDer::from_pem_slice(&client_key_bytes).map_err(|e| {
                    PsqlError::StartupError(anyhow!(e).context("Failed to parse client key").into())
                })?;

            tls_client_config
                .with_client_auth_cert(client_certs, client_private_key)
                .map_err(|err| {
                    PsqlError::StartupError(
                        anyhow!(err)
                            .context("Failed to set client certificate")
                            .into(),
                    )
                })
        } else {
            Ok(tls_client_config.with_no_client_auth())
        }
    }
}

/// LDAP configuration extracted from HBA entry

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Convert the key to unencrypted PKCS#8: openssl pkcs8 -topk8 -nocrypt -in key.pem -out key.pkcs8.pem
  2. Decrypt an encrypted key first: openssl rsa -in encrypted.key -out decrypted.key
  3. Confirm the file holds the private key (-----BEGIN PRIVATE KEY-----/BEGIN RSA PRIVATE KEY-----), not the cert
  4. Ensure the key corresponds to the configured client certificate

Example fix

// before
openssl req -newkey rsa:2048 -nodes  # produced 'BEGIN PRIVATE KEY' with local params
// after (normalize for rustls)
openssl pkcs8 -topk8 -nocrypt -in client.key -out client_pkcs8.key
# then: client_key = '/etc/rw/certs/client_pkcs8.key'
Defensive patterns

Strategy: validation

Validate before calling

fn is_parseable_key_pem(path: &str) -> bool {
    std::fs::read_to_string(path).map(|s| {
        ["-----BEGIN PRIVATE KEY-----", "-----BEGIN RSA PRIVATE KEY-----", "-----BEGIN EC PRIVATE KEY-----"]
            .iter().any(|h| s.starts_with(h)) && !s.contains("ENCRYPTED")
    }).unwrap_or(false)
}

Type guard

fn is_unencrypted_key_pem(s: &str) -> bool {
    s.contains("-----BEGIN") && s.contains("PRIVATE KEY-----") && !s.contains("ENCRYPTED")
}

Try / catch

catch PsqlError::StartupError, inspect the rustls pki-types error in the anyhow chain and re-raise with an 'openssl pkcs8 -topk8 -nocrypt' hint

Prevention

When it happens

Trigger: PrivateKeyDer::from_pem_slice(&client_key_bytes) returns Err — key file is malformed PEM, uses an unsupported key format, or is encrypted (password-protected) PEM

Common situations: Key is in a format rustls rejects (e.g. traditional 'BEGIN RSA PRIVATE KEY' with exotic parameters, or PKCS#12 .pfx); key is passphrase-encrypted; file actually contains a certificate or the CSR; key truncated or CRLF-mangled on Windows.

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 risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/70f55a2054186fe9. Report an issue: GitHub.