Botan 3.12.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 .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(DataSource& source,
47 const std::function<std::string()>& get_passphrase,
48 AlgorithmIdentifier& pk_alg_id,
49 bool is_encrypted) {
50 AlgorithmIdentifier pbe_alg_id;
53
54 try {
55 if(ASN1::maybe_BER(source) && !PEM_Code::matches(source)) {
56 if(is_encrypted) {
57 key_data = PKCS8_extract(source, pbe_alg_id);
58 } else {
59 // todo read more efficiently
60 while(auto b = source.read_byte()) {
61 key_data.push_back(*b);
62 }
63 }
64 } else {
65 std::string label;
66 key_data = PEM_Code::decode(source, label);
67
68 // todo remove autodetect for pem as well?
69 if(label == "PRIVATE KEY") {
70 is_encrypted = false;
71 } else if(label == "ENCRYPTED PRIVATE KEY") {
72 DataSource_Memory key_source(key_data);
73 key_data = PKCS8_extract(key_source, pbe_alg_id);
74 } else {
75 throw PKCS8_Exception(fmt("Unknown PEM label '{}'", label));
76 }
77 }
78
79 if(key_data.empty()) {
80 throw PKCS8_Exception("No key data found");
81 }
82 } catch(Decoding_Error& e) {
83 throw Decoding_Error("PKCS #8 private key decoding", e);
84 }
85
86 try {
87 if(is_encrypted) {
88 if(pbe_alg_id.oid().to_formatted_string() != "PBE-PKCS5v20") {
89 throw PKCS8_Exception(fmt("Unknown PBE type {}", pbe_alg_id.oid()));
90 }
91
92#if defined(BOTAN_HAS_PKCS5_PBES2)
93 key = pbes2_decrypt(key_data, get_passphrase(), pbe_alg_id.parameters());
94#else
95 BOTAN_UNUSED(get_passphrase);
96 throw Decoding_Error("Private key is encrypted but PBES2 was disabled in build");
97#endif
98 } else {
99 key = key_data;
100 }
101
104 .decode_and_check<size_t>(0, "Unknown PKCS #8 version number")
105 .decode(pk_alg_id)
106 .decode(key, ASN1_Type::OctetString)
107 .discard_remaining()
108 .end_cons()
109 .verify_end();
110 } catch(std::exception& e) {
111 throw Decoding_Error("PKCS #8 private key decoding", e);
112 }
113 return key;
114}
115
116} // namespace
117
118/*
119* PEM encode a PKCS #8 private key, unencrypted
120*/
121std::string PEM_encode(const Private_Key& key) {
122 return PEM_Code::encode(key.private_key_info(), "PRIVATE KEY");
123}
124
125#if defined(BOTAN_HAS_PKCS5_PBES2)
126
127namespace {
128
129std::pair<std::string, std::string> choose_pbe_params(std::string_view pbe_algo, std::string_view key_algo) {
130 if(pbe_algo.empty()) {
131 /*
132 * For algorithms where we are using a non-RFC format anyway, default to
133 * SIV or GCM. For others (RSA, ECDSA, ...) default to something widely
134 * compatible.
135 */
136 const bool nonstandard_pk = (key_algo == "McEliece" || key_algo == "XMSS");
137
138 if(nonstandard_pk) {
139 #if defined(BOTAN_HAS_AEAD_SIV) && defined(BOTAN_HAS_SHA2_64)
140 return std::make_pair("AES-256/SIV", "SHA-512");
141 #elif defined(BOTAN_HAS_AEAD_GCM) && defined(BOTAN_HAS_SHA2_64)
142 return std::make_pair("AES-256/GCM", "SHA-512");
143 #endif
144 }
145
146 // Default is something compatible with everyone else
147 return std::make_pair("AES-256/CBC", "SHA-256");
148 }
149
150 const SCAN_Name request(pbe_algo);
151
152 if(request.arg_count() != 2 || (request.algo_name() != "PBE-PKCS5v20" && request.algo_name() != "PBES2")) {
153 throw Invalid_Argument(fmt("Unsupported PBE '{}'", pbe_algo));
154 }
155
156 return std::make_pair(request.arg(0), request.arg(1));
157}
158
159} // namespace
160
161#endif
162
163/*
164* BER encode a PKCS #8 private key, encrypted
165*/
166std::vector<uint8_t> BER_encode(const Private_Key& key,
168 std::string_view pass,
169 std::chrono::milliseconds msec,
170 std::string_view pbe_algo) {
171#if defined(BOTAN_HAS_PKCS5_PBES2)
172 const auto pbe_params = choose_pbe_params(pbe_algo, key.algo_name());
173
174 const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
175 pbes2_encrypt_msec(PKCS8::BER_encode(key), pass, msec, nullptr, pbe_params.first, pbe_params.second, rng);
176
177 std::vector<uint8_t> output;
178 DER_Encoder der(output);
179 der.start_sequence().encode(pbe_info.first).encode(pbe_info.second, ASN1_Type::OctetString).end_cons();
180
181 return output;
182#else
183 BOTAN_UNUSED(key, rng, pass, msec, pbe_algo);
184 throw Encoding_Error("PKCS8::BER_encode cannot encrypt because PBES2 was disabled in build");
185#endif
186}
187
188/*
189* PEM encode a PKCS #8 private key, encrypted
190*/
191std::string PEM_encode(const Private_Key& key,
193 std::string_view pass,
194 std::chrono::milliseconds msec,
195 std::string_view pbe_algo) {
196 if(pass.empty()) {
197 return PEM_encode(key);
198 }
199
200 return PEM_Code::encode(PKCS8::BER_encode(key, rng, pass, msec, pbe_algo), "ENCRYPTED PRIVATE KEY");
201}
202
203/*
204* BER encode a PKCS #8 private key, encrypted
205*/
206std::vector<uint8_t> BER_encode_encrypted_pbkdf_iter(const Private_Key& key,
208 std::string_view pass,
209 size_t pbkdf_iterations,
210 std::string_view cipher,
211 std::string_view pbkdf_hash) {
212#if defined(BOTAN_HAS_PKCS5_PBES2)
213 const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
215 pass,
216 pbkdf_iterations,
217 cipher.empty() ? "AES-256/CBC" : cipher,
218 pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
219 rng);
220
221 std::vector<uint8_t> output;
222 DER_Encoder der(output);
223 der.start_sequence().encode(pbe_info.first).encode(pbe_info.second, ASN1_Type::OctetString).end_cons();
224
225 return output;
226
227#else
228 BOTAN_UNUSED(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash);
229 throw Encoding_Error("PKCS8::BER_encode_encrypted_pbkdf_iter cannot encrypt because PBES2 disabled in build");
230#endif
231}
232
233/*
234* PEM encode a PKCS #8 private key, encrypted
235*/
238 std::string_view pass,
239 size_t pbkdf_iterations,
240 std::string_view cipher,
241 std::string_view pbkdf_hash) {
242 return PEM_Code::encode(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*/
249std::vector<uint8_t> BER_encode_encrypted_pbkdf_msec(const Private_Key& key,
251 std::string_view pass,
252 std::chrono::milliseconds pbkdf_msec,
253 size_t* pbkdf_iterations,
254 std::string_view cipher,
255 std::string_view pbkdf_hash) {
256#if defined(BOTAN_HAS_PKCS5_PBES2)
257 const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
259 pass,
260 pbkdf_msec,
261 pbkdf_iterations,
262 cipher.empty() ? "AES-256/CBC" : cipher,
263 pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
264 rng);
265
266 std::vector<uint8_t> output;
267 DER_Encoder(output)
269 .encode(pbe_info.first)
270 .encode(pbe_info.second, ASN1_Type::OctetString)
271 .end_cons();
272
273 return output;
274#else
275 BOTAN_UNUSED(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash);
276 throw Encoding_Error("BER_encode_encrypted_pbkdf_msec cannot encrypt because PBES2 disabled in build");
277#endif
278}
279
280/*
281* PEM encode a PKCS #8 private key, encrypted
282*/
285 std::string_view pass,
286 std::chrono::milliseconds pbkdf_msec,
287 size_t* pbkdf_iterations,
288 std::string_view cipher,
289 std::string_view pbkdf_hash) {
290 return PEM_Code::encode(
291 PKCS8::BER_encode_encrypted_pbkdf_msec(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash),
292 "ENCRYPTED PRIVATE KEY");
293}
294
295namespace {
296
297/*
298* Extract a private key (encrypted/unencrypted) and return it
299*/
300std::unique_ptr<Private_Key> load_key(DataSource& source,
301 const std::function<std::string()>& get_pass,
302 bool is_encrypted) {
303 AlgorithmIdentifier alg_id;
304 secure_vector<uint8_t> pkcs8_key = PKCS8_decode(source, get_pass, alg_id, is_encrypted);
305
306 const std::string alg_name = alg_id.oid().human_name_or_empty();
307 if(alg_name.empty()) {
308 throw PKCS8_Exception(fmt("Unknown algorithm OID {}", alg_id.oid()));
309 }
310
311 return load_private_key(alg_id, pkcs8_key);
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:409
const OID & oid() const
Definition asn1_obj.h:407
virtual std::string algo_name() const =0
static Limits DER()
Definition ber_dec.h:35
BER_Decoder & decode(bool &out)
Definition ber_dec.h:220
BER_Decoder & verify_end()
Definition ber_dec.cpp:381
BER_Decoder start_sequence()
Definition ber_dec.h:160
BER_Decoder & decode_and_check(const T &expected, std::string_view error_msg)
Definition ber_dec.h:314
DER_Encoder & start_sequence()
Definition der_enc.h:67
DER_Encoder & end_cons()
Definition der_enc.cpp:173
DER_Encoder & encode(bool b)
Definition der_enc.cpp:245
size_t read_byte(uint8_t &out)
Definition data_src.cpp:27
std::string to_formatted_string() const
Definition asn1_oid.cpp:139
std::string human_name_or_empty() const
Definition asn1_oid.cpp:147
secure_vector< uint8_t > private_key_info() const
Definition pk_keys.cpp:75
bool maybe_BER(DataSource &source)
Definition asn1_obj.cpp:197
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:166
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:236
std::string PEM_encode(const Private_Key &key)
Definition pkcs8.cpp:121
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:283
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:206
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:249
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:282
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:291
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:264
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:68
std::unique_ptr< Private_Key > load_private_key(const AlgorithmIdentifier &alg_id, std::span< const uint8_t > key_bits)
Definition pk_algs.cpp:289