Skip to content
ecdsa.com

Generate a CSR · Rust

How to generate a CSR for an EC P-256 key with SANs in Rust

In Rust, rcgen generates a CSR in a few lines: KeyPair::generate_for creates the P-256 key, CertificateParams::new takes the SANs, and serialize_request signs the PKCS#10 request with ECDSA and SHA-256. The run writes key.pem and csr.pem and decodes the CSR with OpenSSL.

Tested with rcgen 0.14.10 (Rust 1.99.1, default features (ring)).

Cargo.toml — rcgen with its default ring backend
[dependencies]
rcgen = "=0.14.10" # default features: ring backend and PEM output
src/main.rs — P-256 key and CSR with two SANs
use rcgen::{CertificateParams, DistinguishedName, DnType, KeyPair, PKCS_ECDSA_P256_SHA256};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let key = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256)?;
// new() takes the SANs (DNS names or IP addresses)
let mut params = CertificateParams::new(vec!["api.example.com".to_owned(), "www.example.com".to_owned()])?;
// Replace the default subject ("CN=rcgen self signed cert")
let mut subject = DistinguishedName::new();
subject.push(DnType::CountryName, "US");
subject.push(DnType::OrganizationName, "Example Corp");
subject.push(DnType::CommonName, "api.example.com");
params.distinguished_name = subject;
let csr = params.serialize_request(&key)?;
std::fs::write("csr.pem", csr.pem()?)?;
std::fs::write("key.pem", key.serialize_pem())?; // PKCS#8
Ok(())
}

How it works

  1. KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256) creates the key; the algorithm constant also fixes the signature algorithm.
  2. CertificateParams::new(vec![…]) turns each string into a DNS SAN, or an IP SAN if it parses as an IP address.
  3. DistinguishedName::new() plus push builds the subject in the order given.
  4. serialize_request(&key) returns the CSR; csr.pem() and key.serialize_pem() write the PEM files.

Gotchas

  • CertificateParams starts with the subject CN=rcgen self signed cert. Forgetting to replace distinguished_name sends that name to the CA.
  • CertificateParams also carries validity, key usages and CA flags that a CSR does not transmit; for a CSR only the subject, the SANs and the extensions requested matter.
  • rcgen's default features pull in ring; on platforms where ring does not build, use default-features = false with the aws_lc_rs and pem features.
  • serialize_request is not deterministic — two calls give different signatures for the same input — so do not compare CSR bytes in tests.

Related recipes