Skip to main content

acme_lib/
cert.rs

1use lazy_static::lazy_static;
2use openssl::ec::{Asn1Flag, EcGroup, EcKey};
3use openssl::hash::MessageDigest;
4use openssl::nid::Nid;
5use openssl::pkey::{self, PKey};
6use openssl::rsa::Rsa;
7use openssl::stack::Stack;
8use openssl::x509::extension::SubjectAlternativeName;
9use openssl::x509::{X509Req, X509ReqBuilder, X509};
10
11use crate::Result;
12
13lazy_static! {
14    pub(crate) static ref EC_GROUP_P256: EcGroup = ec_group(Nid::X9_62_PRIME256V1);
15    pub(crate) static ref EC_GROUP_P384: EcGroup = ec_group(Nid::SECP384R1);
16}
17
18fn ec_group(nid: Nid) -> EcGroup {
19    let mut g = EcGroup::from_curve_name(nid).expect("EcGroup");
20    // this is required for openssl 1.0.x (but not 1.1.x)
21    g.set_asn1_flag(Asn1Flag::NAMED_CURVE);
22    g
23}
24
25/// Make an RSA private key (from which we can derive a public key).
26///
27/// This library does not check the number of bits used to create the key pair.
28/// For Let's Encrypt, the bits must be between 2048 and 4096.
29pub fn create_rsa_key(bits: u32) -> PKey<pkey::Private> {
30    let pri_key_rsa = Rsa::generate(bits).expect("Rsa::generate");
31    PKey::from_rsa(pri_key_rsa).expect("from_rsa")
32}
33
34/// Make a P-256 private key (from which we can derive a public key).
35pub fn create_p256_key() -> PKey<pkey::Private> {
36    let pri_key_ec = EcKey::generate(&*EC_GROUP_P256).expect("EcKey");
37    PKey::from_ec_key(pri_key_ec).expect("from_ec_key")
38}
39
40/// Make a P-384 private key pair (from which we can derive a public key).
41pub fn create_p384_key() -> PKey<pkey::Private> {
42    let pri_key_ec = EcKey::generate(&*EC_GROUP_P384).expect("EcKey");
43    PKey::from_ec_key(pri_key_ec).expect("from_ec_key")
44}
45
46pub(crate) fn create_csr(pkey: &PKey<pkey::Private>, domains: &[&str]) -> Result<X509Req> {
47    //
48    // the csr builder
49    let mut req_bld = X509ReqBuilder::new().expect("X509ReqBuilder");
50
51    // set private/public key in builder
52    req_bld.set_pubkey(pkey).expect("set_pubkey");
53
54    // set all domains as alt names
55    let mut stack = Stack::new().expect("Stack::new");
56    let ctx = req_bld.x509v3_context(None);
57    let mut an = SubjectAlternativeName::new();
58    for d in domains {
59        an.dns(d);
60    }
61
62    let ext = an.build(&ctx).expect("SubjectAlternativeName::build");
63    stack.push(ext).expect("Stack::push");
64    req_bld.add_extensions(&stack).expect("add_extensions");
65
66    // sign it
67    req_bld
68        .sign(pkey, MessageDigest::sha256())
69        .expect("csr_sign");
70
71    // the csr
72    Ok(req_bld.build())
73}
74
75/// Encapsulated certificate and private key.
76#[derive(Debug, Clone, PartialEq, Eq)]
77pub struct Certificate {
78    private_key: String,
79    certificate: String,
80}
81
82impl Certificate {
83    pub(crate) fn new(private_key: String, certificate: String) -> Self {
84        Certificate {
85            private_key,
86            certificate,
87        }
88    }
89
90    /// The PEM encoded private key.
91    pub fn private_key(&self) -> &str {
92        &self.private_key
93    }
94
95    /// The private key as DER.
96    pub fn private_key_der(&self) -> Vec<u8> {
97        let pkey = PKey::private_key_from_pem(self.private_key.as_bytes()).expect("from_pem");
98        pkey.private_key_to_der().expect("private_key_to_der")
99    }
100
101    /// The PEM encoded issued certificate.
102    pub fn certificate(&self) -> &str {
103        &self.certificate
104    }
105
106    /// The issued certificate as DER.
107    pub fn certificate_der(&self) -> Vec<u8> {
108        let x509 = X509::from_pem(self.certificate.as_bytes()).expect("from_pem");
109        x509.to_der().expect("to_der")
110    }
111
112    /// Inspect the certificate to count the number of (whole) valid days left.
113    ///
114    /// It's up to the ACME API provider to decide how long an issued certificate is valid.
115    /// Let's Encrypt sets the validity to 90 days. This function reports 89 days for newly
116    /// issued cert, since it counts _whole_ days.
117    ///
118    /// It is possible to get negative days for an expired certificate.
119    pub fn valid_days_left(&self) -> i64 {
120        // the cert used in the tests is not valid to load as x509
121        if cfg!(test) {
122            return 89;
123        }
124
125        // load as x509
126        let x509 = X509::from_pem(self.certificate.as_bytes()).expect("from_pem");
127
128        // convert asn1 time to Tm
129        let not_after = format!("{}", x509.not_after());
130        // Display trait produces this format, which is kinda dumb.
131        // Apr 19 08:48:46 2019 GMT
132        let expires = parse_date(&not_after);
133        let dur = expires - time::now();
134
135        dur.num_days()
136    }
137}
138
139fn parse_date(s: &str) -> time::Tm {
140    debug!("Parse date/time: {}", s);
141    time::strptime(s, "%h %e %H:%M:%S %Y %Z").expect("strptime")
142}
143
144#[cfg(test)]
145mod test {
146    use super::*;
147
148    #[test]
149    fn test_parse_date() {
150        let x = parse_date("May  3 07:40:15 2019 GMT");
151        assert_eq!(time::strftime("%F %T", &x).unwrap(), "2019-05-03 07:40:15");
152    }
153}