Botan 2.19.1
Crypto and TLS for C&
pkcs8.cpp
Go to the documentation of this file.
1/*
2* PKCS #8
3* (C) 1999-2010,2014,2018 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#include <botan/pkcs8.h>
9#include <botan/rng.h>
10#include <botan/der_enc.h>
11#include <botan/ber_dec.h>
12#include <botan/asn1_obj.h>
13#include <botan/oids.h>
14#include <botan/pem.h>
15#include <botan/scan_name.h>
16#include <botan/pk_algs.h>
17
18#if defined(BOTAN_HAS_PKCS5_PBES2)
19 #include <botan/pbes2.h>
20#endif
21
22namespace Botan {
23
24namespace PKCS8 {
25
26namespace {
27
28/*
29* Get info from an EncryptedPrivateKeyInfo
30*/
31secure_vector<uint8_t> PKCS8_extract(DataSource& source,
32 AlgorithmIdentifier& pbe_alg_id)
33 {
35
36 BER_Decoder(source)
38 .decode(pbe_alg_id)
39 .decode(key_data, OCTET_STRING)
40 .verify_end();
41
42 return key_data;
43 }
44
45/*
46* PEM decode and/or decrypt a private key
47*/
48secure_vector<uint8_t> PKCS8_decode(
49 DataSource& source,
50 std::function<std::string ()> get_passphrase,
51 AlgorithmIdentifier& pk_alg_id,
52 bool is_encrypted)
53 {
54 AlgorithmIdentifier pbe_alg_id;
55 secure_vector<uint8_t> key_data, key;
56
57 try {
58 if(ASN1::maybe_BER(source) && !PEM_Code::matches(source))
59 {
60 if(is_encrypted)
61 {
62 key_data = PKCS8_extract(source, pbe_alg_id);
63 }
64 else
65 {
66 // todo read more efficiently
67 while(!source.end_of_data())
68 {
69 uint8_t b;
70 size_t read = source.read_byte(b);
71 if(read)
72 {
73 key_data.push_back(b);
74 }
75 }
76 }
77 }
78 else
79 {
80 std::string label;
81 key_data = PEM_Code::decode(source, label);
82
83 // todo remove autodetect for pem as well?
84 if(label == "PRIVATE KEY")
85 is_encrypted = false;
86 else if(label == "ENCRYPTED PRIVATE KEY")
87 {
88 DataSource_Memory key_source(key_data);
89 key_data = PKCS8_extract(key_source, pbe_alg_id);
90 }
91 else
92 throw PKCS8_Exception("Unknown PEM label " + label);
93 }
94
95 if(key_data.empty())
96 throw PKCS8_Exception("No key data found");
97 }
98 catch(Decoding_Error& e)
99 {
100 throw Decoding_Error("PKCS #8 private key decoding", e);
101 }
102
103 try
104 {
105 if(is_encrypted)
106 {
107 if(OIDS::oid2str_or_throw(pbe_alg_id.get_oid()) != "PBE-PKCS5v20")
108 throw PKCS8_Exception("Unknown PBE type " + pbe_alg_id.get_oid().to_string());
109#if defined(BOTAN_HAS_PKCS5_PBES2)
110 key = pbes2_decrypt(key_data, get_passphrase(), pbe_alg_id.get_parameters());
111#else
112 BOTAN_UNUSED(get_passphrase);
113 throw Decoding_Error("Private key is encrypted but PBES2 was disabled in build");
114#endif
115 }
116 else
117 key = key_data;
118
119 BER_Decoder(key)
121 .decode_and_check<size_t>(0, "Unknown PKCS #8 version number")
122 .decode(pk_alg_id)
123 .decode(key, OCTET_STRING)
124 .discard_remaining()
125 .end_cons();
126 }
127 catch(std::exception& e)
128 {
129 throw Decoding_Error("PKCS #8 private key decoding", e);
130 }
131 return key;
132 }
133
134}
135
136/*
137* BER encode a PKCS #8 private key, unencrypted
138*/
140 {
141 // keeping around for compat
142 return key.private_key_info();
143 }
144
145/*
146* PEM encode a PKCS #8 private key, unencrypted
147*/
148std::string PEM_encode(const Private_Key& key)
149 {
150 return PEM_Code::encode(PKCS8::BER_encode(key), "PRIVATE KEY");
151 }
152
153#if defined(BOTAN_HAS_PKCS5_PBES2)
154
155namespace {
156
157std::pair<std::string, std::string>
158choose_pbe_params(const std::string& pbe_algo, const std::string& key_algo)
159 {
160 if(pbe_algo.empty())
161 {
162 /*
163 * For algorithms where we are using a non-RFC format anyway, default to
164 * SIV or GCM. For others (RSA, ECDSA, ...) default to something widely
165 * compatible.
166 */
167 const bool nonstandard_pk = (key_algo == "McEliece" || key_algo == "XMSS");
168
169 if(nonstandard_pk)
170 {
171#if defined(BOTAN_HAS_AEAD_SIV) && defined(BOTAN_HAS_SHA2_64)
172 return std::make_pair("AES-256/SIV", "SHA-512");
173#elif defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64)
174 return std::make_pair("AES-256/GCM", "SHA-512");
175#endif
176 }
177
178 // Default is something compatible with everyone else
179 return std::make_pair("AES-256/CBC", "SHA-256");
180 }
181
182 SCAN_Name request(pbe_algo);
183
184 if(request.arg_count() != 2 ||
185 (request.algo_name() != "PBE-PKCS5v20" && request.algo_name() != "PBES2"))
186 {
187 throw Invalid_Argument("Unsupported PBE " + pbe_algo);
188 }
189
190 return std::make_pair(request.arg(0), request.arg(1));
191 }
192
193}
194
195#endif
196
197/*
198* BER encode a PKCS #8 private key, encrypted
199*/
200std::vector<uint8_t> BER_encode(const Private_Key& key,
202 const std::string& pass,
203 std::chrono::milliseconds msec,
204 const std::string& pbe_algo)
205 {
206#if defined(BOTAN_HAS_PKCS5_PBES2)
207 const auto pbe_params = choose_pbe_params(pbe_algo, key.algo_name());
208
209 const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
210 pbes2_encrypt_msec(PKCS8::BER_encode(key), pass, msec, nullptr,
211 pbe_params.first, pbe_params.second, rng);
212
213 std::vector<uint8_t> output;
214 DER_Encoder der(output);
216 .encode(pbe_info.first)
217 .encode(pbe_info.second, OCTET_STRING)
218 .end_cons();
219
220 return output;
221#else
222 BOTAN_UNUSED(key, rng, pass, msec, pbe_algo);
223 throw Encoding_Error("PKCS8::BER_encode cannot encrypt because PBES2 was disabled in build");
224#endif
225 }
226
227/*
228* PEM encode a PKCS #8 private key, encrypted
229*/
230std::string PEM_encode(const Private_Key& key,
232 const std::string& pass,
233 std::chrono::milliseconds msec,
234 const std::string& pbe_algo)
235 {
236 if(pass.empty())
237 return PEM_encode(key);
238
239 return PEM_Code::encode(PKCS8::BER_encode(key, rng, pass, msec, pbe_algo),
240 "ENCRYPTED PRIVATE KEY");
241 }
242
243/*
244* BER encode a PKCS #8 private key, encrypted
245*/
246std::vector<uint8_t> BER_encode_encrypted_pbkdf_iter(const Private_Key& key,
248 const std::string& pass,
249 size_t pbkdf_iterations,
250 const std::string& cipher,
251 const std::string& pbkdf_hash)
252 {
253#if defined(BOTAN_HAS_PKCS5_PBES2)
254 const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
256 pass, pbkdf_iterations,
257 cipher.empty() ? "AES-256/CBC" : cipher,
258 pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
259 rng);
260
261 std::vector<uint8_t> output;
262 DER_Encoder der(output);
264 .encode(pbe_info.first)
265 .encode(pbe_info.second, OCTET_STRING)
266 .end_cons();
267
268 return output;
269
270#else
271 BOTAN_UNUSED(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash);
272 throw Encoding_Error("PKCS8::BER_encode_encrypted_pbkdf_iter cannot encrypt because PBES2 disabled in build");
273#endif
274 }
275
276/*
277* PEM encode a PKCS #8 private key, encrypted
278*/
281 const std::string& pass,
282 size_t pbkdf_iterations,
283 const std::string& cipher,
284 const std::string& pbkdf_hash)
285 {
286 return PEM_Code::encode(
287 PKCS8::BER_encode_encrypted_pbkdf_iter(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash),
288 "ENCRYPTED PRIVATE KEY");
289 }
290
291/*
292* BER encode a PKCS #8 private key, encrypted
293*/
294std::vector<uint8_t> BER_encode_encrypted_pbkdf_msec(const Private_Key& key,
296 const std::string& pass,
297 std::chrono::milliseconds pbkdf_msec,
298 size_t* pbkdf_iterations,
299 const std::string& cipher,
300 const std::string& pbkdf_hash)
301 {
302#if defined(BOTAN_HAS_PKCS5_PBES2)
303 const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
305 pbkdf_msec, pbkdf_iterations,
306 cipher.empty() ? "AES-256/CBC" : cipher,
307 pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
308 rng);
309
310 std::vector<uint8_t> output;
311 DER_Encoder(output)
313 .encode(pbe_info.first)
314 .encode(pbe_info.second, OCTET_STRING)
315 .end_cons();
316
317 return output;
318#else
319 BOTAN_UNUSED(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash);
320 throw Encoding_Error("BER_encode_encrypted_pbkdf_msec cannot encrypt because PBES2 disabled in build");
321#endif
322 }
323
324/*
325* PEM encode a PKCS #8 private key, encrypted
326*/
329 const std::string& pass,
330 std::chrono::milliseconds pbkdf_msec,
331 size_t* pbkdf_iterations,
332 const std::string& cipher,
333 const std::string& pbkdf_hash)
334 {
335 return PEM_Code::encode(
336 PKCS8::BER_encode_encrypted_pbkdf_msec(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash),
337 "ENCRYPTED PRIVATE KEY");
338 }
339
340namespace {
341
342/*
343* Extract a private key (encrypted/unencrypted) and return it
344*/
345std::unique_ptr<Private_Key>
346load_key(DataSource& source,
347 std::function<std::string ()> get_pass,
348 bool is_encrypted)
349 {
350 AlgorithmIdentifier alg_id;
351 secure_vector<uint8_t> pkcs8_key = PKCS8_decode(source, get_pass, alg_id, is_encrypted);
352
353 const std::string alg_name = OIDS::oid2str_or_empty(alg_id.get_oid());
354 if(alg_name.empty())
355 throw PKCS8_Exception("Unknown algorithm OID: " +
356 alg_id.get_oid().to_string());
357
358 return load_private_key(alg_id, pkcs8_key);
359 }
360
361}
362
363/*
364* Extract an encrypted private key and return it
365*/
366std::unique_ptr<Private_Key> load_key(DataSource& source,
367 std::function<std::string ()> get_pass)
368 {
369 return load_key(source, get_pass, true);
370 }
371
372/*
373* Extract an encrypted private key and return it
374*/
375std::unique_ptr<Private_Key> load_key(DataSource& source,
376 const std::string& pass)
377 {
378 // We need to use bind rather than a lambda capturing `pass` here in order to avoid a Clang 8 bug.
379 // See https://github.com/randombit/botan/issues/2255.
380 return load_key(source, std::bind([](const std::string p) { return p; }, pass), true);
381 }
382
383/*
384* Extract an unencrypted private key and return it
385*/
386std::unique_ptr<Private_Key> load_key(DataSource& source)
387 {
388 auto fail_fn = []() -> std::string {
389 throw PKCS8_Exception("Internal error: Attempt to read password for unencrypted key");
390 };
391
392 return load_key(source, fail_fn, false);
393 }
394
395/*
396* Make a copy of this private key
397*/
398std::unique_ptr<Private_Key> copy_key(const Private_Key& key)
399 {
400 DataSource_Memory source(PEM_encode(key));
401 return PKCS8::load_key(source);
402 }
403
404/*
405* Extract an encrypted private key and return it
406*/
409 std::function<std::string ()> get_pass)
410 {
411 BOTAN_UNUSED(rng);
412 return PKCS8::load_key(source, get_pass).release();
413 }
414
415/*
416* Extract an encrypted private key and return it
417*/
420 const std::string& pass)
421 {
422 BOTAN_UNUSED(rng);
423 return PKCS8::load_key(source, pass).release();
424 }
425
426/*
427* Extract an unencrypted private key and return it
428*/
431 {
432 BOTAN_UNUSED(rng);
433 return PKCS8::load_key(source).release();
434 }
435
436#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
437
438/*
439* Extract an encrypted private key and return it
440*/
441Private_Key* load_key(const std::string& fsname,
443 std::function<std::string ()> get_pass)
444 {
445 BOTAN_UNUSED(rng);
446 DataSource_Stream in(fsname);
447 return PKCS8::load_key(in, get_pass).release();
448 }
449
450/*
451* Extract an encrypted private key and return it
452*/
453Private_Key* load_key(const std::string& fsname,
454 RandomNumberGenerator& rng,
455 const std::string& pass)
456 {
457 BOTAN_UNUSED(rng);
458 DataSource_Stream in(fsname);
459 // We need to use bind rather than a lambda capturing `pass` here in order to avoid a Clang 8 bug.
460 // See https://github.com/randombit/botan/issues/2255.
461 return PKCS8::load_key(in, std::bind([](const std::string p) { return p; }, pass)).release();
462 }
463
464/*
465* Extract an unencrypted private key and return it
466*/
467Private_Key* load_key(const std::string& fsname,
468 RandomNumberGenerator& rng)
469 {
470 BOTAN_UNUSED(rng);
471 DataSource_Stream in(fsname);
472 return PKCS8::load_key(in).release();
473 }
474#endif
475
476/*
477* Make a copy of this private key
478*/
481 {
482 BOTAN_UNUSED(rng);
483 return PKCS8::copy_key(key).release();
484 }
485
486
487
488}
489
490}
#define BOTAN_UNUSED(...)
Definition: assert.h:142
const std::vector< uint8_t > & get_parameters() const
Definition: asn1_obj.h:446
const OID & get_oid() const
Definition: asn1_obj.h:445
BER_Decoder start_cons(ASN1_Tag type_tag, ASN1_Tag class_tag=UNIVERSAL)
Definition: ber_dec.cpp:290
BER_Decoder & decode(bool &out)
Definition: ber_dec.h:170
BER_Decoder & decode_and_check(const T &expected, const std::string &error_msg)
Definition: ber_dec.h:277
BER_Decoder & verify_end()
Definition: ber_dec.cpp:208
DER_Encoder & start_cons(ASN1_Tag type_tag, ASN1_Tag class_tag=UNIVERSAL)
Definition: der_enc.cpp:181
DER_Encoder & end_cons()
Definition: der_enc.cpp:191
DER_Encoder & encode(bool b)
Definition: der_enc.cpp:285
size_t read_byte(uint8_t &out)
Definition: data_src.cpp:23
virtual bool end_of_data() const =0
std::string to_string() const
Definition: asn1_oid.cpp:98
secure_vector< uint8_t > private_key_info() const
Definition: pk_keys.cpp:61
virtual std::string algo_name() const =0
bool maybe_BER(DataSource &source)
Definition: asn1_obj.cpp:222
BOTAN_UNSTABLE_API std::string oid2str_or_throw(const OID &oid)
Definition: oids.cpp:121
BOTAN_UNSTABLE_API std::string oid2str_or_empty(const OID &oid)
Definition: oids.cpp:111
std::string encode(const uint8_t der[], size_t length, const std::string &label, size_t width)
Definition: pem.cpp:43
bool matches(DataSource &source, const std::string &extra, size_t search_range)
Definition: pem.cpp:142
secure_vector< uint8_t > decode(DataSource &source, std::string &label)
Definition: pem.cpp:68
std::unique_ptr< Private_Key > copy_key(const Private_Key &key)
Definition: pkcs8.cpp:398
std::vector< uint8_t > BER_encode_encrypted_pbkdf_iter(const Private_Key &key, RandomNumberGenerator &rng, const std::string &pass, size_t pbkdf_iterations, const std::string &cipher, const std::string &pbkdf_hash)
Definition: pkcs8.cpp:246
std::string PEM_encode(const Private_Key &key)
Definition: pkcs8.cpp:148
std::string PEM_encode_encrypted_pbkdf_msec(const Private_Key &key, RandomNumberGenerator &rng, const std::string &pass, std::chrono::milliseconds pbkdf_msec, size_t *pbkdf_iterations, const std::string &cipher, const std::string &pbkdf_hash)
Definition: pkcs8.cpp:327
std::unique_ptr< Private_Key > load_key(DataSource &source, std::function< std::string()> get_pass)
Definition: pkcs8.cpp:366
secure_vector< uint8_t > BER_encode(const Private_Key &key)
Definition: pkcs8.cpp:139
std::vector< uint8_t > BER_encode_encrypted_pbkdf_msec(const Private_Key &key, RandomNumberGenerator &rng, const std::string &pass, std::chrono::milliseconds pbkdf_msec, size_t *pbkdf_iterations, const std::string &cipher, const std::string &pbkdf_hash)
Definition: pkcs8.cpp:294
std::string PEM_encode_encrypted_pbkdf_iter(const Private_Key &key, RandomNumberGenerator &rng, const std::string &pass, size_t pbkdf_iterations, const std::string &cipher, const std::string &pbkdf_hash)
Definition: pkcs8.cpp:279
Definition: alg_id.cpp:13
std::unique_ptr< Private_Key > load_private_key(const AlgorithmIdentifier &alg_id, const secure_vector< uint8_t > &key_bits)
Definition: pk_algs.cpp:163
std::pair< AlgorithmIdentifier, std::vector< uint8_t > > pbes2_encrypt_msec(const secure_vector< uint8_t > &key_bits, const std::string &passphrase, std::chrono::milliseconds msec, size_t *out_iterations_if_nonnull, const std::string &cipher, const std::string &digest, RandomNumberGenerator &rng)
Definition: pbes2.cpp:273
std::pair< AlgorithmIdentifier, std::vector< uint8_t > > pbes2_encrypt_iter(const secure_vector< uint8_t > &key_bits, const std::string &passphrase, size_t pbkdf_iter, const std::string &cipher, const std::string &digest, RandomNumberGenerator &rng)
Definition: pbes2.cpp:292
@ SEQUENCE
Definition: asn1_obj.h:42
@ OCTET_STRING
Definition: asn1_obj.h:38
std::vector< T, secure_allocator< T > > secure_vector
Definition: secmem.h:65
secure_vector< uint8_t > pbes2_decrypt(const secure_vector< uint8_t > &key_bits, const std::string &passphrase, const std::vector< uint8_t > &params)
Definition: pbes2.cpp:303