1#![allow(clippy::uninlined_format_args)]
2
3//! A program that generates ca certs, certs verified by the ca, and public
4//! and private keys.
5
6use openssl::asn1::Asn1Time;
7use openssl::bn::{BigNum, MsbOption};
8use openssl::error::ErrorStack;
9use openssl::hash::MessageDigest;
10use openssl::pkey::{PKey, PKeyRef, Private};
11use openssl::rsa::Rsa;
12use openssl::x509::extension::{
13    AuthorityKeyIdentifier, BasicConstraints, KeyUsage, SubjectAlternativeName,
14    SubjectKeyIdentifier,
15};
16use openssl::x509::{X509NameBuilder, X509Ref, X509Req, X509ReqBuilder, X509VerifyResult, X509};
17
18/// Make a CA certificate and private key
19fn mk_ca_cert() -> Result<(X509, PKey<Private>), ErrorStack> {
20    let rsa = Rsa::generate(2048)?;
21    let key_pair = PKey::from_rsa(rsa)?;
22
23    let mut x509_name = X509NameBuilder::new()?;
24    x509_name.append_entry_by_text("C", "US")?;
25    x509_name.append_entry_by_text("ST", "TX")?;
26    x509_name.append_entry_by_text("O", "Some CA organization")?;
27    x509_name.append_entry_by_text("CN", "ca test")?;
28    let x509_name = x509_name.build();
29
30    let mut cert_builder = X509::builder()?;
31    cert_builder.set_version(2)?;
32    let serial_number = {
33        let mut serial = BigNum::new()?;
34        serial.rand(159, MsbOption::MAYBE_ZERO, false)?;
35        serial.to_asn1_integer()?
36    };
37    cert_builder.set_serial_number(&serial_number)?;
38    cert_builder.set_subject_name(&x509_name)?;
39    cert_builder.set_issuer_name(&x509_name)?;
40    cert_builder.set_pubkey(&key_pair)?;
41    let not_before = Asn1Time::days_from_now(0)?;
42    cert_builder.set_not_before(&not_before)?;
43    let not_after = Asn1Time::days_from_now(365)?;
44    cert_builder.set_not_after(&not_after)?;
45
46    cert_builder.append_extension(BasicConstraints::new().critical().ca().build()?)?;
47    cert_builder.append_extension(
48        KeyUsage::new()
49            .critical()
50            .key_cert_sign()
51            .crl_sign()
52            .build()?,
53    )?;
54
55    let subject_key_identifier =
56        SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?;
57    cert_builder.append_extension(subject_key_identifier)?;
58
59    cert_builder.sign(&key_pair, MessageDigest::sha256())?;
60    let cert = cert_builder.build();
61
62    Ok((cert, key_pair))
63}
64
65/// Make a X509 request with the given private key
66fn mk_request(key_pair: &PKey<Private>) -> Result<X509Req, ErrorStack> {
67    let mut req_builder = X509ReqBuilder::new()?;
68    req_builder.set_pubkey(key_pair)?;
69
70    let mut x509_name = X509NameBuilder::new()?;
71    x509_name.append_entry_by_text("C", "US")?;
72    x509_name.append_entry_by_text("ST", "TX")?;
73    x509_name.append_entry_by_text("O", "Some organization")?;
74    x509_name.append_entry_by_text("CN", "www.example.com")?;
75    let x509_name = x509_name.build();
76    req_builder.set_subject_name(&x509_name)?;
77
78    req_builder.sign(key_pair, MessageDigest::sha256())?;
79    let req = req_builder.build();
80    Ok(req)
81}
82
83/// Make a certificate and private key signed by the given CA cert and private key
84fn mk_ca_signed_cert(
85    ca_cert: &X509Ref,
86    ca_key_pair: &PKeyRef<Private>,
87) -> Result<(X509, PKey<Private>), ErrorStack> {
88    let rsa = Rsa::generate(2048)?;
89    let key_pair = PKey::from_rsa(rsa)?;
90
91    let req = mk_request(&key_pair)?;
92
93    let mut cert_builder = X509::builder()?;
94    cert_builder.set_version(2)?;
95    let serial_number = {
96        let mut serial = BigNum::new()?;
97        serial.rand(159, MsbOption::MAYBE_ZERO, false)?;
98        serial.to_asn1_integer()?
99    };
100    cert_builder.set_serial_number(&serial_number)?;
101    cert_builder.set_subject_name(req.subject_name())?;
102    cert_builder.set_issuer_name(ca_cert.subject_name())?;
103    cert_builder.set_pubkey(&key_pair)?;
104    let not_before = Asn1Time::days_from_now(0)?;
105    cert_builder.set_not_before(&not_before)?;
106    let not_after = Asn1Time::days_from_now(365)?;
107    cert_builder.set_not_after(&not_after)?;
108
109    cert_builder.append_extension(BasicConstraints::new().build()?)?;
110
111    cert_builder.append_extension(
112        KeyUsage::new()
113            .critical()
114            .non_repudiation()
115            .digital_signature()
116            .key_encipherment()
117            .build()?,
118    )?;
119
120    let subject_key_identifier =
121        SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(Some(ca_cert), None))?;
122    cert_builder.append_extension(subject_key_identifier)?;
123
124    let auth_key_identifier = AuthorityKeyIdentifier::new()
125        .keyid(false)
126        .issuer(false)
127        .build(&cert_builder.x509v3_context(Some(ca_cert), None))?;
128    cert_builder.append_extension(auth_key_identifier)?;
129
130    let subject_alt_name = SubjectAlternativeName::new()
131        .dns("*.example.com")
132        .dns("hello.com")
133        .build(&cert_builder.x509v3_context(Some(ca_cert), None))?;
134    cert_builder.append_extension(subject_alt_name)?;
135
136    cert_builder.sign(ca_key_pair, MessageDigest::sha256())?;
137    let cert = cert_builder.build();
138
139    Ok((cert, key_pair))
140}
141
142fn real_main() -> Result<(), ErrorStack> {
143    let (ca_cert, ca_key_pair) = mk_ca_cert()?;
144    let (cert, _key_pair) = mk_ca_signed_cert(&ca_cert, &ca_key_pair)?;
145
146    // Verify that this cert was issued by this ca
147    match ca_cert.issued(&cert) {
148        X509VerifyResult::OK => println!("Certificate verified!"),
149        ver_err => println!("Failed to verify certificate: {}", ver_err),
150    };
151
152    Ok(())
153}
154
155fn main() {
156    match real_main() {
157        Ok(()) => println!("Finished."),
158        Err(e) => println!("Error: {}", e),
159    };
160}
161