Botan  2.18.1
Crypto and TLS for C++11
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 
22 namespace Botan {
23 
24 namespace PKCS8 {
25 
26 namespace {
27 
28 /*
29 * Get info from an EncryptedPrivateKeyInfo
30 */
31 secure_vector<uint8_t> PKCS8_extract(DataSource& source,
32  AlgorithmIdentifier& pbe_alg_id)
33  {
34  secure_vector<uint8_t> key_data;
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 */
48 secure_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 */
148 std::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 
155 namespace {
156 
157 std::pair<std::string, std::string>
158 choose_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 */
200 std::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);
215  der.start_cons(SEQUENCE)
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 */
230 std::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 */
246 std::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);
263  der.start_cons(SEQUENCE)
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 */
294 std::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 
340 namespace {
341 
342 /*
343 * Extract a private key (encrypted/unencrypted) and return it
344 */
345 std::unique_ptr<Private_Key>
346 load_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 */
366 std::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 */
375 std::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 */
386 std::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 */
398 std::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 */
441 Private_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 */
453 Private_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 */
467 Private_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 }
BOTAN_UNSTABLE_API std::string oid2str_or_throw(const OID &oid)
Definition: oids.cpp:121
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::unique_ptr< Private_Key > copy_key(const Private_Key &key)
Definition: pkcs8.cpp:398
BER_Decoder & decode_and_check(const T &expected, const std::string &error_msg)
Definition: ber_dec.h:277
BOTAN_UNSTABLE_API std::string oid2str_or_empty(const OID &oid)
Definition: oids.cpp:111
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
virtual std::string algo_name() const =0
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
bool maybe_BER(DataSource &source)
Definition: asn1_obj.cpp:222
DER_Encoder & end_cons()
Definition: der_enc.cpp:191
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
BER_Decoder & decode(bool &out)
Definition: ber_dec.h:170
std::string to_string() const
Definition: asn1_oid.cpp:98
DER_Encoder & encode(bool b)
Definition: der_enc.cpp:285
secure_vector< uint8_t > private_key_info() const
Definition: pk_keys.cpp:61
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
virtual bool end_of_data() const =0
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
std::string PEM_encode(const Private_Key &key)
Definition: pkcs8.cpp:148
secure_vector< uint8_t > decode(DataSource &source, std::string &label)
Definition: pem.cpp:68
BER_Decoder start_cons(ASN1_Tag type_tag, ASN1_Tag class_tag=UNIVERSAL)
Definition: ber_dec.cpp:290
Definition: alg_id.cpp:13
size_t read_byte(uint8_t &out)
Definition: data_src.cpp:23
secure_vector< uint8_t > BER_encode(const Private_Key &key)
Definition: pkcs8.cpp:139
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
#define BOTAN_UNUSED(...)
Definition: assert.h:142
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
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 & 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
std::vector< T, secure_allocator< T > > secure_vector
Definition: secmem.h:65
std::unique_ptr< Private_Key > load_key(DataSource &source, std::function< std::string()> get_pass)
Definition: pkcs8.cpp:366
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