diff --git a/rcgen/Cargo.toml b/rcgen/Cargo.toml index 0e52f9aa..2b10c1f2 100644 --- a/rcgen/Cargo.toml +++ b/rcgen/Cargo.toml @@ -39,6 +39,10 @@ required-features = ["pem"] name = "sign-leaf-with-ca" required-features = ["pem", "x509-parser"] +[[example]] +name = "sign-leaf-with-pem-files" +required-features = ["pem", "x509-parser"] + [[example]] name = "simple" required-features = ["crypto", "pem"] diff --git a/rcgen/examples/sign-leaf-with-pem-files.rs b/rcgen/examples/sign-leaf-with-pem-files.rs new file mode 100644 index 00000000..33ee4436 --- /dev/null +++ b/rcgen/examples/sign-leaf-with-pem-files.rs @@ -0,0 +1,82 @@ +//! Generate a new certificate, and sign it with an existing root or +//! intermediate certificate. +//! +//! Requires four positional command line arguments: +//! * File path to PEM containing signer's key pair +//! * File path to PEM containing signer's certificate +//! * File path for generated PEM containing output key pair +//! * File path for generated PEM containing output certificate + +use std::error::Error; +use std::fs; +use std::path::PathBuf; + +use rcgen::{CertificateParams, DnType, ExtendedKeyUsagePurpose, Issuer, KeyPair, KeyUsagePurpose}; +use time::{Duration, OffsetDateTime}; + +fn main() -> Result<(), Box> { + let mut args = std::env::args().skip(1); + + let signer_keys_file = PathBuf::from( + args.next() + .ok_or("provide signer's pem keys file as 1st argument")?, + ); + + let signer_cert_file = PathBuf::from( + args.next() + .ok_or("provide signer's pem certificate file as 2nd argument")?, + ); + + let output_keys_file = + PathBuf::from(args.next().ok_or("output pem keys file as 3rd argument")?); + + let output_cert_file = PathBuf::from(args.next().ok_or("output pem cert file as 4th fourth")?); + + // Read existing certificate authority + let keys_pem = fs::read_to_string(&signer_keys_file)?; + let cert_pem = fs::read_to_string(&signer_cert_file)?; + + let key_pair = KeyPair::from_pem(&keys_pem)?; + let signer = Issuer::from_ca_cert_pem(&cert_pem, key_pair)?; + + // Create a new signed server certificate + const DOMAIN: &str = "example.domain"; + + let sans = vec![DOMAIN.into()]; + + let mut params = CertificateParams::new(sans)?; + + params.distinguished_name.push(DnType::CommonName, DOMAIN); + params.use_authority_key_identifier_extension = true; + params.key_usages.push(KeyUsagePurpose::DigitalSignature); + params + .extended_key_usages + .push(ExtendedKeyUsagePurpose::ServerAuth); + + const DAY: Duration = Duration::days(1); + + let yesterday = OffsetDateTime::now_utc() + .checked_sub(DAY) + .ok_or("invalid yesterday")?; + + let tomorrow = OffsetDateTime::now_utc() + .checked_add(DAY) + .ok_or("invalid tomorrow")?; + + params.not_before = yesterday; + params.not_after = tomorrow; + + let output_keys = KeyPair::generate()?; + let output_cert = params.signed_by(&output_keys, &signer)?; + + // Write new certificate + fs::write(&output_keys_file, output_keys.serialize_pem())?; + fs::write(&output_cert_file, output_cert.pem())?; + + println!("Wrote signed leaf certificate:"); + println!(" keys: {}", output_keys_file.display()); + println!(" cert: {}", output_cert_file.display()); + println!(); + + Ok(()) +}