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