Botan 3.13.0
Crypto and TLS for C&
pkcs12.cpp
Go to the documentation of this file.
1/*
2* PKCS#12
3* (C) 2026 Damiano Mazzella
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#include <botan/pkcs12.h>
9
10#include <botan/asn1_obj.h>
11#include <botan/ber_dec.h>
12#include <botan/data_src.h>
13#include <botan/der_enc.h>
14#include <botan/exceptn.h>
15#include <botan/hash.h>
16#include <botan/mac.h>
17#include <botan/mem_ops.h>
18#include <botan/pkcs8.h>
19#include <botan/rng.h>
20#include <botan/internal/charset.h>
21#include <botan/internal/fmt.h>
22#include <botan/internal/pkcs12_kdf.h>
23#include <botan/internal/pkcs12_pbe.h>
24#include <algorithm>
25#include <array>
26#include <memory>
27
28namespace Botan {
29
30namespace {
31
32/// Maximum allowed SafeContentsBag nesting depth during parsing (anti-DoS).
33constexpr size_t PKCS12_MAX_NESTING = 10;
34
35// Associates a parsed certificate with its bag attributes
36struct ParsedCert {
37 X509_Certificate cert;
38 std::vector<uint8_t> local_key_id;
39 std::string friendly_name;
40};
41
42/*
43* Encode a friendly name as a BMPString (UTF-16BE)
44* ASN1_String doesn't support encoding BMPStrings, so we do it manually
45*/
46void encode_bmpstring(DER_Encoder& enc, std::string_view str) {
47 const std::vector<uint8_t> utf16be = utf8_to_ucs2(str);
48 enc.add_object(ASN1_Type::BmpString, ASN1_Class::Universal, utf16be);
49}
50
51/*
52* Resolve a MAC digest OID to a hash name, throwing on unsupported algorithms.
53*/
54std::string resolve_mac_hash(const OID& oid) {
55 if(oid == OID::from_string("SHA-1")) {
56 return "SHA-1";
57 }
58 if(oid == OID::from_string("SHA-224")) {
59 return "SHA-224";
60 }
61 if(oid == OID::from_string("SHA-256")) {
62 return "SHA-256";
63 }
64 if(oid == OID::from_string("SHA-384")) {
65 return "SHA-384";
66 }
67 if(oid == OID::from_string("SHA-512")) {
68 return "SHA-512";
69 }
70 if(oid == OID::from_string("SHA-512-256")) {
71 return "SHA-512-256";
72 }
73 throw Decoding_Error(fmt("Unsupported PKCS#12 MAC digest: {}", oid.to_formatted_string()));
74}
75
76/*
77* Validate PKCS12_Export_Options before starting export.
78*/
79void validate_options(const PKCS12_Export_Options& opts) {
80 if(opts.iterations() == 0 || opts.iterations() > PKCS12_MAX_ITERATIONS) {
81 throw Invalid_Argument(fmt("PKCS#12: iteration count must be between 1 and {}", PKCS12_MAX_ITERATIONS));
82 }
83 static const std::array<std::string_view, 4> supported_key_algos = {
84 "PBE-SHA1-3DES",
85 "PBE-SHA1-2DES",
86 "PBES2-SHA256-AES256",
87 "PBES2-SHA256-AES128",
88 };
89 if(std::find(supported_key_algos.begin(), supported_key_algos.end(), opts.key_encryption_algo()) ==
90 supported_key_algos.end()) {
91 throw Invalid_Argument(fmt("PKCS#12: unsupported key encryption algorithm '{}'", opts.key_encryption_algo()));
92 }
93 if(!opts.cert_encryption_algo().empty()) {
94 if(std::find(supported_key_algos.begin(), supported_key_algos.end(), opts.cert_encryption_algo()) ==
95 supported_key_algos.end()) {
96 throw Invalid_Argument(
97 fmt("PKCS#12: unsupported cert encryption algorithm '{}'", opts.cert_encryption_algo()));
98 }
99 }
100 // An empty password is permitted for both encryption and the MAC: PKCS#12
101 // defines an encoding for it (RFC 7292) and such files are common in the
102 // wild (e.g. some Java keystores). It offers no real protection, but there
103 // is no technical reason to reject it.
104 if(opts.include_mac()) {
105 static const std::array<std::string_view, 6> supported_mac_digests = {
106 "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512", "SHA-512-256"};
107 if(std::find(supported_mac_digests.begin(), supported_mac_digests.end(), opts.mac_digest()) ==
108 supported_mac_digests.end()) {
109 throw Invalid_Argument(fmt("PKCS#12: unsupported MAC digest '{}'", opts.mac_digest()));
110 }
111 if(!HashFunction::create(opts.mac_digest())) {
112 throw Invalid_Argument(fmt("PKCS#12: MAC digest '{}' is not available in this build", opts.mac_digest()));
113 }
114 }
115}
116
117/*
118* Verify PKCS#12 MAC.
119*
120* When @p openssl_empty_pwd_compat is @c true and @p password is empty, the
121* KDF is fed an empty byte string (OpenSSL non-conforming behavior) instead
122* of the RFC 7292 form (a two-byte {0x00,0x00} terminator).
123*/
124void verify_mac(std::span<const uint8_t> auth_safe_data,
125 std::span<const uint8_t> mac_value,
126 std::span<const uint8_t> mac_salt,
127 size_t iterations,
128 const std::string& hash_name,
129 std::string_view password,
130 bool openssl_empty_pwd_compat) {
131 auto hmac = MessageAuthenticationCode::create_or_throw(fmt("HMAC({})", hash_name));
132 const size_t mac_key_len = hmac->output_length();
133
134 secure_vector<uint8_t> mac_key(mac_key_len);
135 if(openssl_empty_pwd_compat && password.empty()) {
136 const auto hash = HashFunction::create_or_throw(hash_name);
137 pkcs12_kdf({mac_key.data(), mac_key_len}, {}, {mac_salt.data(), mac_salt.size()}, iterations, 3, *hash);
138 } else {
139 const PKCS12_KDF kdf(HashFunction::create_or_throw(hash_name), 3, iterations);
140 kdf.derive_key(mac_key.data(), mac_key_len, password.data(), password.size(), mac_salt.data(), mac_salt.size());
141 }
142
143 hmac->set_key(mac_key);
144 hmac->update(auth_safe_data);
145 if(!constant_time_compare(hmac->final(), mac_value)) {
146 throw Invalid_Authentication_Tag("PKCS#12 MAC verification failed");
147 }
148}
149
150/*
151* Parse attributes from a SafeBag. Only FriendlyName and LocalKeyId are
152* handled; other attributes are silently skipped (RFC 7292 sec.4.2).
153*/
154void parse_bag_attributes(BER_Decoder& decoder, std::string& friendly_name, std::vector<uint8_t>& local_key_id) {
155 if(!decoder.more_items()) {
156 return;
157 }
158
159 const OID friendly_name_oid = OID::from_string("PKCS9.FriendlyName");
160 const OID local_key_id_oid = OID::from_string("PKCS9.LocalKeyId");
161
162 BER_Decoder attrs = decoder.start_set();
163 while(attrs.more_items()) {
164 OID attr_oid;
165 BER_Decoder attr_seq = attrs.start_sequence();
166 attr_seq.decode(attr_oid);
167
168 BER_Decoder values = attr_seq.start_set();
169 if(attr_oid == friendly_name_oid) {
170 ASN1_String str;
171 values.decode(str);
172 friendly_name = str.value();
173 } else if(attr_oid == local_key_id_oid) {
174 values.decode(local_key_id, ASN1_Type::OctetString);
175 }
176 values.discard_remaining();
177 values.end_cons();
178 attr_seq.discard_remaining();
179 attr_seq.end_cons();
180 }
181 attrs.end_cons();
182}
183
184// Helper bag carrying a parsed private key with its attributes.
185struct ParsedKey {
186 std::shared_ptr<Private_Key> key;
187 std::vector<uint8_t> local_key_id;
188 std::string friendly_name;
189};
190
191/*
192* Parse SafeContents (sequence of SafeBag)
193*/
194void parse_safe_contents(BER_Decoder& decoder,
195 std::string_view password,
196 std::vector<ParsedCert>& cert_entries,
197 std::vector<ParsedKey>& key_entries,
198 std::vector<OID>& unknown_bag_types,
199 bool openssl_empty_pwd_compat,
200 size_t depth = 0) {
201 if(depth >= PKCS12_MAX_NESTING) {
202 throw Decoding_Error("PKCS#12: SafeContentsBag nesting too deep");
203 }
204 const OID cert_bag_oid = OID::from_string("PKCS12.CertBag");
205 const OID shrouded_key_bag_oid = OID::from_string("PKCS12.PKCS8ShroudedKeyBag");
206 const OID key_bag_oid = OID::from_string("PKCS12.KeyBag");
207 const OID safe_contents_bag_oid = OID::from_string("PKCS12.SafeContentsBag");
208 const OID x509_cert_oid = OID::from_string("PKCS9.X509Certificate");
209
210 while(decoder.more_items()) {
211 OID bag_type;
212 std::string bag_friendly_name;
213 std::vector<uint8_t> bag_key_id;
214
215 BER_Decoder bag_seq = decoder.start_sequence();
216 bag_seq.decode(bag_type);
217
218 BER_Decoder bag_value = bag_seq.start_context_specific(0);
219
220 bool pushed_cert = false;
221 bool pushed_key = false;
222
223 if(bag_type == cert_bag_oid) {
224 OID cert_type;
225 BER_Decoder cert_bag = bag_value.start_sequence();
226 cert_bag.decode(cert_type);
227
228 if(cert_type == x509_cert_oid) {
229 std::vector<uint8_t> cert_data;
230 BER_Decoder cert_value = cert_bag.start_context_specific(0);
231 cert_value.decode(cert_data, ASN1_Type::OctetString);
232 cert_value.verify_end();
233
234 cert_entries.push_back({X509_Certificate(cert_data), {}, {}});
235 pushed_cert = true;
236 } else {
237 cert_bag.discard_remaining();
238 }
239 cert_bag.end_cons();
240 bag_value.verify_end();
241 } else if(bag_type == shrouded_key_bag_oid) {
242 AlgorithmIdentifier pbe_algo;
243 std::vector<uint8_t> encrypted_key;
244
245 BER_Decoder shrouded = bag_value.start_sequence();
246 shrouded.decode(pbe_algo);
247 shrouded.decode(encrypted_key, ASN1_Type::OctetString);
248 shrouded.verify_end();
249
250 auto decrypted = pkcs12_pbe_decrypt(encrypted_key, password, pbe_algo, openssl_empty_pwd_compat);
251 DataSource_Memory src(decrypted);
252 key_entries.push_back({std::shared_ptr<Private_Key>(PKCS8::load_key(src)), {}, {}});
253 pushed_key = true;
254 } else if(bag_type == key_bag_oid) {
255 secure_vector<uint8_t> key_data;
256 bag_value.raw_bytes(key_data);
257 bag_value.verify_end();
258 DataSource_Memory src(key_data);
259 key_entries.push_back({std::shared_ptr<Private_Key>(PKCS8::load_key(src)), {}, {}});
260 pushed_key = true;
261 } else if(bag_type == safe_contents_bag_oid) {
262 BER_Decoder nested_sc = bag_value.start_sequence();
263 parse_safe_contents(
264 nested_sc, password, cert_entries, key_entries, unknown_bag_types, openssl_empty_pwd_compat, depth + 1);
265 nested_sc.verify_end();
266 bag_value.verify_end();
267 } else {
268 unknown_bag_types.push_back(bag_type);
269 bag_value.discard_remaining();
270 }
271
272 bag_value.end_cons();
273
274 parse_bag_attributes(bag_seq, bag_friendly_name, bag_key_id);
275
276 if(pushed_cert && !cert_entries.empty()) {
277 if(!bag_key_id.empty()) {
278 cert_entries.back().local_key_id = bag_key_id;
279 }
280 if(!bag_friendly_name.empty()) {
281 cert_entries.back().friendly_name = bag_friendly_name;
282 }
283 } else if(pushed_key && !key_entries.empty()) {
284 if(!bag_key_id.empty()) {
285 key_entries.back().local_key_id = bag_key_id;
286 }
287 if(!bag_friendly_name.empty()) {
288 key_entries.back().friendly_name = bag_friendly_name;
289 }
290 }
291
292 bag_seq.verify_end();
293 }
294}
295
296/*
297* Parse AuthenticatedSafe (sequence of ContentInfo)
298*/
299void parse_authenticated_safe(std::span<const uint8_t> data,
300 std::string_view password,
301 std::vector<ParsedCert>& cert_entries,
302 std::vector<ParsedKey>& key_entries,
303 std::vector<OID>& unknown_bag_types,
304 bool openssl_empty_pwd_compat) {
305 const OID pkcs7_data_oid = OID::from_string("PKCS7.Data");
306 const OID pkcs7_enc_data_oid = OID::from_string("PKCS7.EncryptedData");
307
308 BER_Decoder auth_safe(data);
309 BER_Decoder seq = auth_safe.start_sequence();
310
311 while(seq.more_items()) {
312 OID content_type;
313 BER_Decoder content_info = seq.start_sequence();
314 content_info.decode(content_type);
315
316 if(content_type == pkcs7_data_oid) {
317 std::vector<uint8_t> safe_contents_data;
318 BER_Decoder content = content_info.start_context_specific(0);
319 content.decode(safe_contents_data, ASN1_Type::OctetString);
320 content.verify_end();
321
322 BER_Decoder safe_contents(safe_contents_data);
323 BER_Decoder sc_seq = safe_contents.start_sequence();
324 parse_safe_contents(sc_seq, password, cert_entries, key_entries, unknown_bag_types, openssl_empty_pwd_compat);
325 sc_seq.verify_end();
326 safe_contents.verify_end();
327 content_info.verify_end();
328 } else if(content_type == pkcs7_enc_data_oid) {
329 BER_Decoder content = content_info.start_context_specific(0);
330 BER_Decoder enc_data = content.start_sequence();
331
332 size_t version = 0;
333 enc_data.decode(version);
334
335 if(version != 0) {
336 throw Decoding_Error(fmt("PKCS#12: unsupported EncryptedData version: {}", version));
337 }
338
339 BER_Decoder enc_content_info = enc_data.start_sequence();
340 OID enc_content_type;
341 AlgorithmIdentifier enc_algo;
342 enc_content_info.decode(enc_content_type);
343 enc_content_info.decode(enc_algo);
344
345 if(enc_content_type != pkcs7_data_oid) {
346 throw Decoding_Error(
347 fmt("PKCS#12: EncryptedData contentType must be Data, got {}", enc_content_type.to_formatted_string()));
348 }
349
350 std::vector<uint8_t> encrypted_content;
351 const BER_Object enc_content_obj = enc_content_info.get_next_object();
352
353 if(enc_content_obj.is_a(0, ASN1_Class::ContextSpecific | ASN1_Class::Constructed)) {
354 const std::span<const uint8_t> raw(enc_content_obj.bits(), enc_content_obj.length());
355 encrypted_content.reserve(raw.size());
356 BER_Decoder chunks(raw);
357 while(chunks.more_items()) {
358 std::vector<uint8_t> chunk;
359 chunks.decode(chunk, ASN1_Type::OctetString);
360 encrypted_content.insert(encrypted_content.end(), chunk.begin(), chunk.end());
361 }
362 chunks.verify_end();
363 } else if(enc_content_obj.is_a(0, ASN1_Class::ContextSpecific)) {
364 encrypted_content.assign(enc_content_obj.bits(), enc_content_obj.bits() + enc_content_obj.length());
365 } else {
366 throw Decoding_Error("PKCS#12: Expected [0] context-specific for encrypted content");
367 }
368
369 enc_content_info.verify_end();
370 enc_data.verify_end();
371
372 const secure_vector<uint8_t> decrypted =
373 pkcs12_pbe_decrypt(encrypted_content, password, enc_algo, openssl_empty_pwd_compat);
374
375 BER_Decoder safe_contents(decrypted);
376 BER_Decoder sc_seq = safe_contents.start_sequence();
377 parse_safe_contents(sc_seq, password, cert_entries, key_entries, unknown_bag_types, openssl_empty_pwd_compat);
378 sc_seq.verify_end();
379 safe_contents.verify_end();
380 content.verify_end();
381 content_info.verify_end();
382 } else {
383 throw Decoding_Error(
384 fmt("PKCS#12: unsupported AuthenticatedSafe content type {}", content_type.to_formatted_string()));
385 }
386 }
387
388 seq.verify_end();
389 auth_safe.verify_end();
390}
391
392} // namespace
393
394//
395// PKCS12_Export_Options
396//
397
398PKCS12_Export_Options::PKCS12_Export_Options(std::string_view password, std::optional<std::string> friendly_name) :
399 m_password(password), m_friendly_name(std::move(friendly_name)) {}
400
402 std::optional<std::string> friendly_name) {
403 return PKCS12_Export_Options(password, std::move(friendly_name));
404}
405
407 std::optional<std::string> friendly_name) {
409 opts.m_iterations = 2048;
410 opts.m_key_encryption_algo = "PBE-SHA1-3DES";
411 opts.m_mac_digest = "SHA-1";
412 return opts;
413}
414
416 m_friendly_name = std::move(name);
417 return *this;
418}
419
421 m_iterations = n;
422 return *this;
423}
424
426 m_key_encryption_algo = std::move(algo);
427 return *this;
428}
429
431 m_cert_encryption_algo = std::move(algo);
432 return *this;
433}
434
436 m_mac_digest = std::move(algo);
437 return *this;
438}
439
441 m_include_mac = false;
442 return *this;
443}
444
445//
446// PKCS12
447//
448
449PKCS12::PKCS12(std::span<const uint8_t> data, std::string_view password) {
450 std::vector<ParsedCert> cert_entries;
451 std::vector<ParsedKey> key_entries;
452
453 BER_Decoder pfx(data);
454 BER_Decoder pfx_seq = pfx.start_sequence();
455
456 size_t version = 0;
457 pfx_seq.decode(version);
458 if(version != 3) {
459 throw Decoding_Error(fmt("Unsupported PKCS#12 version: {}", version));
460 }
461
462 OID auth_safe_type;
463 std::vector<uint8_t> auth_safe_content;
464
465 BER_Decoder auth_safe_info = pfx_seq.start_sequence();
466 auth_safe_info.decode(auth_safe_type);
467
468 const OID pkcs7_data_oid = OID::from_string("PKCS7.Data");
469 if(auth_safe_type != pkcs7_data_oid) {
470 throw Decoding_Error("PKCS#12 authSafe must be of type Data");
471 }
472
473 BER_Decoder auth_safe_content_wrapper = auth_safe_info.start_context_specific(0);
474 auth_safe_content_wrapper.decode(auth_safe_content, ASN1_Type::OctetString);
475 auth_safe_content_wrapper.verify_end();
476 auth_safe_info.verify_end();
477
478 // Tracks whether MAC verification succeeded with OpenSSL's non-conforming
479 // empty-password encoding; if so, the same convention is used for any
480 // subsequent EncryptedData / PKCS8ShroudedKeyBag decryption.
481 bool openssl_empty_pwd_compat = false;
482
483 if(pfx_seq.more_items()) {
484 BER_Decoder mac_data = pfx_seq.start_sequence();
485
486 BER_Decoder digest_info = mac_data.start_sequence();
487 AlgorithmIdentifier digest_algo;
488 std::vector<uint8_t> mac_value;
489 digest_info.decode(digest_algo);
490 digest_info.decode(mac_value, ASN1_Type::OctetString);
491 digest_info.verify_end();
492
493 std::vector<uint8_t> mac_salt;
494 size_t iterations = 1;
495 mac_data.decode(mac_salt, ASN1_Type::OctetString);
496 if(mac_data.more_items()) {
497 mac_data.decode(iterations);
498 }
499 mac_data.verify_end();
500 if(iterations == 0 || iterations > PKCS12_MAX_ITERATIONS) {
501 throw Decoding_Error(fmt("PKCS#12 MAC has invalid iteration count: {}", iterations));
502 }
503
504 const std::string hash_name = resolve_mac_hash(digest_algo.oid());
505 // Try RFC 7292 password encoding first. If MAC verification fails and
506 // the password is empty, retry with OpenSSL's non-conforming empty
507 // encoding (some OpenSSL releases pass an empty byte string to the KDF
508 // instead of the RFC {0x00,0x00} form when the password is empty).
509 // Propagate the chosen convention to any subsequent PBE decryption.
510 try {
511 verify_mac(auth_safe_content, mac_value, mac_salt, iterations, hash_name, password, false);
512 } catch(const Invalid_Authentication_Tag&) {
513 if(!password.empty()) {
514 throw;
515 }
516 verify_mac(auth_safe_content, mac_value, mac_salt, iterations, hash_name, password, true);
517 openssl_empty_pwd_compat = true;
518 }
519 }
520
521 parse_authenticated_safe(
522 auth_safe_content, password, cert_entries, key_entries, m_unknown_bag_types, openssl_empty_pwd_compat);
523
524 // Move all parsed keys into storage.
525 m_private_keys.reserve(key_entries.size());
526 for(auto& ke : key_entries) {
527 m_private_keys.push_back(std::move(ke.key));
528 }
529
530 // Capture bundle-level attributes from the first key (if any), or from
531 // the end-entity certificate (if found below).
532 if(!key_entries.empty()) {
533 if(!key_entries.front().friendly_name.empty()) {
534 m_friendly_name = key_entries.front().friendly_name;
535 }
536 if(!key_entries.front().local_key_id.empty()) {
537 m_local_key_id = key_entries.front().local_key_id;
538 }
539 }
540
541 // Reorder certificates so the end-entity (cert matching the first key)
542 // comes first; rest follow in original order. Match prefers localKeyId,
543 // falls back to subjectPublicKeyInfo comparison.
544 std::optional<size_t> end_entity_idx;
545 if(!cert_entries.empty() && !m_private_keys.empty()) {
546 const auto& first_key = m_private_keys.front();
547 const auto& first_key_id = key_entries.empty() ? std::vector<uint8_t>{} : key_entries.front().local_key_id;
548
549 if(!first_key_id.empty()) {
550 for(size_t i = 0; i < cert_entries.size(); ++i) {
551 if(cert_entries[i].local_key_id == first_key_id) {
552 end_entity_idx = i;
553 break;
554 }
555 }
556 }
557 if(!end_entity_idx) {
558 const auto key_spki = first_key->subject_public_key();
559 for(size_t i = 0; i < cert_entries.size(); ++i) {
560 try {
561 if(cert_entries[i].cert.subject_public_key_info() == key_spki) {
562 end_entity_idx = i;
563 break;
564 }
565 } catch(const Decoding_Error&) {
566 // Certificate with unsupported key algorithm - skip
567 }
568 }
569 }
570 }
571
572 m_certificates.reserve(cert_entries.size());
573 if(end_entity_idx) {
574 m_certificates.push_back(std::move(cert_entries[*end_entity_idx].cert));
575 if(!m_friendly_name && !cert_entries[*end_entity_idx].friendly_name.empty()) {
576 m_friendly_name = cert_entries[*end_entity_idx].friendly_name;
577 }
578 if(!m_local_key_id && !cert_entries[*end_entity_idx].local_key_id.empty()) {
579 m_local_key_id = cert_entries[*end_entity_idx].local_key_id;
580 }
581 for(size_t i = 0; i < cert_entries.size(); ++i) {
582 if(i != *end_entity_idx) {
583 // Still surface any friendly name found on non-end-entity certs
584 // when the bundle doesn't have one yet (some producers attach the
585 // attribute to the CA bag instead of the end-entity bag).
586 if(!m_friendly_name && !cert_entries[i].friendly_name.empty()) {
587 m_friendly_name = cert_entries[i].friendly_name;
588 }
589 m_certificates.push_back(std::move(cert_entries[i].cert));
590 }
591 }
592 } else {
593 for(auto& ce : cert_entries) {
594 if(!m_friendly_name && !ce.friendly_name.empty()) {
595 m_friendly_name = ce.friendly_name;
596 }
597 m_certificates.push_back(std::move(ce.cert));
598 }
599 }
600
601 pfx_seq.verify_end();
602 pfx_seq.end_cons();
603 pfx.verify_end("PKCS#12: trailing data after PFX");
604}
605
606std::vector<X509_Certificate> PKCS12::ca_certificates() const {
607 if(m_certificates.size() < 2) {
608 return {};
609 }
610 const auto ee = end_entity_certificate();
611 std::vector<X509_Certificate> result;
612 result.reserve(m_certificates.size() - 1);
613 if(ee) {
614 // Skip the first certificate matching the end-entity (only one, in case
615 // the bundle contains multiple certs signed for the same key, e.g. an
616 // old leaf still kept alongside a renewed one).
617 const auto ee_spki = ee->subject_public_key_info();
618 bool skipped = false;
619 for(const auto& c : m_certificates) {
620 if(!skipped && c.subject_public_key_info() == ee_spki) {
621 skipped = true;
622 continue;
623 }
624 result.push_back(c);
625 }
626 } else {
627 // No end-entity (e.g. key-less bundle): treat the first stored cert as
628 // the "primary" and surface the rest as CA / chain certs. This matches
629 // the storage order used by parsing.
630 for(size_t i = 1; i < m_certificates.size(); ++i) {
631 result.push_back(m_certificates[i]);
632 }
633 }
634 return result;
635}
636
637std::optional<X509_Certificate> PKCS12::end_entity_certificate() const {
638 if(m_certificates.empty() || m_private_keys.empty()) {
639 return std::nullopt;
640 }
641 const auto& first_key = m_private_keys.front();
642 const auto key_spki = first_key->subject_public_key();
643 for(const auto& c : m_certificates) {
644 try {
645 if(c.subject_public_key_info() == key_spki) {
646 return c;
647 }
648 } catch(const Decoding_Error&) {
649 // Skip certificates with unsupported algorithms
650 }
651 }
652 return std::nullopt;
653}
654
655void PKCS12::add_key(std::shared_ptr<Private_Key> key) {
656 if(!key) {
657 throw Invalid_Argument("PKCS12::add_key: key must not be null");
658 }
659 m_private_keys.push_back(std::move(key));
660}
661
663 m_certificates.push_back(std::move(cert));
664}
665
666void PKCS12::set_friendly_name(std::string name) {
667 m_friendly_name = std::move(name);
668}
669
671 m_friendly_name.reset();
672}
673
674void PKCS12::set_local_key_id(std::vector<uint8_t> id) {
675 m_local_key_id = std::move(id);
676}
677
679 m_local_key_id.reset();
680}
681
682std::vector<uint8_t> PKCS12::export_to(const PKCS12_Export_Options& options, RandomNumberGenerator& rng) const {
683 if(m_private_keys.empty() && m_certificates.empty()) {
684 throw Invalid_Argument("PKCS#12::export_to requires at least a key or certificate");
685 }
686
687 validate_options(options);
688
689 // Determine end-entity certificate(s). With a single key we pair it
690 // against a cert matching its SPKI; that pair gets the
691 // friendly_name/localKeyId from options or the bundle.
692 std::optional<size_t> end_entity_idx;
693 if(!m_private_keys.empty() && !m_certificates.empty()) {
694 const auto& first_key = m_private_keys.front();
695 const auto key_spki = first_key->subject_public_key();
696 for(size_t i = 0; i < m_certificates.size(); ++i) {
697 try {
698 if(m_certificates[i].subject_public_key_info() == key_spki) {
699 end_entity_idx = i;
700 break;
701 }
702 } catch(const Decoding_Error&) {
703 // skip
704 }
705 }
706 if(!end_entity_idx) {
707 throw Invalid_Argument("PKCS#12::export_to: private key does not match any certificate");
708 }
709 }
710
711 const OID cert_bag_oid = OID::from_string("PKCS12.CertBag");
712 const OID shrouded_key_oid = OID::from_string("PKCS12.PKCS8ShroudedKeyBag");
713 const OID x509_cert_oid = OID::from_string("PKCS9.X509Certificate");
714 const OID friendly_name_oid = OID::from_string("PKCS9.FriendlyName");
715 const OID local_key_id_oid = OID::from_string("PKCS9.LocalKeyId");
716 const OID pkcs7_data_oid = OID::from_string("PKCS7.Data");
717 const OID pkcs7_enc_data_oid = OID::from_string("PKCS7.EncryptedData");
718
719 // Pick the friendly-name and local-key-id used by attribute encoding.
720 // Options take precedence over the bundle-level fields.
721 const std::optional<std::string>& friendly_name =
722 options.friendly_name().has_value() ? options.friendly_name() : m_friendly_name;
723
724 std::vector<uint8_t> local_key_id;
725 if(m_local_key_id) {
726 local_key_id = *m_local_key_id;
727 } else if(end_entity_idx) {
728 local_key_id = m_certificates[*end_entity_idx].subject_public_key_bitstring_sha1();
729 } else if(!m_private_keys.empty()) {
730 // Key-only bundle: derive from SHA-1 of the public key bits (matching the
731 // convention used by X509_Certificate::subject_public_key_bitstring_sha1).
732 auto sha1 = HashFunction::create_or_throw("SHA-1");
733 const auto pub_bits = m_private_keys.front()->public_key_bits();
734 sha1->update(pub_bits);
735 local_key_id = unlock(sha1->final());
736 }
737
738 auto write_attributes = [&](DER_Encoder& enc) {
739 const bool has_fn = friendly_name.has_value() && !friendly_name->empty();
740 const bool has_id = !local_key_id.empty();
741 if(!has_fn && !has_id) {
742 return;
743 }
744 enc.start_set();
745 if(has_fn) {
746 enc.start_sequence();
747 enc.encode(friendly_name_oid);
748 enc.start_set();
749 encode_bmpstring(enc, *friendly_name);
750 enc.end_cons();
751 enc.end_cons();
752 }
753 if(has_id) {
754 enc.start_sequence();
755 enc.encode(local_key_id_oid);
756 enc.start_set();
758 enc.end_cons();
759 enc.end_cons();
760 }
761 enc.end_cons();
762 };
763
764 // CertBags
765 std::vector<uint8_t> cert_safe_contents;
766 if(!m_certificates.empty()) {
767 DER_Encoder cert_bags(cert_safe_contents);
768 cert_bags.start_sequence();
769
770 auto add_cert_bag = [&](const X509_Certificate& c, bool add_attrs) {
771 cert_bags.start_sequence();
772 cert_bags.encode(cert_bag_oid);
773
774 cert_bags.start_context_specific(0);
775 cert_bags.start_sequence();
776 cert_bags.encode(x509_cert_oid);
777 cert_bags.start_context_specific(0);
779 cert_bags.end_cons();
780 cert_bags.end_cons();
781 cert_bags.end_cons();
782
783 if(add_attrs) {
784 write_attributes(cert_bags);
785 }
786
787 cert_bags.end_cons();
788 };
789
790 // End-entity first (so the file is read in the typical order), then
791 // the rest in their stored order.
792 if(end_entity_idx) {
793 add_cert_bag(m_certificates[*end_entity_idx], true);
794 for(size_t i = 0; i < m_certificates.size(); ++i) {
795 if(i != *end_entity_idx) {
796 add_cert_bag(m_certificates[i], false);
797 }
798 }
799 } else {
800 for(const auto& c : m_certificates) {
801 add_cert_bag(c, false);
802 }
803 }
804
805 cert_bags.end_cons();
806 }
807
808 // Key SafeBag(s)
809 std::vector<uint8_t> key_safe_contents;
810 if(!m_private_keys.empty()) {
811 DER_Encoder key_bags(key_safe_contents);
812 key_bags.start_sequence();
813
814 for(size_t i = 0; i < m_private_keys.size(); ++i) {
815 const Private_Key& key = *m_private_keys[i];
816
817 key_bags.start_sequence();
818 key_bags.encode(shrouded_key_oid);
819
821 auto [enc_algo, enc_key] =
822 pkcs12_pbe_encrypt(pkcs8_key, options.password(), options.key_encryption_algo(), options.iterations(), rng);
823
824 key_bags.start_context_specific(0);
825 key_bags.start_sequence();
826 key_bags.encode(enc_algo);
827 key_bags.encode(enc_key, ASN1_Type::OctetString);
828 key_bags.end_cons();
829 key_bags.end_cons();
830
831 // Only the first key carries the bundle-level attributes (preserves
832 // the historical single-key behavior).
833 if(i == 0) {
834 write_attributes(key_bags);
835 }
836
837 key_bags.end_cons();
838 }
839 key_bags.end_cons();
840 }
841
842 // AuthenticatedSafe
843 std::vector<uint8_t> auth_safe_content;
844 DER_Encoder auth_safe(auth_safe_content);
845 auth_safe.start_sequence();
846
847 if(!cert_safe_contents.empty()) {
848 if(!options.cert_encryption_algo().empty()) {
849 auto [enc_algo, enc_data] = pkcs12_pbe_encrypt(
850 cert_safe_contents, options.password(), options.cert_encryption_algo(), options.iterations(), rng);
851
852 auth_safe.start_sequence();
853 auth_safe.encode(pkcs7_enc_data_oid);
854 auth_safe.start_context_specific(0);
855 auth_safe.start_sequence();
856 auth_safe.encode(size_t(0));
857 auth_safe.start_sequence();
858 auth_safe.encode(pkcs7_data_oid);
859 auth_safe.encode(enc_algo);
860 auth_safe.add_object(ASN1_Type(0), ASN1_Class::ContextSpecific, enc_data);
861 auth_safe.end_cons();
862 auth_safe.end_cons();
863 auth_safe.end_cons();
864 auth_safe.end_cons();
865 } else {
866 auth_safe.start_sequence();
867 auth_safe.encode(pkcs7_data_oid);
868 auth_safe.start_context_specific(0);
869 auth_safe.encode(cert_safe_contents, ASN1_Type::OctetString);
870 auth_safe.end_cons();
871 auth_safe.end_cons();
872 }
873 }
874
875 if(!key_safe_contents.empty()) {
876 auth_safe.start_sequence();
877 auth_safe.encode(pkcs7_data_oid);
878 auth_safe.start_context_specific(0);
879 auth_safe.encode(key_safe_contents, ASN1_Type::OctetString);
880 auth_safe.end_cons();
881 auth_safe.end_cons();
882 }
883
884 auth_safe.end_cons();
885
886 // PFX
887 std::vector<uint8_t> pfx_data;
888 DER_Encoder pfx(pfx_data);
889 pfx.start_sequence();
890 pfx.encode(size_t(3));
891
892 pfx.start_sequence();
893 pfx.encode(pkcs7_data_oid);
895 pfx.encode(auth_safe_content, ASN1_Type::OctetString);
896 pfx.end_cons();
897 pfx.end_cons();
898
899 if(options.include_mac()) {
900 const std::string& mac_hash = options.mac_digest();
901
902 auto hmac = MessageAuthenticationCode::create_or_throw(fmt("HMAC({})", mac_hash));
903
904 std::vector<uint8_t> mac_salt(hmac->output_length());
905 rng.randomize(mac_salt.data(), mac_salt.size());
906 const size_t mac_key_len = hmac->output_length();
907 secure_vector<uint8_t> mac_key(mac_key_len);
908 const PKCS12_KDF kdf(HashFunction::create_or_throw(mac_hash), 3, options.iterations());
909 kdf.derive_key(mac_key.data(),
910 mac_key_len,
911 options.password().data(),
912 options.password().size(),
913 mac_salt.data(),
914 mac_salt.size());
915
916 hmac->set_key(mac_key);
917 hmac->update(auth_safe_content);
918 const secure_vector<uint8_t> mac_value = hmac->final();
919
920 pfx.start_sequence();
921 pfx.start_sequence();
922 const auto param_encoding =
924 pfx.encode(AlgorithmIdentifier(OID::from_string(mac_hash), param_encoding));
925 pfx.encode(mac_value, ASN1_Type::OctetString);
926 pfx.end_cons();
927 pfx.encode(mac_salt, ASN1_Type::OctetString);
928 if(options.iterations() != 1) {
929 pfx.encode(options.iterations());
930 }
931 pfx.end_cons();
932 }
933
934 pfx.end_cons();
935
936 return pfx_data;
937}
938
939} // namespace Botan
std::vector< uint8_t > BER_encode() const
Definition asn1_obj.cpp:21
const std::string & value() const
Definition asn1_obj.h:590
const OID & oid() const
Definition asn1_obj.h:688
BER_Decoder start_set()
Definition ber_dec.h:281
void push_back(const BER_Object &obj)
Definition ber_dec.cpp:600
BER_Decoder & decode(bool &out)
Definition ber_dec.h:358
bool more_items() const
Definition ber_dec.cpp:461
BER_Decoder & verify_end()
Definition ber_dec.cpp:471
BER_Decoder & end_cons()
Definition ber_dec.cpp:630
BER_Decoder & discard_remaining()
Definition ber_dec.cpp:488
BER_Decoder start_sequence()
Definition ber_dec.h:275
BER_Decoder start_context_specific(uint32_t tag)
Definition ber_dec.h:290
DER_Encoder & add_object(ASN1_Type type_tag, ASN1_Class class_tag, const uint8_t rep[], size_t length)
Definition der_enc.cpp:285
DER_Encoder & start_context_specific(uint32_t tag)
Definition der_enc.h:113
DER_Encoder & start_sequence()
Definition der_enc.h:86
DER_Encoder & end_cons()
Definition der_enc.cpp:208
DER_Encoder & encode(bool b)
Definition der_enc.cpp:313
static std::unique_ptr< HashFunction > create_or_throw(std::string_view algo_spec, std::string_view provider="")
Definition hash.cpp:308
static std::unique_ptr< HashFunction > create(std::string_view algo_spec, std::string_view provider="")
Definition hash.cpp:111
static std::unique_ptr< MessageAuthenticationCode > create_or_throw(std::string_view algo_spec, std::string_view provider="")
Definition mac.cpp:149
static OID from_string(std::string_view str)
Definition asn1_oid.cpp:80
const std::optional< std::string > & friendly_name() const
Definition pkcs12.h:88
PKCS12_Export_Options & without_mac()
Disable the integrity MAC. Generally not recommended.
Definition pkcs12.cpp:440
PKCS12_Export_Options & with_mac_digest(std::string algo)
Set the digest used for the integrity MAC.
Definition pkcs12.cpp:435
static PKCS12_Export_Options modern(std::string_view password, std::optional< std::string > friendly_name={})
Definition pkcs12.cpp:401
const std::string & key_encryption_algo() const
Definition pkcs12.h:92
PKCS12_Export_Options & with_friendly_name(std::string name)
Override the friendly-name attribute (otherwise taken from the bundle).
Definition pkcs12.cpp:415
static PKCS12_Export_Options legacy_compat(std::string_view password, std::optional< std::string > friendly_name={})
Definition pkcs12.cpp:406
size_t iterations() const
Definition pkcs12.h:90
const std::string & password() const
Definition pkcs12.h:86
bool include_mac() const
Definition pkcs12.h:99
const std::string & cert_encryption_algo() const
Empty means: store certificates unencrypted.
Definition pkcs12.h:95
PKCS12_Export_Options & with_cert_encryption_algo(std::string algo)
Definition pkcs12.cpp:430
PKCS12_Export_Options & with_iterations(size_t n)
Set number of KDF iterations.
Definition pkcs12.cpp:420
const std::string & mac_digest() const
Definition pkcs12.h:97
PKCS12_Export_Options(std::string_view password, std::optional< std::string > friendly_name={})
Definition pkcs12.cpp:398
PKCS12_Export_Options & with_key_encryption_algo(std::string algo)
Set the private key encryption algorithm (PKCS#12 PBE or PBES2 name).
Definition pkcs12.cpp:425
void derive_key(uint8_t out[], size_t out_len, const char *password, size_t password_len, const uint8_t salt[], size_t salt_len) const override
std::vector< uint8_t > export_to(const PKCS12_Export_Options &options, RandomNumberGenerator &rng) const
Definition pkcs12.cpp:682
void clear_local_key_id()
Clear the localKeyId attribute.
Definition pkcs12.cpp:678
const std::optional< std::vector< uint8_t > > & local_key_id() const
Definition pkcs12.h:200
std::vector< X509_Certificate > ca_certificates() const
Definition pkcs12.cpp:606
const std::optional< std::string > & friendly_name() const
Definition pkcs12.h:194
void add_certificate(X509_Certificate cert)
Definition pkcs12.cpp:662
void clear_friendly_name()
Clear the friendly-name attribute.
Definition pkcs12.cpp:670
std::optional< X509_Certificate > end_entity_certificate() const
Definition pkcs12.cpp:637
PKCS12()=default
Construct an empty bundle.
void set_friendly_name(std::string name)
Set (or replace) the friendly-name attribute.
Definition pkcs12.cpp:666
void add_key(std::shared_ptr< Private_Key > key)
Add a private key. PKCS#12 supports multiple keys per file.
Definition pkcs12.cpp:655
void set_local_key_id(std::vector< uint8_t > id)
Set (or replace) the localKeyId attribute.
Definition pkcs12.cpp:674
void randomize(std::span< uint8_t > output)
Definition rng.h:86
std::vector< uint8_t > BER_encode(const Private_Key &key, RandomNumberGenerator &rng, std::string_view pass, std::chrono::milliseconds msec, std::string_view pbe_algo)
Definition pkcs8.cpp:167
std::unique_ptr< Private_Key > load_key(DataSource &source, const std::function< std::string()> &get_pass)
Definition pkcs8.cpp:319
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
ASN1_Type
Definition asn1_obj.h:47
secure_vector< uint8_t > pkcs12_pbe_decrypt(std::span< const uint8_t > ciphertext, std::string_view password, const AlgorithmIdentifier &pbe_algo, bool openssl_empty_pwd_compat)
std::vector< T > unlock(const secure_vector< T > &in)
Definition secmem.h:155
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128
std::pair< AlgorithmIdentifier, std::vector< uint8_t > > pkcs12_pbe_encrypt(std::span< const uint8_t > plaintext, std::string_view password, std::string_view algo, size_t iterations, RandomNumberGenerator &rng)
bool constant_time_compare(std::span< const uint8_t > x, std::span< const uint8_t > y)
Definition mem_ops.cpp:17
void pkcs12_kdf(std::span< uint8_t > out, std::span< const uint8_t > pwd_bytes, std::span< const uint8_t > salt, size_t iterations, uint8_t id, HashFunction &hash)
std::vector< uint8_t > utf8_to_ucs2(std::string_view utf8)
Definition charset.cpp:137
constexpr size_t PKCS12_MAX_ITERATIONS
Definition pkcs12_pbe.h:25