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