[dependencies]rcgen = "=0.14.10"time = "=0.3.55" # OffsetDateTime for the validity perioduse rcgen::{ CertificateParams, DistinguishedName, DnType, ExtendedKeyUsagePurpose, IsCa, KeyPair, KeyUsagePurpose, PKCS_ECDSA_P256_SHA256,};use time::{Duration, OffsetDateTime}; fn main() -> Result<(), Box<dyn std::error::Error>> { let key = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256)?; // Strings that parse as IP addresses become IP SANs, the rest DNS SANs let mut params = CertificateParams::new(vec!["localhost".to_owned(), "127.0.0.1".to_owned()])?; let mut subject = DistinguishedName::new(); subject.push(DnType::CommonName, "localhost"); params.distinguished_name = subject; // The defaults are a 1975-to-4096 validity and no basicConstraints: set both let now = OffsetDateTime::now_utc(); params.not_before = now; params.not_after = now + Duration::days(365); params.is_ca = IsCa::ExplicitNoCa; // writes CA:FALSE params.key_usages = vec![KeyUsagePurpose::DigitalSignature]; params.extended_key_usages = vec![ExtendedKeyUsagePurpose::ServerAuth]; let cert = params.self_signed(&key)?; std::fs::write("cert.pem", cert.pem())?; std::fs::write("key.pem", key.serialize_pem())?; Ok(())}How it works
KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256)creates the key and fixes the signature algorithm.CertificateParams::newmakes an IP SAN of every string that parses as an IP address and a DNS SAN of the rest.not_beforeandnot_aftertake atime::OffsetDateTime;IsCa::ExplicitNoCawrites a critical basicConstraints with CA:FALSE, and rcgen marks keyUsage critical as well.params.self_signed(&key)signs;cert.pem()returns the certificate andkey.serialize_pem()the key as PKCS#8.
Gotchas
CertificateParamsstarts with defaults meant for tests: the subjectCN=rcgen self signed certand a validity from 1975 to 4096.rcgen::generate_simple_self_signedkeeps all of them, plus no basicConstraints and no key usages.- With the default
IsCa::NoCa, rcgen writes no basicConstraints extension at all;IsCa::ExplicitNoCais what addsCA:FALSE. - Without
serial_number, rcgen derives the serial from a SHA-256 hash of the public key, so two certificates for the same key get the same serial. Setparams.serial_numberwhen you re-issue for a kept key. - rcgen does not re-export the time crate:
OffsetDateTime::now_utc()needstimeas a direct dependency (rcgen's owndate_time_ymdonly builds fixed dates).