1use std::collections::{BTreeMap, btree_map::Iter};
2
3use cms::content_info::ContentInfo;
4use der::{Any, Decode, Encode, asn1::OctetString, oid::ObjectIdentifier};
5use hex::ToHex;
6use pkcs12::{
7 AuthenticatedSafe,
8 pfx::{Pfx, Version},
9};
10
11use crate::{
12 Result,
13 cert::Certificate,
14 codec::{self, ParsedAuthSafe, secret_to_safe_bag},
15 error::Error,
16 keychain::PrivateKeyChain,
17 oid,
18 secret::Secret,
19};
20
21#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum KeyStoreEntry {
24 PrivateKeyChain(PrivateKeyChain),
25 Certificate(Certificate),
26 Secret(Secret),
27}
28
29pub struct Entries<'a> {
31 iter: Iter<'a, String, KeyStoreEntry>,
32 len: usize,
33}
34
35impl Entries<'_> {
36 pub fn len(&self) -> usize {
38 self.len
39 }
40
41 pub fn is_empty(&self) -> bool {
43 self.len == 0
44 }
45}
46
47impl<'a> Iterator for Entries<'a> {
48 type Item = (&'a String, &'a KeyStoreEntry);
49
50 fn next(&mut self) -> Option<Self::Item> {
51 self.iter.next()
52 }
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
56pub enum Pkcs12ImportPolicy {
57 #[default]
59 Strict,
60 Relaxed,
62 Raw,
64}
65
66#[derive(Debug, Clone, Default)]
68pub struct KeyStore {
69 entries: BTreeMap<String, KeyStoreEntry>,
70}
71
72impl KeyStore {
73 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn from_pkcs12(data: &[u8], password: &str, policy: Pkcs12ImportPolicy) -> Result<Self> {
80 let pfx = Pfx::from_der(data)?;
81
82 if pfx.version != Version::V3 {
83 return Err(Error::InvalidVersion);
84 }
85
86 if let Some(mac_data) = pfx.mac_data {
87 codec::verify_mac(&mac_data, password, pfx.auth_safe.content.value())?;
88 }
89
90 let safes: AuthenticatedSafe = if pfx.auth_safe.content_type == oid::CONTENT_TYPE_DATA_OID {
91 AuthenticatedSafe::from_der(&OctetString::from_der(&pfx.auth_safe.content.to_der()?)?.into_bytes())?
92 } else {
93 return Err(Error::UnsupportedContentType);
94 };
95
96 let mut keystore = Self::new();
97
98 let mut parsed_keys = Vec::new();
99 let mut parsed_certs = Vec::new();
100 let mut parsed_secrets = Vec::new();
101
102 for safe in safes.into_iter() {
103 let ParsedAuthSafe { keys, certs, secrets } = codec::parse_auth_safe(&safe, password)?;
104 parsed_keys.extend(keys);
105 parsed_certs.extend(certs);
106 parsed_secrets.extend(secrets);
107 }
108
109 for key in parsed_keys {
110 let should_link = policy != Pkcs12ImportPolicy::Raw;
111 let cert_entry = if should_link {
112 parsed_certs.iter().find(|c| {
113 c.local_key_id
114 .as_ref()
115 .is_some_and(|k| k.as_slice() == key.key.local_key_id.as_ref())
116 })
117 } else {
118 None
119 };
120
121 if let Some(mut entry) = cert_entry {
122 let alias = key
123 .friendly_name
124 .as_deref()
125 .unwrap_or_else(|| entry.cert.subject.as_ref());
126
127 let mut certs = vec![entry.cert.clone()];
128 let leaf_cert = &entry.cert;
129
130 while let Some(issuer) = parsed_certs
131 .iter()
132 .find(|c| c.cert.subject == entry.cert.issuer && !c.trusted)
133 {
134 if issuer.cert.subject != leaf_cert.subject {
136 certs.push(issuer.cert.clone());
137 }
138 if issuer.cert.issuer == issuer.cert.subject {
139 break;
140 }
141 entry = issuer;
142 }
143
144 let key_chain = PrivateKeyChain {
145 key: key.key.key,
146 local_key_id: key.key.local_key_id,
147 certs,
148 };
149 keystore.add_entry(alias, KeyStoreEntry::PrivateKeyChain(key_chain));
150 } else if policy != Pkcs12ImportPolicy::Strict {
151 let alias = key
152 .friendly_name
153 .clone()
154 .unwrap_or_else(|| key.key.local_key_id.encode_hex());
155
156 let key_chain = PrivateKeyChain {
157 key: key.key.key,
158 local_key_id: key.key.local_key_id,
159 certs: vec![],
160 };
161 keystore.add_entry(&alias, KeyStoreEntry::PrivateKeyChain(key_chain));
162 }
163 }
164
165 for cert in parsed_certs {
166 let should_import = policy == Pkcs12ImportPolicy::Raw || (cert.local_key_id.is_none() && cert.trusted);
167
168 if should_import {
169 let alias: String = cert.friendly_name.clone().unwrap_or_else(|| cert.cert.subject.clone());
170 keystore.add_entry(&alias, KeyStoreEntry::Certificate(cert.cert));
171 }
172 }
173
174 for secret in parsed_secrets {
175 let alias = secret
176 .friendly_name
177 .clone()
178 .unwrap_or_else(|| secret.key.local_key_id.encode_hex());
179 keystore.add_entry(&alias, KeyStoreEntry::Secret(secret.key));
180 }
181
182 Ok(keystore)
183 }
184
185 pub fn writer<'a, 'b>(&'a self, password: &'b str) -> Pkcs12Writer<'a, 'b> {
187 Pkcs12Writer {
189 keystore: self,
190 password,
191 encryption_algorithm: EncryptionAlgorithm::PbeWithHmacSha256AndAes256,
192 encryption_iterations: 10000,
193 mac_algorithm: MacAlgorithm::HmacSha256,
194 mac_iterations: 10000,
195 }
196 }
197
198 pub fn entries(&self) -> Entries<'_> {
200 let iter = self.entries.iter();
201 Entries {
202 iter,
203 len: self.entries.len(),
204 }
205 }
206
207 pub fn entry(&self, alias: &str) -> Option<&KeyStoreEntry> {
209 self.entries.get(alias)
210 }
211
212 pub fn entries_len(&self) -> usize {
214 self.entries.len()
215 }
216
217 pub fn add_entry(&mut self, alias: &str, entry: KeyStoreEntry) {
219 self.entries.insert(alias.to_owned(), entry);
220 }
221
222 pub fn delete_entry(&mut self, alias: &str) -> Option<KeyStoreEntry> {
224 self.entries.remove(alias)
225 }
226
227 pub fn rename_entry(&mut self, old_alias: &str, new_alias: &str) -> Option<&KeyStoreEntry> {
229 if let Some(old) = self.entries.remove(old_alias) {
230 self.entries.insert(new_alias.to_owned(), old);
231 self.entry(new_alias)
232 } else {
233 None
234 }
235 }
236
237 pub fn private_key_chain(&self) -> Option<(&str, &PrivateKeyChain)> {
239 self.entries().find_map(|(alias, entry)| match entry {
240 KeyStoreEntry::PrivateKeyChain(chain) => Some((alias.as_str(), chain)),
241 _ => None,
242 })
243 }
244}
245
246#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
248#[non_exhaustive]
249pub enum EncryptionAlgorithm {
250 PbeWithHmacSha256AndAes256,
251 PbeWithShaAnd40BitRc4Cbc,
252 PbeWithShaAnd3KeyTripleDesCbc,
253}
254
255impl EncryptionAlgorithm {
256 pub(crate) fn to_oid(self) -> ObjectIdentifier {
257 match self {
258 EncryptionAlgorithm::PbeWithHmacSha256AndAes256 => oid::PBES2_OID,
259 EncryptionAlgorithm::PbeWithShaAnd40BitRc4Cbc => oid::PBE_WITH_SHA_AND_40BIT_RC2_CBC_OID,
260 EncryptionAlgorithm::PbeWithShaAnd3KeyTripleDesCbc => oid::PBE_WITH_SHA_AND3_KEY_TRIPLE_DES_CBC_OID,
261 }
262 }
263}
264
265#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
267#[non_exhaustive]
268pub enum MacAlgorithm {
269 HmacSha1,
270 HmacSha256,
271}
272
273pub struct Pkcs12Writer<'a, 'b> {
275 keystore: &'a KeyStore,
276 password: &'b str,
277 encryption_algorithm: EncryptionAlgorithm,
278 encryption_iterations: u32,
279 mac_algorithm: MacAlgorithm,
280 mac_iterations: u32,
281}
282
283impl Pkcs12Writer<'_, '_> {
284 pub fn encryption_algorithm(mut self, algorithm: EncryptionAlgorithm) -> Self {
286 self.encryption_algorithm = algorithm;
287 self
288 }
289
290 pub fn encryption_iterations(mut self, iterations: u32) -> Self {
292 self.encryption_iterations = iterations;
293 self
294 }
295
296 pub fn mac_algorithm(mut self, algorithm: MacAlgorithm) -> Self {
298 self.mac_algorithm = algorithm;
299 self
300 }
301
302 pub fn mac_iterations(mut self, iterations: u32) -> Self {
304 self.mac_iterations = iterations;
305 self
306 }
307
308 pub fn write(self) -> Result<Vec<u8>> {
310 let mut cert_bags = Vec::new();
311
312 let certs = self.keystore.entries.iter().filter_map(|(alias, entry)| match entry {
313 KeyStoreEntry::PrivateKeyChain(_) => None,
314 KeyStoreEntry::Certificate(cert) => Some((alias, cert)),
315 KeyStoreEntry::Secret(_) => None,
316 });
317
318 for (alias, cert) in certs {
319 cert_bags.push(codec::certificate_to_safe_bag(cert, alias, None, true)?);
320 }
321
322 let chain_certs = self
323 .keystore
324 .entries
325 .values()
326 .filter_map(|entry| match entry {
327 KeyStoreEntry::PrivateKeyChain(chain) => Some(chain.certs.iter().enumerate().map(|(i, c)| {
328 (
329 if i == 0 {
330 Some(chain.local_key_id.as_ref())
331 } else {
332 None
333 },
334 c,
335 )
336 })),
337 KeyStoreEntry::Certificate(_) => None,
338 KeyStoreEntry::Secret(_) => None,
339 })
340 .flatten();
341
342 for (local_key_id, cert) in chain_certs {
343 cert_bags.push(codec::certificate_to_safe_bag(
344 cert,
345 &cert.subject,
346 local_key_id,
347 false,
348 )?);
349 }
350
351 let certs_safe = codec::cert_bags_to_auth_safe(
352 cert_bags,
353 self.encryption_algorithm,
354 self.encryption_iterations as i32,
355 self.password,
356 )?;
357
358 let private_keys = self.keystore.entries.iter().filter_map(|(alias, entry)| match entry {
359 KeyStoreEntry::PrivateKeyChain(chain) => Some((alias, chain)),
360 KeyStoreEntry::Certificate(_) => None,
361 KeyStoreEntry::Secret(_) => None,
362 });
363
364 let mut key_bags = Vec::new();
365
366 for (alias, chain) in private_keys {
367 key_bags.push(codec::private_key_to_safe_bag(
368 chain,
369 alias,
370 self.encryption_algorithm,
371 self.encryption_iterations as i32,
372 self.password,
373 )?);
374 }
375
376 let keys_safe = codec::key_bags_to_auth_safe(key_bags)?;
377
378 let mut safes = vec![certs_safe, keys_safe];
379
380 let secrets = self
381 .keystore
382 .entries
383 .iter()
384 .filter_map(|(alias, entry)| match entry {
385 KeyStoreEntry::PrivateKeyChain(_) => None,
386 KeyStoreEntry::Certificate(_) => None,
387 KeyStoreEntry::Secret(secret) => {
388 let bag = secret_to_safe_bag(
389 secret,
390 self.encryption_algorithm,
391 alias,
392 self.encryption_iterations as i32,
393 self.password,
394 );
395 Some(bag)
396 }
397 })
398 .flatten();
399
400 for secret in secrets {
401 safes.push(codec::key_bags_to_auth_safe(vec![secret])?)
402 }
403
404 let safe_bags = OctetString::new(safes.to_der()?)?;
405 let auth_safe = ContentInfo {
406 content_type: oid::CONTENT_TYPE_DATA_OID,
407 content: Any::from_der(&safe_bags.to_der()?)?,
408 };
409
410 let mac_data = codec::compute_mac(
411 auth_safe.content.value(),
412 self.mac_algorithm,
413 self.mac_iterations as i32,
414 self.password,
415 )?;
416
417 let pfx = Pfx {
418 version: Version::V3,
419 auth_safe,
420 mac_data: Some(mac_data),
421 };
422
423 Ok(pfx.to_der()?)
424 }
425}