Skip to main content

cert_helper/certificate/
builder.rs

1use super::key::KeyType;
2use super::usage::Usage;
3use openssl::hash::MessageDigest;
4use openssl::pkey::{PKey, Private};
5use std::collections::HashSet;
6
7macro_rules! vec_str_to_hs {
8    ($vec:expr) => {
9        $vec.iter()
10            .map(|s| s.to_string())
11            .collect::<HashSet<String>>()
12    };
13}
14
15/// Defines which hash algorithm to be used in certificate signing
16#[derive(Debug, Clone)]
17pub enum HashAlg {
18    /// SHA-1 (Secure Hash Algorithm 1), now considered weak and generally discouraged for new certificates.
19    SHA1,
20    /// SHA-256 (part of SHA-2 family)
21    SHA256,
22    /// SHA-384 (SHA-2 family), offers stronger security and is often used with larger key sizes.
23    SHA384,
24    /// SHA-512 (SHA-2 family), provides the highest bit-length hash in the SHA-2 family.
25    SHA512,
26}
27/// Defines a common interface for setting X509 certificate or CSR builder fields.
28pub trait BuilderCommon {
29    fn set_common_name(&mut self, name: &str);
30    fn set_signer(&mut self, signer: &str);
31    fn set_country_name(&mut self, name: &str);
32    fn set_state_province(&mut self, name: &str);
33    fn set_organization(&mut self, name: &str);
34    fn set_organization_unit(&mut self, name: &str);
35    fn set_alternative_names(&mut self, alternative_names: Vec<&str>);
36    fn set_locality_time(&mut self, locality_time: &str);
37    fn set_key_type(&mut self, key_type: KeyType);
38    fn set_signature_alg(&mut self, signature_alg: HashAlg);
39    fn set_key_usage(&mut self, key_usage: HashSet<Usage>);
40    fn set_private_key(&mut self, pkey: PKey<Private>);
41}
42
43/// Stores common configurable fields used during X509 certificate or CSR generation.
44#[derive(Debug)]
45pub struct BuilderFields {
46    pub(crate) common_name: String,
47    pub(crate) signer: Option<String>, //place holder for maybe future use??
48    pub(crate) alternative_names: HashSet<String>,
49    pub(crate) organization_unit: String,
50    pub(crate) country_name: String,
51    pub(crate) state_province: String,
52    pub(crate) organization: String,
53    pub(crate) locality_time: String,
54    pub(crate) key_type: Option<KeyType>,
55    pub(crate) existing_key: Option<PKey<Private>>,
56    pub(crate) signature_alg: Option<HashAlg>,
57    pub(crate) usage: Option<HashSet<Usage>>,
58}
59impl BuilderCommon for BuilderFields {
60    // Sets the common name, CN. Whether it also appears in the SAN is decided
61    // when the certificate is built, since it depends on the CA flag.
62    fn set_common_name(&mut self, common_name: &str) {
63        self.common_name = common_name.into();
64    }
65    // The alternative names (SAN) the caller asked for. The CN is not merged in
66    // here — see the SAN assembly in prepare_x509_builder.
67    fn set_alternative_names(&mut self, alternative_names: Vec<&str>) {
68        self.alternative_names
69            .extend(vec_str_to_hs!(alternative_names));
70    }
71    // maybe
72    fn set_signer(&mut self, signer: &str) {
73        self.signer = Some(signer.into());
74    }
75    // Country, a valid two char value
76    fn set_country_name(&mut self, country_name: &str) {
77        self.country_name = country_name.into();
78    }
79    // State, province an utf-8 value
80    fn set_state_province(&mut self, state_province: &str) {
81        self.state_province = state_province.into();
82    }
83    // Org. an utf-8 value
84    fn set_organization(&mut self, organization: &str) {
85        self.organization = organization.into();
86    }
87    // Org. unit an utf-8 value
88    fn set_organization_unit(&mut self, organization_unit: &str) {
89        self.organization_unit = organization_unit.into();
90    }
91    // Locality, represents the city, town, or locality of the certificate subject
92    fn set_locality_time(&mut self, locality_time: &str) {
93        self.locality_time = locality_time.into();
94    }
95    // Selects what type of key to use RSA or elliptic
96    fn set_key_type(&mut self, key_type: KeyType) {
97        self.key_type = Some(key_type);
98    }
99    // Selects what alg to use for signature
100    fn set_signature_alg(&mut self, signature_alg: HashAlg) {
101        self.signature_alg = Some(signature_alg);
102    }
103    // Sets a private key to be used
104    fn set_private_key(&mut self, pkey: PKey<Private>) {
105        self.existing_key = Some(pkey);
106    }
107    // Set what the certificate are allowed to do, KeyUsage and ExtendeKeyUsage
108    fn set_key_usage(&mut self, key_usage: HashSet<Usage>) {
109        match &mut self.usage {
110            Some(existing_usage) => {
111                existing_usage.extend(key_usage);
112            }
113            None => {
114                self.usage = Some(key_usage);
115            }
116        };
117    }
118}
119impl Default for BuilderFields {
120    /// Returns default values for all fields
121    fn default() -> Self {
122        Self {
123            common_name: Default::default(),
124            signer: Default::default(),
125            alternative_names: Default::default(),
126            country_name: Default::default(),
127            state_province: Default::default(),
128            organization: Default::default(),
129            organization_unit: Default::default(),
130            locality_time: Default::default(),
131            key_type: Default::default(),
132            signature_alg: Default::default(),
133            usage: Default::default(),
134            existing_key: Default::default(),
135        }
136    }
137}
138/// Provides a builder interface for configuring X509 certificate or CSR fields.
139pub trait UseesBuilderFields: Sized {
140    /// Returns a mutable reference to the internal `BuilderFields` structure.
141    fn fields_mut(&mut self) -> &mut BuilderFields;
142
143    /// Sets the Common Name (CN) of the certificate subject.
144    ///
145    /// For **end-entity** certificates the CN is also added to the Subject
146    /// Alternative Names, because RFC 6125 verifiers match the hostname against
147    /// the SAN and ignore the CN entirely.
148    ///
149    /// **CA** certificates receive no SAN, so the CN is not copied there — a CA
150    /// is identified by its distinguished name and key identifier during path
151    /// validation.
152    fn common_name(mut self, common_name: &str) -> Self {
153        self.fields_mut().set_common_name(common_name);
154        self
155    }
156    /// Sets the signer name or identifier for the certificate.
157    fn signer(mut self, signer: &str) -> Self {
158        self.fields_mut().set_signer(signer);
159        self
160    }
161    /// Sets the list of Subject Alternative Names (SAN).
162    ///
163    /// An entry that parses as an IPv4 or IPv6 address is emitted as an
164    /// `iPAddress` name, everything else as a `dNSName`.
165    ///
166    /// For end-entity certificates the Common Name is added to this list
167    /// automatically. CA certificates are issued without a SAN, and an empty
168    /// list produces no extension at all rather than an empty one.
169    fn alternative_names(mut self, alternative_names: Vec<&str>) -> Self {
170        self.fields_mut().set_alternative_names(alternative_names);
171        self
172    }
173    /// Sets the country name (C), which must be a valid two-letter country code.
174    fn country_name(mut self, country_name: &str) -> Self {
175        self.fields_mut().set_country_name(country_name);
176        self
177    }
178    /// Sets the state or province name (ST) as a UTF-8 string.
179    fn state_province(mut self, state_province: &str) -> Self {
180        self.fields_mut().set_state_province(state_province);
181        self
182    }
183    /// Sets the organization name (O) as a UTF-8 string.
184    fn organization(mut self, organization: &str) -> Self {
185        self.fields_mut().set_organization(organization);
186        self
187    }
188    /// Sets the locality name (L), typically representing the city or town.
189    fn locality_time(mut self, locality_time: &str) -> Self {
190        self.fields_mut().set_locality_time(locality_time);
191        self
192    }
193    /// Sets the type of key to generate (e.g., RSA or Elliptic Curve).
194    fn key_type(mut self, key_type: KeyType) -> Self {
195        self.fields_mut().set_key_type(key_type);
196        self
197    }
198    /// Use a private key you already hold instead of generating a new one.
199    ///
200    /// This takes precedence over [`key_type`](Self::key_type): if both are set
201    /// the supplied key wins and the requested key type is ignored, since the
202    /// algorithm is a property of the key itself.
203    ///
204    /// Useful for re-issuing a certificate against an existing key (so deployed
205    /// configuration and any pinning keep working), and for minting many
206    /// certificates cheaply — key generation, not signing, is the expensive part
207    /// of issuance.
208    ///
209    /// Reusing one key across certificates is a deliberate trade-off: a
210    /// compromise of that key affects every certificate issued from it. Prefer a
211    /// fresh key per certificate unless you have a specific reason not to.
212    fn private_key(mut self, pkey: PKey<Private>) -> Self {
213        self.fields_mut().set_private_key(pkey);
214        self
215    }
216    /// Sets the signature algorithm to use when signing the certificate.
217    fn signature_alg(mut self, signature_alg: HashAlg) -> Self {
218        self.fields_mut().set_signature_alg(signature_alg);
219        self
220    }
221
222    /// Sets the allowed usages for the certificate (e.g., key signing, digital signature).
223    ///
224    /// This includes both `KeyUsage` and `ExtendedKeyUsage` extensions.
225    fn key_usage(mut self, key_usage: HashSet<Usage>) -> Self {
226        self.fields_mut().set_key_usage(key_usage);
227        self
228    }
229}
230
231pub(crate) fn select_hash(hash_type: &Option<HashAlg>) -> MessageDigest {
232    match hash_type {
233        Some(HashAlg::SHA1) => MessageDigest::sha1(),
234        Some(HashAlg::SHA384) => MessageDigest::sha384(),
235        Some(HashAlg::SHA512) => MessageDigest::sha512(),
236        _ => MessageDigest::sha256(),
237    }
238}