Skip to main content

p12_keystore/
keystore.rs

1use std::{
2    collections::{BTreeMap, btree_map::Iter},
3    fmt,
4};
5
6use crate::codec::{ParsedAuthSafe, secret_to_safe_bag};
7use crate::secret::Secret;
8use crate::{Result, codec, error::Error, oid};
9use cms::content_info::ContentInfo;
10use der::oid::ObjectIdentifier;
11use der::{Any, Decode, Encode, asn1::OctetString};
12use hex::ToHex;
13use pkcs12::{
14    authenticated_safe::AuthenticatedSafe,
15    pfx::{Pfx, Version},
16};
17
18/// X.509 certificate wrapper
19#[derive(Clone, PartialEq, Eq)]
20pub struct Certificate {
21    pub(crate) data: Vec<u8>,
22    pub(crate) subject: String,
23    pub(crate) issuer: String,
24}
25
26impl Certificate {
27    /// Create certificate from DER encoding
28    pub fn from_der(der: &[u8]) -> Result<Self> {
29        let (_, cert) = x509_parser::parse_x509_certificate(der)?;
30        Ok(Self {
31            data: der.to_vec(),
32            subject: cert.subject.to_string(),
33            issuer: cert.issuer.to_string(),
34        })
35    }
36
37    /// Get certificate subject
38    pub fn subject(&self) -> &str {
39        &self.subject
40    }
41
42    /// Get certificate issuer
43    pub fn issuer(&self) -> &str {
44        &self.issuer
45    }
46
47    /// Get certificate data in DER encoding
48    pub fn as_der(&self) -> &[u8] {
49        &self.data
50    }
51}
52
53impl fmt::Debug for Certificate {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        f.debug_struct("Certificate")
56            .field("data", &"<CERT>")
57            .field("subject", &self.subject)
58            .field("issuer", &self.issuer)
59            .finish()
60    }
61}
62
63/// PrivateKeyChain represents a private key and a certificate chain
64#[derive(Clone, PartialEq, Eq)]
65pub struct PrivateKeyChain {
66    pub(crate) key: Vec<u8>,
67    pub(crate) local_key_id: Vec<u8>,
68    pub(crate) chain: Vec<Certificate>,
69}
70
71impl PrivateKeyChain {
72    /// Creates a new keychain with a given key data, key id and a list of certificates.
73    /// The leaf (entity) certificate must be the first in the list, and the root certificate must be the last.
74    pub fn new<K, D, I>(key: K, local_key_id: D, chain: I) -> Self
75    where
76        K: AsRef<[u8]>,
77        D: AsRef<[u8]>,
78        I: IntoIterator<Item = Certificate>,
79    {
80        Self {
81            key: key.as_ref().to_owned(),
82            local_key_id: local_key_id.as_ref().to_owned(),
83            chain: chain.into_iter().collect(),
84        }
85    }
86
87    /// Get private key data
88    pub fn key(&self) -> &[u8] {
89        &self.key
90    }
91
92    /// Get a slice of certificates
93    pub fn chain(&self) -> &[Certificate] {
94        &self.chain
95    }
96
97    /// Get local key id
98    pub fn local_key_id(&self) -> &[u8] {
99        &self.local_key_id
100    }
101}
102
103impl fmt::Debug for PrivateKeyChain {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        f.debug_struct("PrivateKeyChain")
106            .field("key", &"<KEY>")
107            .field("chain", &self.chain)
108            .field("local_key_id", &hex::encode(&self.local_key_id))
109            .finish()
110    }
111}
112/// KeyStoreEntry represents one entry in the keystore
113#[derive(Debug, Clone, PartialEq, Eq)]
114pub enum KeyStoreEntry {
115    PrivateKeyChain(PrivateKeyChain),
116    Certificate(Certificate),
117    Secret(Secret),
118}
119
120/// Keystore entries iterator
121pub struct Entries<'a> {
122    iter: Iter<'a, String, KeyStoreEntry>,
123}
124
125impl<'a> Iterator for Entries<'a> {
126    type Item = (&'a String, &'a KeyStoreEntry);
127
128    fn next(&mut self) -> Option<Self::Item> {
129        self.iter.next()
130    }
131}
132
133/// KeyStore holds a dictionary of [KeyStoreEntry] instances indexed by aliases (names)
134#[derive(Debug, Clone, Default)]
135pub struct KeyStore {
136    entries: BTreeMap<String, KeyStoreEntry>,
137}
138
139impl KeyStore {
140    /// Create new empty keystore
141    pub fn new() -> Self {
142        Self::default()
143    }
144
145    /// Parse keystore from PKCS#12 data
146    pub fn from_pkcs12(data: &[u8], password: &str) -> Result<Self> {
147        let pfx = Pfx::from_der(data)?;
148
149        if pfx.version != Version::V3 {
150            return Err(Error::InvalidVersion);
151        }
152
153        if let Some(mac_data) = pfx.mac_data {
154            codec::verify_mac(&mac_data, password, pfx.auth_safe.content.value())?;
155        }
156
157        let safes: AuthenticatedSafe = if pfx.auth_safe.content_type == oid::CONTENT_TYPE_DATA_OID {
158            AuthenticatedSafe::from_der(&OctetString::from_der(&pfx.auth_safe.content.to_der()?)?.into_bytes())?
159        } else {
160            return Err(Error::UnsupportedContentType);
161        };
162
163        let mut keystore = Self::new();
164
165        let mut parsed_keys = Vec::new();
166        let mut parsed_certs = Vec::new();
167        let mut parsed_secrets = Vec::new();
168
169        for safe in safes.into_iter() {
170            let ParsedAuthSafe { keys, certs, secrets } = codec::parse_auth_safe(&safe, password)?;
171            parsed_keys.extend(keys);
172            parsed_certs.extend(certs);
173            parsed_secrets.extend(secrets);
174        }
175
176        let find_cert_by_key = |key: &[u8]| {
177            parsed_certs
178                .iter()
179                .find(|c| c.local_key_id.as_ref().is_some_and(|k| k.as_slice() == key))
180        };
181
182        let find_issuer = |issuer: &str| parsed_certs.iter().find(|c| c.cert.subject == issuer && !c.trusted);
183
184        for key in parsed_keys {
185            if let Some(mut entry) = find_cert_by_key(&key.key.local_key_id) {
186                let alias = key
187                    .friendly_name
188                    .as_deref()
189                    .unwrap_or_else(|| entry.cert.subject.as_ref());
190
191                let mut key_chain = PrivateKeyChain {
192                    key: key.key.key,
193                    local_key_id: key.key.local_key_id,
194                    chain: vec![entry.cert.clone()],
195                };
196
197                let leaf_cert = &entry.cert;
198
199                while let Some(issuer) = find_issuer(&entry.cert.issuer) {
200                    // Avoid duplication of self-signed certs.
201                    if issuer.cert.subject != leaf_cert.subject {
202                        key_chain.chain.push(issuer.cert.clone());
203                    }
204                    if issuer.cert.issuer == issuer.cert.subject {
205                        break;
206                    }
207                    entry = issuer;
208                }
209                keystore.add_entry(alias, KeyStoreEntry::PrivateKeyChain(key_chain));
210            }
211        }
212
213        for cert in parsed_certs {
214            if cert.local_key_id.is_none() && cert.trusted {
215                let alias = cert.friendly_name.clone().unwrap_or_else(|| cert.cert.subject.clone());
216                keystore.add_entry(&alias, KeyStoreEntry::Certificate(cert.cert));
217            }
218        }
219
220        for secret in parsed_secrets {
221            let alias = secret
222                .friendly_name
223                .clone()
224                .unwrap_or_else(|| secret.key.local_key_id.encode_hex());
225            keystore.add_entry(&alias, KeyStoreEntry::Secret(secret.key));
226        }
227
228        Ok(keystore)
229    }
230
231    /// Create keystore writer with a given password to use for data encryption
232    pub fn writer<'a, 'b>(&'a self, password: &'b str) -> Pkcs12Writer<'a, 'b> {
233        // default values are taken from JVM java.security config file
234        Pkcs12Writer {
235            keystore: self,
236            password,
237            encryption_algorithm: EncryptionAlgorithm::PbeWithHmacSha256AndAes256,
238            encryption_iterations: 10000,
239            mac_algorithm: MacAlgorithm::HmacSha256,
240            mac_iterations: 10000,
241        }
242    }
243
244    /// Get entries iterator
245    pub fn entries(&self) -> Entries<'_> {
246        let iter = self.entries.iter();
247        Entries { iter }
248    }
249
250    /// Get an entry for a given alias
251    pub fn entry(&self, alias: &str) -> Option<&KeyStoreEntry> {
252        self.entries.get(alias)
253    }
254
255    /// Get entries count in the keystore
256    pub fn entries_count(&self) -> usize {
257        self.entries.len()
258    }
259
260    /// Add new entry to the keystore
261    pub fn add_entry(&mut self, alias: &str, entry: KeyStoreEntry) {
262        self.entries.insert(alias.to_owned(), entry);
263    }
264
265    /// Delete entry from the keystore
266    pub fn delete_entry(&mut self, alias: &str) -> Option<KeyStoreEntry> {
267        self.entries.remove(alias)
268    }
269
270    /// Rename entry in the keystore. If an entry with new alias already exists it will be replaced
271    pub fn rename_entry(&mut self, old_alias: &str, new_alias: &str) -> Option<&KeyStoreEntry> {
272        if let Some(old) = self.entries.remove(old_alias) {
273            self.entries.insert(new_alias.to_owned(), old);
274            self.entry(new_alias)
275        } else {
276            None
277        }
278    }
279
280    /// Get the first private keychain
281    pub fn private_key_chain(&self) -> Option<(&str, &PrivateKeyChain)> {
282        self.entries().find_map(|(alias, entry)| match entry {
283            KeyStoreEntry::PrivateKeyChain(chain) => Some((alias.as_str(), chain)),
284            KeyStoreEntry::Certificate(_) => None,
285            KeyStoreEntry::Secret(_) => None,
286        })
287    }
288}
289
290/// Encryption algorithm to use when creating the PKCS#12 file
291#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
292#[non_exhaustive]
293pub enum EncryptionAlgorithm {
294    PbeWithHmacSha256AndAes256,
295    PbeWithShaAnd40BitRc4Cbc,
296    PbeWithShaAnd3KeyTripleDesCbc,
297}
298
299impl EncryptionAlgorithm {
300    pub(crate) fn as_oid(&self) -> ObjectIdentifier {
301        match self {
302            EncryptionAlgorithm::PbeWithHmacSha256AndAes256 => oid::PBES2_OID,
303            EncryptionAlgorithm::PbeWithShaAnd40BitRc4Cbc => oid::PBE_WITH_SHA_AND_40BIT_RC2_CBC_OID,
304            EncryptionAlgorithm::PbeWithShaAnd3KeyTripleDesCbc => oid::PBE_WITH_SHA_AND3_KEY_TRIPLE_DES_CBC_OID,
305        }
306    }
307}
308
309/// MAC algorithm to use when creating the PKCS#12 file
310#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
311#[non_exhaustive]
312pub enum MacAlgorithm {
313    HmacSha1,
314    HmacSha256,
315}
316
317/// PKCS#12 writer
318pub struct Pkcs12Writer<'a, 'b> {
319    keystore: &'a KeyStore,
320    password: &'b str,
321    encryption_algorithm: EncryptionAlgorithm,
322    encryption_iterations: u64,
323    mac_algorithm: MacAlgorithm,
324    mac_iterations: u64,
325}
326
327impl Pkcs12Writer<'_, '_> {
328    /// Set encryption algorithm. Default is [EncryptionAlgorithm::PbeWithHmacSha256AndAes256]
329    pub fn encryption_algorithm(mut self, algorithm: EncryptionAlgorithm) -> Self {
330        self.encryption_algorithm = algorithm;
331        self
332    }
333
334    /// Set encryption iterations. Default is 10000
335    pub fn encryption_iterations(mut self, iterations: u64) -> Self {
336        self.encryption_iterations = iterations;
337        self
338    }
339
340    /// Set MAC algorithm. Default is [MacAlgorithm::HmacSha256]
341    pub fn mac_algorithm(mut self, algorithm: MacAlgorithm) -> Self {
342        self.mac_algorithm = algorithm;
343        self
344    }
345
346    /// Set MAC iterations. Default is 10000
347    pub fn mac_iterations(mut self, iterations: u64) -> Self {
348        self.mac_iterations = iterations;
349        self
350    }
351
352    /// Write keystore into PKCS#12 format
353    pub fn write(self) -> Result<Vec<u8>> {
354        let mut cert_bags = Vec::new();
355
356        let certs = self.keystore.entries.iter().filter_map(|(alias, entry)| match entry {
357            KeyStoreEntry::PrivateKeyChain(_) => None,
358            KeyStoreEntry::Certificate(cert) => Some((alias, cert)),
359            KeyStoreEntry::Secret(_) => None,
360        });
361
362        for (alias, cert) in certs {
363            cert_bags.push(codec::certificate_to_safe_bag(cert, alias, None, true)?);
364        }
365
366        let chain_certs = self
367            .keystore
368            .entries
369            .iter()
370            .filter_map(|(_, entry)| match entry {
371                KeyStoreEntry::PrivateKeyChain(chain) => Some(chain.chain.iter().enumerate().map(|(i, c)| {
372                    (
373                        if i == 0 {
374                            Some(chain.local_key_id.as_slice())
375                        } else {
376                            None
377                        },
378                        c,
379                    )
380                })),
381                KeyStoreEntry::Certificate(_) => None,
382                KeyStoreEntry::Secret(_) => None,
383            })
384            .flatten();
385
386        for (local_key_id, cert) in chain_certs {
387            cert_bags.push(codec::certificate_to_safe_bag(
388                cert,
389                &cert.subject,
390                local_key_id,
391                false,
392            )?);
393        }
394
395        let certs_safe = codec::cert_bags_to_auth_safe(
396            cert_bags,
397            self.encryption_algorithm,
398            self.encryption_iterations,
399            self.password,
400        )?;
401
402        let private_keys = self.keystore.entries.iter().filter_map(|(alias, entry)| match entry {
403            KeyStoreEntry::PrivateKeyChain(chain) => Some((alias, chain)),
404            KeyStoreEntry::Certificate(_) => None,
405            KeyStoreEntry::Secret(_) => None,
406        });
407
408        let mut key_bags = Vec::new();
409
410        for (alias, chain) in private_keys {
411            key_bags.push(codec::private_key_to_safe_bag(
412                chain,
413                alias,
414                self.encryption_algorithm,
415                self.encryption_iterations,
416                self.password,
417            )?);
418        }
419
420        let keys_safe = codec::key_bags_to_auth_safe(key_bags)?;
421
422        let mut safes = vec![certs_safe, keys_safe];
423
424        let secrets = self
425            .keystore
426            .entries
427            .iter()
428            .filter_map(|(alias, entry)| match entry {
429                KeyStoreEntry::PrivateKeyChain(_) => None,
430                KeyStoreEntry::Certificate(_) => None,
431                KeyStoreEntry::Secret(secret) => {
432                    let bag = secret_to_safe_bag(
433                        secret,
434                        self.encryption_algorithm,
435                        alias,
436                        self.encryption_iterations,
437                        self.password,
438                    );
439                    Some(bag)
440                }
441            })
442            .flatten();
443
444        for secret in secrets {
445            safes.push(codec::key_bags_to_auth_safe(vec![secret])?)
446        }
447
448        let safe_bags = OctetString::new(safes.to_der()?)?;
449        let auth_safe = ContentInfo {
450            content_type: oid::CONTENT_TYPE_DATA_OID,
451            content: Any::from_der(&safe_bags.to_der()?)?,
452        };
453
454        let mac_data = codec::compute_mac(
455            auth_safe.content.value(),
456            self.mac_algorithm,
457            self.mac_iterations,
458            self.password,
459        )?;
460
461        let pfx = Pfx {
462            version: Version::V3,
463            auth_safe,
464            mac_data: Some(mac_data),
465        };
466
467        Ok(pfx.to_der()?)
468    }
469}