diff --git a/openssl/examples/mk_certs.rs b/openssl/examples/mk_certs.rs new file mode 100644 index 0000000000000000000000000000000000000000..c64dc007ff9348a07c1371d538fae241edd420d4 --- /dev/null +++ b/openssl/examples/mk_certs.rs @@ -0,0 +1,152 @@ +//! A program that generates ca certs, certs verified by the ca, and public +//! and private keys. + +extern crate openssl; + +use openssl::asn1::Asn1Time; +use openssl::bn::{BigNum, MSB_MAYBE_ZERO}; +use openssl::error::ErrorStack; +use openssl::hash::MessageDigest; +use openssl::pkey::{PKey, PKeyRef}; +use openssl::rsa::Rsa; +use openssl::x509::{X509, X509Ref}; +use openssl::x509::{X509NameBuilder, X509Req, X509ReqBuilder}; +use openssl::x509::extension::{AuthorityKeyIdentifier, BasicConstraints, KeyUsage, + SubjectAlternativeName, SubjectKeyIdentifier}; + +/// Make a CA certificate and private key +fn mk_ca_cert() -> Result<(X509, PKey), ErrorStack> { + let rsa = Rsa::generate(2048)?; + let privkey = PKey::from_rsa(rsa)?; + + let mut x509_name = X509NameBuilder::new()?; + x509_name.append_entry_by_text("C", "US")?; + x509_name.append_entry_by_text("ST", "TX")?; + x509_name.append_entry_by_text("O", "Some CA organization")?; + x509_name.append_entry_by_text("CN", "ca test")?; + let x509_name = x509_name.build(); + + let mut cert_builder = X509::builder()?; + cert_builder.set_version(2)?; + let serial_number = { + let mut serial = BigNum::new()?; + serial.rand(159, MSB_MAYBE_ZERO, false)?; + serial.to_asn1_integer()? + }; + cert_builder.set_serial_number(&serial_number)?; + cert_builder.set_subject_name(&x509_name)?; + cert_builder.set_issuer_name(&x509_name)?; + cert_builder.set_pubkey(&privkey)?; + let not_before = Asn1Time::days_from_now(0)?; + cert_builder.set_not_before(¬_before)?; + let not_after = Asn1Time::days_from_now(365)?; + cert_builder.set_not_after(¬_after)?; + + cert_builder.append_extension(BasicConstraints::new().critical().ca().build()?)?; + cert_builder.append_extension(KeyUsage::new() + .critical() + .key_cert_sign() + .crl_sign() + .build()?)?; + + let subject_key_identifier = + SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?; + cert_builder.append_extension(subject_key_identifier)?; + + cert_builder.sign(&privkey, MessageDigest::sha256())?; + let cert = cert_builder.build(); + + Ok((cert, privkey)) +} + +/// Make a X509 request with the given private key +fn mk_request(privkey: &PKey) -> Result<(X509Req), ErrorStack> { + let mut req_builder = X509ReqBuilder::new()?; + req_builder.set_pubkey(&privkey)?; + + let mut x509_name = X509NameBuilder::new()?; + x509_name.append_entry_by_text("C", "US")?; + x509_name.append_entry_by_text("ST", "TX")?; + x509_name.append_entry_by_text("O", "Some organization")?; + x509_name.append_entry_by_text("CN", "www.example.com")?; + let x509_name = x509_name.build(); + req_builder.set_subject_name(&x509_name)?; + + req_builder.sign(&privkey, MessageDigest::sha256())?; + let req = req_builder.build(); + Ok(req) +} + +/// Make a certificate and private key signed by the given CA cert and private key +fn mk_ca_signed_cert(ca_cert: &X509Ref, ca_privkey: &PKeyRef) -> Result<(X509, PKey), ErrorStack> { + let rsa = Rsa::generate(2048)?; + let privkey = PKey::from_rsa(rsa)?; + + let req = mk_request(&privkey)?; + + let mut cert_builder = X509::builder()?; + cert_builder.set_version(2)?; + let serial_number = { + let mut serial = BigNum::new()?; + serial.rand(159, MSB_MAYBE_ZERO, false)?; + serial.to_asn1_integer()? + }; + cert_builder.set_serial_number(&serial_number)?; + cert_builder.set_subject_name(req.subject_name())?; + cert_builder.set_issuer_name(ca_cert.subject_name())?; + cert_builder.set_pubkey(&privkey)?; + let not_before = Asn1Time::days_from_now(0)?; + cert_builder.set_not_before(¬_before)?; + let not_after = Asn1Time::days_from_now(365)?; + cert_builder.set_not_after(¬_after)?; + + cert_builder.append_extension(BasicConstraints::new().build()?)?; + + cert_builder.append_extension(KeyUsage::new() + .critical() + .non_repudiation() + .digital_signature() + .key_encipherment() + .build()?)?; + + let subject_key_identifier = SubjectKeyIdentifier::new() + .build(&cert_builder.x509v3_context(Some(ca_cert), None))?; + cert_builder.append_extension(subject_key_identifier)?; + + let auth_key_identifier = AuthorityKeyIdentifier::new() + .keyid(false) + .issuer(false) + .build(&cert_builder.x509v3_context(Some(ca_cert), None))?; + cert_builder.append_extension(auth_key_identifier)?; + + let subject_alt_name = SubjectAlternativeName::new() + .dns("*.example.com") + .dns("hello.com") + .build(&cert_builder.x509v3_context(Some(ca_cert), None))?; + cert_builder.append_extension(subject_alt_name)?; + + cert_builder.sign(&ca_privkey, MessageDigest::sha256())?; + let cert = cert_builder.build(); + + Ok((cert, privkey)) +} + +fn real_main() -> Result<(), ErrorStack> { + let (ca_cert, ca_privkey) = mk_ca_cert()?; + let (cert, _privkey) = mk_ca_signed_cert(&ca_cert, &ca_privkey)?; + + // Verify that this cert was issued by this ca + match ca_cert.issued(&cert) { + Err(ver_err) => println!("Failed to verify certificate: {}", ver_err), + Ok(()) => println!("Certificate verified!"), + }; + + Ok(()) +} + +fn main() { + match real_main() { + Ok(()) => println!("Finished."), + Err(e) => println!("Error: {}", e), + }; +}