Botan 3.0.0-alpha0
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/internal/scan_name.h>
16#include <botan/pk_algs.h>
17
18#if defined(BOTAN_HAS_PKCS5_PBES2)
19 #include <botan/internal/pbes2.h>
20#endif
21
22namespace Botan::PKCS8 {
23
24namespace {
25
26/*
27* Get info from an EncryptedPrivateKeyInfo
28*/
29secure_vector<uint8_t> PKCS8_extract(DataSource& source,
30 AlgorithmIdentifier& pbe_alg_id)
31 {
33
34 BER_Decoder(source)
36 .decode(pbe_alg_id)
38 .verify_end();
39
40 return key_data;
41 }
42
43/*
44* PEM decode and/or decrypt a private key
45*/
46secure_vector<uint8_t> PKCS8_decode(
47 DataSource& source,
48 const std::function<std::string ()>& get_passphrase,
49 AlgorithmIdentifier& pk_alg_id,
50 bool is_encrypted)
51 {
52 AlgorithmIdentifier pbe_alg_id;
53 secure_vector<uint8_t> key_data, key;
54
55 try {
56 if(ASN1::maybe_BER(source) && !PEM_Code::matches(source))
57 {
58 if(is_encrypted)
59 {
60 key_data = PKCS8_extract(source, pbe_alg_id);
61 }
62 else
63 {
64 // todo read more efficiently
65 while(!source.end_of_data())
66 {
67 uint8_t b;
68 size_t read = source.read_byte(b);
69 if(read)
70 {
71 key_data.push_back(b);
72 }
73 }
74 }
75 }
76 else
77 {
78 std::string label;
79 key_data = PEM_Code::decode(source, label);
80
81 // todo remove autodetect for pem as well?
82 if(label == "PRIVATE KEY")
83 is_encrypted = false;
84 else if(label == "ENCRYPTED PRIVATE KEY")
85 {
86 DataSource_Memory key_source(key_data);
87 key_data = PKCS8_extract(key_source, pbe_alg_id);
88 }
89 else
90 throw PKCS8_Exception("Unknown PEM label " + label);
91 }
92
93 if(key_data.empty())
94 throw PKCS8_Exception("No key data found");
95 }
96 catch(Decoding_Error& e)
97 {
98 throw Decoding_Error("PKCS #8 private key decoding", e);
99 }
100
101 try
102 {
103 if(is_encrypted)
104 {
105 if(OIDS::oid2str_or_throw(pbe_alg_id.get_oid()) != "PBE-PKCS5v20")
106 throw PKCS8_Exception("Unknown PBE type " + pbe_alg_id.get_oid().to_string());
107#if defined(BOTAN_HAS_PKCS5_PBES2)
108 key = pbes2_decrypt(key_data, get_passphrase(), pbe_alg_id.get_parameters());
109#else
110 BOTAN_UNUSED(get_passphrase);
111 throw Decoding_Error("Private key is encrypted but PBES2 was disabled in build");
112#endif
113 }
114 else
115 key = key_data;
116
117 BER_Decoder(key)
119 .decode_and_check<size_t>(0, "Unknown PKCS #8 version number")
120 .decode(pk_alg_id)
121 .decode(key, ASN1_Type::OctetString)
122 .discard_remaining()
123 .end_cons();
124 }
125 catch(std::exception& e)
126 {
127 throw Decoding_Error("PKCS #8 private key decoding", e);
128 }
129 return key;
130 }
131
132}
133
134/*
135* PEM encode a PKCS #8 private key, unencrypted
136*/
137std::string PEM_encode(const Private_Key& key)
138 {
139 return PEM_Code::encode(key.private_key_info(), "PRIVATE KEY");
140 }
141
142#if defined(BOTAN_HAS_PKCS5_PBES2)
143
144namespace {
145
146std::pair<std::string, std::string>
147choose_pbe_params(const std::string& pbe_algo, const std::string& key_algo)
148 {
149 if(pbe_algo.empty())
150 {
151 /*
152 * For algorithms where we are using a non-RFC format anyway, default to
153 * SIV or GCM. For others (RSA, ECDSA, ...) default to something widely
154 * compatible.
155 */
156 const bool nonstandard_pk = (key_algo == "McEliece" || key_algo == "XMSS");
157
158 if(nonstandard_pk)
159 {
160#if defined(BOTAN_HAS_AEAD_SIV) && defined(BOTAN_HAS_SHA2_64)
161 return std::make_pair("AES-256/SIV", "SHA-512");
162#elif defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64)
163 return std::make_pair("AES-256/GCM", "SHA-512");
164#endif
165 }
166
167 // Default is something compatible with everyone else
168 return std::make_pair("AES-256/CBC", "SHA-256");
169 }
170
171 SCAN_Name request(pbe_algo);
172
173 if(request.arg_count() != 2 ||
174 (request.algo_name() != "PBE-PKCS5v20" && request.algo_name() != "PBES2"))
175 {
176 throw Invalid_Argument("Unsupported PBE " + pbe_algo);
177 }
178
179 return std::make_pair(request.arg(0), request.arg(1));
180 }
181
182}
183
184#endif
185
186/*
187* BER encode a PKCS #8 private key, encrypted
188*/
189std::vector<uint8_t> BER_encode(const Private_Key& key,
191 const std::string& pass,
192 std::chrono::milliseconds msec,
193 const std::string& pbe_algo)
194 {
195#if defined(BOTAN_HAS_PKCS5_PBES2)
196 const auto pbe_params = choose_pbe_params(pbe_algo, key.algo_name());
197
198 const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
199 pbes2_encrypt_msec(PKCS8::BER_encode(key), pass, msec, nullptr,
200 pbe_params.first, pbe_params.second, rng);
201
202 std::vector<uint8_t> output;
203 DER_Encoder der(output);
204 der.start_sequence()
205 .encode(pbe_info.first)
206 .encode(pbe_info.second, ASN1_Type::OctetString)
207 .end_cons();
208
209 return output;
210#else
211 BOTAN_UNUSED(key, rng, pass, msec, pbe_algo);
212 throw Encoding_Error("PKCS8::BER_encode cannot encrypt because PBES2 was disabled in build");
213#endif
214 }
215
216/*
217* PEM encode a PKCS #8 private key, encrypted
218*/
219std::string PEM_encode(const Private_Key& key,
221 const std::string& pass,
222 std::chrono::milliseconds msec,
223 const std::string& pbe_algo)
224 {
225 if(pass.empty())
226 return PEM_encode(key);
227
228 return PEM_Code::encode(PKCS8::BER_encode(key, rng, pass, msec, pbe_algo),
229 "ENCRYPTED PRIVATE KEY");
230 }
231
232/*
233* BER encode a PKCS #8 private key, encrypted
234*/
235std::vector<uint8_t> BER_encode_encrypted_pbkdf_iter(const Private_Key& key,
237 const std::string& pass,
238 size_t pbkdf_iterations,
239 const std::string& cipher,
240 const std::string& pbkdf_hash)
241 {
242#if defined(BOTAN_HAS_PKCS5_PBES2)
243 const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
245 pass, pbkdf_iterations,
246 cipher.empty() ? "AES-256/CBC" : cipher,
247 pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
248 rng);
249
250 std::vector<uint8_t> output;
251 DER_Encoder der(output);
252 der.start_sequence()
253 .encode(pbe_info.first)
254 .encode(pbe_info.second, ASN1_Type::OctetString)
255 .end_cons();
256
257 return output;
258
259#else
260 BOTAN_UNUSED(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash);
261 throw Encoding_Error("PKCS8::BER_encode_encrypted_pbkdf_iter cannot encrypt because PBES2 disabled in build");
262#endif
263 }
264
265/*
266* PEM encode a PKCS #8 private key, encrypted
267*/
270 const std::string& pass,
271 size_t pbkdf_iterations,
272 const std::string& cipher,
273 const std::string& pbkdf_hash)
274 {
275 return PEM_Code::encode(
276 PKCS8::BER_encode_encrypted_pbkdf_iter(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash),
277 "ENCRYPTED PRIVATE KEY");
278 }
279
280/*
281* BER encode a PKCS #8 private key, encrypted
282*/
283std::vector<uint8_t> BER_encode_encrypted_pbkdf_msec(const Private_Key& key,
285 const std::string& pass,
286 std::chrono::milliseconds pbkdf_msec,
287 size_t* pbkdf_iterations,
288 const std::string& cipher,
289 const std::string& pbkdf_hash)
290 {
291#if defined(BOTAN_HAS_PKCS5_PBES2)
292 const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
294 pbkdf_msec, pbkdf_iterations,
295 cipher.empty() ? "AES-256/CBC" : cipher,
296 pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
297 rng);
298
299 std::vector<uint8_t> output;
300 DER_Encoder(output)
302 .encode(pbe_info.first)
303 .encode(pbe_info.second, ASN1_Type::OctetString)
304 .end_cons();
305
306 return output;
307#else
308 BOTAN_UNUSED(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash);
309 throw Encoding_Error("BER_encode_encrypted_pbkdf_msec cannot encrypt because PBES2 disabled in build");
310#endif
311 }
312
313/*
314* PEM encode a PKCS #8 private key, encrypted
315*/
318 const std::string& pass,
319 std::chrono::milliseconds pbkdf_msec,
320 size_t* pbkdf_iterations,
321 const std::string& cipher,
322 const std::string& pbkdf_hash)
323 {
324 return PEM_Code::encode(
325 PKCS8::BER_encode_encrypted_pbkdf_msec(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash),
326 "ENCRYPTED PRIVATE KEY");
327 }
328
329namespace {
330
331/*
332* Extract a private key (encrypted/unencrypted) and return it
333*/
334std::unique_ptr<Private_Key>
335load_key(DataSource& source,
336 const std::function<std::string ()>& get_pass,
337 bool is_encrypted)
338 {
339 AlgorithmIdentifier alg_id;
340 secure_vector<uint8_t> pkcs8_key = PKCS8_decode(source, get_pass, alg_id, is_encrypted);
341
342 const std::string alg_name = OIDS::oid2str_or_empty(alg_id.get_oid());
343 if(alg_name.empty())
344 throw PKCS8_Exception("Unknown algorithm OID: " +
345 alg_id.get_oid().to_string());
346
347 return load_private_key(alg_id, pkcs8_key);
348 }
349
350}
351
352/*
353* Extract an encrypted private key and return it
354*/
355std::unique_ptr<Private_Key> load_key(DataSource& source,
356 const std::function<std::string ()>& get_pass)
357 {
358 return load_key(source, get_pass, true);
359 }
360
361/*
362* Extract an encrypted private key and return it
363*/
364std::unique_ptr<Private_Key> load_key(DataSource& source,
365 const std::string& pass)
366 {
367 // We need to use bind rather than a lambda capturing `pass` here in order to avoid a Clang 8 bug.
368 // See https://github.com/randombit/botan/issues/2255.
369 return load_key(source, std::bind([](const std::string& p) { return p; }, pass), true);
370 }
371
372/*
373* Extract an unencrypted private key and return it
374*/
375std::unique_ptr<Private_Key> load_key(DataSource& source)
376 {
377 auto fail_fn = []() -> std::string {
378 throw PKCS8_Exception("Internal error: Attempt to read password for unencrypted key");
379 };
380
381 return load_key(source, fail_fn, false);
382 }
383
384}
#define BOTAN_UNUSED(...)
Definition: assert.h:141
const std::vector< uint8_t > & get_parameters() const
Definition: asn1_obj.h:448
const OID & get_oid() const
Definition: asn1_obj.h:447
BER_Decoder & decode(bool &out)
Definition: ber_dec.h:187
BER_Decoder & decode_and_check(const T &expected, const std::string &error_msg)
Definition: ber_dec.h:294
BER_Decoder & verify_end()
Definition: ber_dec.cpp:210
BER_Decoder start_sequence()
Definition: ber_dec.h:111
DER_Encoder & start_sequence()
Definition: der_enc.h:66
DER_Encoder & end_cons()
Definition: der_enc.cpp:194
DER_Encoder & encode(bool b)
Definition: der_enc.cpp:288
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:97
secure_vector< uint8_t > private_key_info() const
Definition: pk_keys.cpp:61
virtual std::string algo_name() const =0
PolynomialVector b
Definition: kyber.cpp:821
bool maybe_BER(DataSource &source)
Definition: asn1_obj.cpp:218
BOTAN_UNSTABLE_API std::string oid2str_or_throw(const OID &oid)
Definition: oids.cpp:115
BOTAN_UNSTABLE_API std::string oid2str_or_empty(const OID &oid)
Definition: oids.cpp:105
std::string encode(const uint8_t der[], size_t length, const std::string &label, size_t width)
Definition: pem.cpp:41
bool matches(DataSource &source, const std::string &extra, size_t search_range)
Definition: pem.cpp:140
secure_vector< uint8_t > decode(DataSource &source, std::string &label)
Definition: pem.cpp:66
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:235
std::string PEM_encode(const Private_Key &key)
Definition: pkcs8.cpp:137
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:316
std::vector< uint8_t > BER_encode(const Private_Key &key, RandomNumberGenerator &rng, const std::string &pass, std::chrono::milliseconds msec, const std::string &pbe_algo)
Definition: pkcs8.cpp:189
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:283
std::unique_ptr< Private_Key > load_key(DataSource &source, const std::function< std::string()> &get_pass)
Definition: pkcs8.cpp:355
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:268
std::unique_ptr< Private_Key > load_private_key(const AlgorithmIdentifier &alg_id, const secure_vector< uint8_t > &key_bits)
Definition: pk_algs.cpp:159
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:271
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:290
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:301