Skip to main content

simoom_tls/
certgen.rs

1//! Runtime generation of self-signed ECDSA P-256 certificates (DER encoder),
2//! for tests and development servers.
3
4use crate::asn1;
5use crate::crypto::rng;
6use crate::crypto::sha2::Sha256;
7use crate::keys::{self, EcCurve, PrivateKey};
8use crate::pem;
9use crate::x509;
10use std::net::IpAddr;
11
12/// A generated certificate and its private key, in DER and PEM forms.
13#[derive(Clone, Debug)]
14pub struct SelfSigned {
15    pub cert_der: Vec<u8>,
16    /// PKCS#8 DER.
17    pub key_der: Vec<u8>,
18    pub cert_pem: String,
19    pub key_pem: String,
20    pub key: PrivateKey,
21}
22
23/// Self-signed P-256 certificate for `name` (also used as CN), valid from one
24/// day ago for 365 days. `localhost` additionally gets the 127.0.0.1 and ::1 IP SANs.
25pub fn self_signed(name: &str) -> SelfSigned {
26    let mut names = vec![name.to_string()];
27    if name.eq_ignore_ascii_case("localhost") {
28        names.push("127.0.0.1".into());
29        names.push("::1".into());
30    }
31    let refs: Vec<&str> = names.iter().map(|s| s.as_str()).collect();
32    self_signed_with_sans(&refs, 365)
33}
34
35/// Self-signed P-256 certificate with the given SAN entries (DNS names or IP
36/// addresses); the first entry becomes the subject CN.
37pub fn self_signed_with_sans(names: &[&str], days_valid: i64) -> SelfSigned {
38    assert!(!names.is_empty(), "at least one name required");
39    let curve = EcCurve::P256;
40    let d = curve.generate_private();
41    let public = curve.public_key(&d).expect("valid scalar");
42    let key = PrivateKey::Ec { curve, d: d.clone(), public: public.clone() };
43
44    let now = x509::now_unix();
45    let mut serial = rng::bytes(16);
46    serial[0] &= 0x7f;
47    serial[0] |= 0x01;
48    let sig_alg = asn1::seq(&[asn1::oid("1.2.840.10045.4.3.2")]);
49    let name = asn1::seq(&[asn1::set(&[asn1::seq(&[asn1::oid("2.5.4.3"), asn1::tlv(asn1::TAG_UTF8_STRING, names[0].as_bytes())])])]);
50    let validity = asn1::seq(&[asn1::time(now - 86400), asn1::time(now + days_valid * 86400)]);
51    let spki = asn1::seq(&[asn1::seq(&[asn1::oid(keys::OID_EC_PUBLIC_KEY), asn1::oid(keys::OID_P256)]), asn1::bit_string(&public)]);
52
53    let mut general_names = Vec::new();
54    for n in names {
55        match n.parse::<IpAddr>() {
56            Ok(IpAddr::V4(v4)) => general_names.push(asn1::tlv(0x87, &v4.octets())),
57            Ok(IpAddr::V6(v6)) => general_names.push(asn1::tlv(0x87, &v6.octets())),
58            Err(_) => general_names.push(asn1::tlv(0x82, n.as_bytes())),
59        }
60    }
61    let ext = |oid: &str, critical: bool, value: Vec<u8>| -> Vec<u8> {
62        let mut parts = vec![asn1::oid(oid)];
63        if critical {
64            parts.push(asn1::tlv(asn1::TAG_BOOLEAN, &[0xff]));
65        }
66        parts.push(asn1::octet_string(&value));
67        asn1::seq(&parts)
68    };
69    let ski = Sha256::digest(&public)[..20].to_vec();
70    let extensions = asn1::seq(&[
71        ext("2.5.29.17", false, asn1::seq(&general_names)),
72        // CA:TRUE so the certificate can act as its own trust anchor in other TLS stacks
73        ext("2.5.29.19", true, asn1::seq(&[asn1::tlv(asn1::TAG_BOOLEAN, &[0xff])])),
74        // digitalSignature | keyCertSign
75        ext("2.5.29.15", true, asn1::tlv(asn1::TAG_BIT_STRING, &[0x02, 0x84])),
76        ext("2.5.29.37", false, asn1::seq(&[asn1::oid(x509::OID_EKU_SERVER_AUTH), asn1::oid(x509::OID_EKU_CLIENT_AUTH)])),
77        ext("2.5.29.14", false, asn1::octet_string(&ski)),
78    ]);
79    let tbs = asn1::seq(&[
80        asn1::explicit(0, &asn1::uint(&[2])),
81        asn1::uint(&serial),
82        sig_alg.clone(),
83        name.clone(),
84        validity,
85        name,
86        spki,
87        asn1::explicit(3, &extensions),
88    ]);
89    let (r, s) = curve.sign(&d, &Sha256::digest(&tbs), crate::crypto::sha2::HashAlg::Sha256).expect("sign");
90    let sig = keys::ecdsa_sig_to_der(&r, &s);
91    let cert_der = asn1::seq(&[tbs, sig_alg, asn1::bit_string(&sig)]);
92    let key_der = key.to_pkcs8_der().expect("EC key");
93    SelfSigned {
94        cert_pem: pem::encode_pem("CERTIFICATE", &cert_der),
95        key_pem: pem::encode_pem("PRIVATE KEY", &key_der),
96        cert_der,
97        key_der,
98        key,
99    }
100}
101
102/// Parameters of a certificate issued by [`issue`].
103#[derive(Clone, Debug)]
104pub struct CertParams {
105    /// Subject common name.
106    pub common_name: String,
107    /// SAN entries (DNS names or IP addresses).
108    pub sans: Vec<String>,
109    pub is_ca: bool,
110    /// Extended key usage OIDs (empty = no extension).
111    pub ext_key_usage: Vec<String>,
112    pub days_valid: i64,
113}
114
115impl CertParams {
116    /// A TLS server certificate for the given names (first = CN).
117    pub fn server(names: &[&str]) -> CertParams {
118        CertParams {
119            common_name: names[0].to_string(),
120            sans: names.iter().map(|s| s.to_string()).collect(),
121            is_ca: false,
122            ext_key_usage: vec![x509::OID_EKU_SERVER_AUTH.to_string()],
123            days_valid: 365,
124        }
125    }
126
127    /// A TLS client certificate with subject CN `cn`.
128    pub fn client(cn: &str) -> CertParams {
129        CertParams { common_name: cn.to_string(), sans: Vec::new(), is_ca: false, ext_key_usage: vec![x509::OID_EKU_CLIENT_AUTH.to_string()], days_valid: 365 }
130    }
131
132    /// A CA certificate.
133    pub fn ca(cn: &str) -> CertParams {
134        CertParams { common_name: cn.to_string(), sans: Vec::new(), is_ca: true, ext_key_usage: Vec::new(), days_valid: 3650 }
135    }
136}
137
138pub(crate) fn sig_alg_for(key: &PrivateKey) -> Vec<u8> {
139    match key {
140        #[cfg(feature = "p384")]
141        PrivateKey::Ec { curve: EcCurve::P384, .. } => asn1::seq(&[asn1::oid("1.2.840.10045.4.3.3")]),
142        PrivateKey::Ec { .. } => asn1::seq(&[asn1::oid("1.2.840.10045.4.3.2")]),
143        #[cfg(feature = "ed25519")]
144        PrivateKey::Ed25519 { .. } => asn1::seq(&[asn1::oid(keys::OID_ED25519)]),
145        #[cfg(feature = "rsa")]
146        PrivateKey::Rsa(_) => asn1::seq(&[asn1::oid("1.2.840.113549.1.1.11"), asn1::tlv(asn1::TAG_NULL, &[])]),
147    }
148}
149
150pub(crate) fn sign_with(key: &PrivateKey, tbs: &[u8]) -> Vec<u8> {
151    #[cfg(feature = "rsa")]
152    use crate::crypto::sha2::HashAlg;
153    match key {
154        PrivateKey::Ec { curve, d, .. } => {
155            let h = curve.hash();
156            let (r, s) = curve.sign(d, &h.hash(tbs), h).expect("sign");
157            keys::ecdsa_sig_to_der(&r, &s)
158        }
159        #[cfg(feature = "ed25519")]
160        PrivateKey::Ed25519 { seed, public } => crate::crypto::ed25519::sign_with_public(seed, public, tbs).to_vec(),
161        #[cfg(feature = "rsa")]
162        PrivateKey::Rsa(k) => k.sign_pkcs1(HashAlg::Sha256, &HashAlg::Sha256.hash(tbs)).expect("sign"),
163    }
164}
165
166fn name_der(cn: &str) -> Vec<u8> {
167    asn1::seq(&[asn1::set(&[asn1::seq(&[asn1::oid("2.5.4.3"), asn1::tlv(asn1::TAG_UTF8_STRING, cn.as_bytes())])])])
168}
169
170/// Issues a certificate for `key` signed by `issuer` (self-signed when `None`).
171pub fn issue(params: &CertParams, key: PrivateKey, issuer: Option<&SelfSigned>) -> SelfSigned {
172    let now = x509::now_unix();
173    let mut serial = rng::bytes(16);
174    serial[0] &= 0x7f;
175    serial[0] |= 0x01;
176    let (issuer_name, signer) = match issuer {
177        Some(i) => (x509::Certificate::from_der(&i.cert_der).expect("issuer certificate").subject_raw, &i.key),
178        None => (name_der(&params.common_name), &key),
179    };
180    let sig_alg = sig_alg_for(signer);
181    let spki = key.public_key().to_spki();
182    let ext = |oid: &str, critical: bool, value: Vec<u8>| -> Vec<u8> {
183        let mut parts = vec![asn1::oid(oid)];
184        if critical {
185            parts.push(asn1::tlv(asn1::TAG_BOOLEAN, &[0xff]));
186        }
187        parts.push(asn1::octet_string(&value));
188        asn1::seq(&parts)
189    };
190    let mut exts = Vec::new();
191    if !params.sans.is_empty() {
192        let mut general_names = Vec::new();
193        for n in &params.sans {
194            match n.parse::<IpAddr>() {
195                Ok(IpAddr::V4(v4)) => general_names.push(asn1::tlv(0x87, &v4.octets())),
196                Ok(IpAddr::V6(v6)) => general_names.push(asn1::tlv(0x87, &v6.octets())),
197                Err(_) => general_names.push(asn1::tlv(0x82, n.as_bytes())),
198            }
199        }
200        exts.push(ext("2.5.29.17", false, asn1::seq(&general_names)));
201    }
202    if params.is_ca {
203        exts.push(ext("2.5.29.19", true, asn1::seq(&[asn1::tlv(asn1::TAG_BOOLEAN, &[0xff])])));
204        // digitalSignature | keyCertSign | cRLSign
205        exts.push(ext("2.5.29.15", true, asn1::tlv(asn1::TAG_BIT_STRING, &[0x01, 0x86])));
206    } else {
207        exts.push(ext("2.5.29.19", true, asn1::seq(&[])));
208        exts.push(ext("2.5.29.15", true, asn1::tlv(asn1::TAG_BIT_STRING, &[0x07, 0x80])));
209    }
210    if !params.ext_key_usage.is_empty() {
211        exts.push(ext("2.5.29.37", false, asn1::seq(&params.ext_key_usage.iter().map(|o| asn1::oid(o)).collect::<Vec<_>>())));
212    }
213    let tbs = asn1::seq(&[
214        asn1::explicit(0, &asn1::uint(&[2])),
215        asn1::uint(&serial),
216        sig_alg.clone(),
217        issuer_name,
218        asn1::seq(&[asn1::time(now - 86400), asn1::time(now + params.days_valid * 86400)]),
219        name_der(&params.common_name),
220        spki,
221        asn1::explicit(3, &asn1::seq(&exts)),
222    ]);
223    let sig = sign_with(signer, &tbs);
224    let cert_der = asn1::seq(&[tbs, sig_alg, asn1::bit_string(&sig)]);
225    let key_der = key.to_pkcs8();
226    SelfSigned { cert_pem: pem::encode_pem("CERTIFICATE", &cert_der), key_pem: pem::encode_pem("PRIVATE KEY", &key_der), cert_der, key_der, key }
227}