Botan  2.4.0
Crypto and TLS for C++11
pkcs8.cpp
Go to the documentation of this file.
1 /*
2 * PKCS #8
3 * (C) 1999-2010,2014 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/alg_id.h>
13 #include <botan/oids.h>
14 #include <botan/pem.h>
15 #include <botan/pbes2.h>
16 #include <botan/scan_name.h>
17 #include <botan/pk_algs.h>
18 
19 namespace Botan {
20 
21 namespace PKCS8 {
22 
23 namespace {
24 
25 /*
26 * Get info from an EncryptedPrivateKeyInfo
27 */
28 secure_vector<uint8_t> PKCS8_extract(DataSource& source,
29  AlgorithmIdentifier& pbe_alg_id)
30  {
31  secure_vector<uint8_t> key_data;
32 
33  BER_Decoder(source)
35  .decode(pbe_alg_id)
36  .decode(key_data, OCTET_STRING)
37  .verify_end();
38 
39  return key_data;
40  }
41 
42 /*
43 * PEM decode and/or decrypt a private key
44 */
45 secure_vector<uint8_t> PKCS8_decode(
46  DataSource& source,
47  std::function<std::string ()> get_passphrase,
48  AlgorithmIdentifier& pk_alg_id,
49  bool is_encrypted)
50  {
51  AlgorithmIdentifier pbe_alg_id;
52  secure_vector<uint8_t> key_data, key;
53 
54  try {
55  if(ASN1::maybe_BER(source) && !PEM_Code::matches(source))
56  {
57  if ( is_encrypted )
58  {
59  key_data = PKCS8_extract(source, pbe_alg_id);
60  }
61  else
62  {
63  // todo read more efficiently
64  while ( !source.end_of_data() )
65  {
66  uint8_t b;
67  size_t read = source.read_byte( b );
68  if ( read )
69  {
70  key_data.push_back( b );
71  }
72  }
73  }
74  }
75  else
76  {
77  std::string label;
78  key_data = PEM_Code::decode(source, label);
79 
80  // todo remove autodetect for pem as well?
81  if(label == "PRIVATE KEY")
82  is_encrypted = false;
83  else if(label == "ENCRYPTED PRIVATE KEY")
84  {
85  DataSource_Memory key_source(key_data);
86  key_data = PKCS8_extract(key_source, pbe_alg_id);
87  }
88  else
89  throw PKCS8_Exception("Unknown PEM label " + label);
90  }
91 
92  if(key_data.empty())
93  throw PKCS8_Exception("No key data found");
94  }
95  catch(Decoding_Error& e)
96  {
97  throw Decoding_Error("PKCS #8 private key decoding failed: " + std::string(e.what()));
98  }
99 
100  try
101  {
102  if(is_encrypted)
103  {
104  if(OIDS::lookup(pbe_alg_id.get_oid()) != "PBE-PKCS5v20")
105  throw Exception("Unknown PBE type " + pbe_alg_id.get_oid().as_string());
106  key = pbes2_decrypt(key_data, get_passphrase(), pbe_alg_id.get_parameters());
107  }
108  else
109  key = key_data;
110 
111  BER_Decoder(key)
113  .decode_and_check<size_t>(0, "Unknown PKCS #8 version number")
114  .decode(pk_alg_id)
115  .decode(key, OCTET_STRING)
116  .discard_remaining()
117  .end_cons();
118  }
119  catch(std::exception& e)
120  {
121  throw Decoding_Error("PKCS #8 private key decoding failed: " + std::string(e.what()));
122  }
123  return key;
124  }
125 
126 }
127 
128 /*
129 * BER encode a PKCS #8 private key, unencrypted
130 */
132  {
133  // keeping around for compat
134  return key.private_key_info();
135  }
136 
137 /*
138 * PEM encode a PKCS #8 private key, unencrypted
139 */
140 std::string PEM_encode(const Private_Key& key)
141  {
142  return PEM_Code::encode(PKCS8::BER_encode(key), "PRIVATE KEY");
143  }
144 
145 namespace {
146 
147 std::pair<std::string, std::string>
148 choose_pbe_params(const std::string& pbe_algo, const std::string& key_algo)
149  {
150  if(pbe_algo.empty())
151  {
152  // Defaults:
153  if(key_algo == "Curve25519" || key_algo == "McEliece")
154  return std::make_pair("AES-256/GCM", "SHA-512");
155  else // for everything else (RSA, DSA, ECDSA, GOST, ...)
156  return std::make_pair("AES-256/CBC", "SHA-256");
157  }
158 
159  SCAN_Name request(pbe_algo);
160  if(request.algo_name() != "PBE-PKCS5v20" || request.arg_count() != 2)
161  throw Exception("Unsupported PBE " + pbe_algo);
162  return std::make_pair(request.arg(1), request.arg(0));
163  }
164 
165 }
166 
167 /*
168 * BER encode a PKCS #8 private key, encrypted
169 */
170 std::vector<uint8_t> BER_encode(const Private_Key& key,
172  const std::string& pass,
173  std::chrono::milliseconds msec,
174  const std::string& pbe_algo)
175  {
176  const auto pbe_params = choose_pbe_params(pbe_algo, key.algo_name());
177 
178  const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
179  pbes2_encrypt_msec(PKCS8::BER_encode(key), pass, msec, nullptr,
180  pbe_params.first, pbe_params.second, rng);
181 
182  return DER_Encoder()
184  .encode(pbe_info.first)
185  .encode(pbe_info.second, OCTET_STRING)
186  .end_cons()
188  }
189 
190 /*
191 * PEM encode a PKCS #8 private key, encrypted
192 */
193 std::string PEM_encode(const Private_Key& key,
195  const std::string& pass,
196  std::chrono::milliseconds msec,
197  const std::string& pbe_algo)
198  {
199  if(pass.empty())
200  return PEM_encode(key);
201 
202  return PEM_Code::encode(PKCS8::BER_encode(key, rng, pass, msec, pbe_algo),
203  "ENCRYPTED PRIVATE KEY");
204  }
205 
206 /*
207 * BER encode a PKCS #8 private key, encrypted
208 */
209 std::vector<uint8_t> BER_encode_encrypted_pbkdf_iter(const Private_Key& key,
211  const std::string& pass,
212  size_t pbkdf_iterations,
213  const std::string& cipher,
214  const std::string& pbkdf_hash)
215  {
216  const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
218  pass, pbkdf_iterations,
219  cipher.empty() ? "AES-256/CBC" : cipher,
220  pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
221  rng);
222 
223  return DER_Encoder()
225  .encode(pbe_info.first)
226  .encode(pbe_info.second, OCTET_STRING)
227  .end_cons()
229  }
230 
231 /*
232 * PEM encode a PKCS #8 private key, encrypted
233 */
236  const std::string& pass,
237  size_t pbkdf_iterations,
238  const std::string& cipher,
239  const std::string& pbkdf_hash)
240  {
241  return PEM_Code::encode(
242  PKCS8::BER_encode_encrypted_pbkdf_iter(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash),
243  "ENCRYPTED PRIVATE KEY");
244  }
245 
246 /*
247 * BER encode a PKCS #8 private key, encrypted
248 */
249 std::vector<uint8_t> BER_encode_encrypted_pbkdf_msec(const Private_Key& key,
251  const std::string& pass,
252  std::chrono::milliseconds pbkdf_msec,
253  size_t* pbkdf_iterations,
254  const std::string& cipher,
255  const std::string& pbkdf_hash)
256  {
257  const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
259  pbkdf_msec, pbkdf_iterations,
260  cipher.empty() ? "AES-256/CBC" : cipher,
261  pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
262  rng);
263 
264  return DER_Encoder()
266  .encode(pbe_info.first)
267  .encode(pbe_info.second, OCTET_STRING)
268  .end_cons()
270  }
271 
272 /*
273 * PEM encode a PKCS #8 private key, encrypted
274 */
277  const std::string& pass,
278  std::chrono::milliseconds pbkdf_msec,
279  size_t* pbkdf_iterations,
280  const std::string& cipher,
281  const std::string& pbkdf_hash)
282  {
283  return PEM_Code::encode(
284  PKCS8::BER_encode_encrypted_pbkdf_msec(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash),
285  "ENCRYPTED PRIVATE KEY");
286  }
287 
288 namespace {
289 
290 /*
291 * Extract a private key (encrypted/unencrypted) and return it
292 */
293 std::unique_ptr<Private_Key>
294 load_key(DataSource& source,
295  std::function<std::string ()> get_pass,
296  bool is_encrypted)
297  {
298  AlgorithmIdentifier alg_id;
299  secure_vector<uint8_t> pkcs8_key = PKCS8_decode(source, get_pass, alg_id, is_encrypted);
300 
301  const std::string alg_name = OIDS::lookup(alg_id.get_oid());
302  if(alg_name.empty() || alg_name == alg_id.get_oid().as_string())
303  throw PKCS8_Exception("Unknown algorithm OID: " +
304  alg_id.get_oid().as_string());
305 
306  return load_private_key(alg_id, pkcs8_key);
307  }
308 
309 }
310 
311 /*
312 * Extract an encrypted private key and return it
313 */
314 std::unique_ptr<Private_Key> load_key(DataSource& source,
315  std::function<std::string ()> get_pass)
316  {
317  return load_key(source, get_pass, true);
318  }
319 
320 /*
321 * Extract an encrypted private key and return it
322 */
323 std::unique_ptr<Private_Key> load_key(DataSource& source,
324  const std::string& pass)
325  {
326  return load_key(source, [pass]() { return pass; }, true);
327  }
328 
329 /*
330 * Extract an unencrypted private key and return it
331 */
332 std::unique_ptr<Private_Key> load_key(DataSource& source)
333  {
334  auto fail_fn = []() -> std::string {
335  throw PKCS8_Exception("Internal error: Attempt to read password for unencrypted key");
336  };
337 
338  return load_key(source, fail_fn, false);
339  }
340 
341 /*
342 * Make a copy of this private key
343 */
344 std::unique_ptr<Private_Key> copy_key(const Private_Key& key)
345  {
346  DataSource_Memory source(PEM_encode(key));
347  return PKCS8::load_key(source);
348  }
349 
350 /*
351 * Extract an encrypted private key and return it
352 */
355  std::function<std::string ()> get_pass)
356  {
357  BOTAN_UNUSED(rng);
358  return PKCS8::load_key(source, get_pass).release();
359  }
360 
361 /*
362 * Extract an encrypted private key and return it
363 */
366  const std::string& pass)
367  {
368  BOTAN_UNUSED(rng);
369  return PKCS8::load_key(source, pass).release();
370  }
371 
372 /*
373 * Extract an unencrypted private key and return it
374 */
377  {
378  BOTAN_UNUSED(rng);
379  return PKCS8::load_key(source).release();
380  }
381 
382 #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
383 
384 /*
385 * Extract an encrypted private key and return it
386 */
387 Private_Key* load_key(const std::string& fsname,
389  std::function<std::string ()> get_pass)
390  {
391  BOTAN_UNUSED(rng);
392  DataSource_Stream in(fsname);
393  return PKCS8::load_key(in, get_pass).release();
394  }
395 
396 /*
397 * Extract an encrypted private key and return it
398 */
399 Private_Key* load_key(const std::string& fsname,
401  const std::string& pass)
402  {
403  BOTAN_UNUSED(rng);
404  DataSource_Stream in(fsname);
405  return PKCS8::load_key(in, [pass]() { return pass; }).release();
406  }
407 
408 /*
409 * Extract an unencrypted private key and return it
410 */
411 Private_Key* load_key(const std::string& fsname,
413  {
414  BOTAN_UNUSED(rng);
415  DataSource_Stream in(fsname);
416  return PKCS8::load_key(in).release();
417  }
418 #endif
419 
420 /*
421 * Make a copy of this private key
422 */
425  {
426  BOTAN_UNUSED(rng);
427  return PKCS8::copy_key(key).release();
428  }
429 
430 
431 
432 }
433 
434 }
std::unique_ptr< Private_Key > load_private_key(const AlgorithmIdentifier &alg_id, const secure_vector< uint8_t > &key_bits)
Definition: pk_algs.cpp:170
size_t arg_count() const
Definition: scan_name.h:49
std::unique_ptr< Private_Key > copy_key(const Private_Key &key)
Definition: pkcs8.cpp:344
std::vector< uint8_t > get_contents_unlocked()
Definition: der_enc.h:27
BER_Decoder & decode_and_check(const T &expected, const std::string &error_msg)
Definition: ber_dec.h:163
std::string as_string() const
Definition: asn1_oid.cpp:50
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:133
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:209
BER_Decoder & decode(bool &v)
Definition: ber_dec.cpp:355
bool maybe_BER(DataSource &source)
Definition: asn1_obj.cpp:116
DER_Encoder & end_cons()
Definition: der_enc.cpp:146
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:249
DER_Encoder & encode(bool b)
Definition: der_enc.cpp:202
secure_vector< uint8_t > private_key_info() const
Definition: pk_keys.cpp:62
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
std::string arg(size_t i) const
Definition: scan_name.cpp:122
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:152
std::string PEM_encode(const Private_Key &key)
Definition: pkcs8.cpp:140
const char * what() const BOTAN_NOEXCEPT override
Definition: exceptn.h:25
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:251
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:131
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:163
#define BOTAN_UNUSED(...)
Definition: assert.h:106
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:234
const std::vector< uint8_t > & get_parameters() const
Definition: alg_id.h:38
const std::string & algo_name() const
Definition: scan_name.h:44
const OID & get_oid() const
Definition: alg_id.h:37
BER_Decoder & verify_end()
Definition: ber_dec.cpp:176
DER_Encoder & start_cons(ASN1_Tag type_tag, ASN1_Tag class_tag=UNIVERSAL)
Definition: der_enc.cpp:136
std::vector< T, secure_allocator< T > > secure_vector
Definition: secmem.h:88
std::unique_ptr< Private_Key > load_key(DataSource &source, std::function< std::string()> get_pass)
Definition: pkcs8.cpp:314
std::string lookup(const OID &oid)
Definition: oids.cpp:18
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:275