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