ndn-cxx: NDN C++ Library 0.9.0-33-g832ea91d
Loading...
Searching...
No Matches
private-key.cpp
Go to the documentation of this file.
1/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * Copyright (c) 2013-2023 Regents of the University of California.
4 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6 *
7 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
10 *
11 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License and GNU Lesser
16 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17 * <http://www.gnu.org/licenses/>.
18 *
19 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20 */
21
29#include "ndn-cxx/security/impl/openssl-helper.hpp"
33#if OPENSSL_VERSION_NUMBER < 0x30000000L
35#endif
36
37#include <boost/lexical_cast.hpp>
38#include <cstring>
39
40#include <openssl/ec.h>
41#include <openssl/pem.h>
42#include <openssl/rsa.h>
43#include <openssl/x509.h>
44
45#define ENSURE_PRIVATE_KEY_LOADED(key) \
46 do { \
47 if ((key) == nullptr) \
48 NDN_THROW(Error("Private key has not been loaded yet")); \
49 } while (false)
50
51#define ENSURE_PRIVATE_KEY_NOT_LOADED(key) \
52 do { \
53 if ((key) != nullptr) \
54 NDN_THROW(Error("Private key has already been loaded")); \
55 } while (false)
56
58
59class PrivateKey::Impl : noncopyable
60{
61public:
62 ~Impl()
63 {
64 EVP_PKEY_free(key);
65 }
66
67public:
68 EVP_PKEY* key = nullptr;
69};
70
72 : m_impl(make_unique<Impl>())
73{
74}
75
76PrivateKey::~PrivateKey() = default;
77
80{
81 if (!m_impl->key)
82 return KeyType::NONE;
83
84 switch (detail::getEvpPkeyType(m_impl->key)) {
85 case EVP_PKEY_RSA:
86 return KeyType::RSA;
87 case EVP_PKEY_EC:
88 return KeyType::EC;
89 case EVP_PKEY_HMAC:
90 return KeyType::HMAC;
91 default:
92 return KeyType::NONE;
93 }
94}
95
96size_t
98{
99 switch (getKeyType()) {
100 case KeyType::RSA:
101 case KeyType::EC:
102 return static_cast<size_t>(EVP_PKEY_bits(m_impl->key));
103 case KeyType::HMAC: {
104 size_t nBytes = 0;
105 EVP_PKEY_get_raw_private_key(m_impl->key, nullptr, &nBytes);
106 return nBytes * 8;
107 }
108 default:
109 return 0;
110 }
111}
112
115{
116 if (getKeyType() != KeyType::HMAC)
117 NDN_THROW(Error("Digest is not supported for key type " +
118 boost::lexical_cast<std::string>(getKeyType())));
119
120 size_t len = 0;
121 if (EVP_PKEY_get_raw_private_key(m_impl->key, nullptr, &len) != 1)
122 NDN_THROW(Error("Failed to get raw key length"));
123 Buffer digest(len);
124 if (EVP_PKEY_get_raw_private_key(m_impl->key, digest.data(), &len) != 1)
125 NDN_THROW(Error("Failed to get raw key"));
126
127 OBufferStream os;
128 bufferSource(digest) >> digestFilter(algo) >> streamSink(os);
129 return os.buf();
130}
131
132void
133PrivateKey::loadRaw(KeyType type, span<const uint8_t> buf)
134{
136
137 int pkeyType;
138 switch (type) {
139 case KeyType::HMAC:
140 pkeyType = EVP_PKEY_HMAC;
141 break;
142 default:
143 NDN_THROW(std::invalid_argument("Unsupported key type " + boost::lexical_cast<std::string>(type)));
144 }
145
146 m_impl->key = EVP_PKEY_new_raw_private_key(pkeyType, nullptr, buf.data(), buf.size());
147 if (m_impl->key == nullptr)
148 NDN_THROW(Error("Failed to load private key"));
149}
150
151void
152PrivateKey::loadPkcs1(span<const uint8_t> buf)
153{
155
156 auto ptr = buf.data();
157 if (d2i_AutoPrivateKey(&m_impl->key, &ptr, static_cast<long>(buf.size())) == nullptr)
158 NDN_THROW(Error("Failed to load private key"));
159}
160
161void
162PrivateKey::loadPkcs1(std::istream& is)
163{
164 OBufferStream os;
165 streamSource(is) >> streamSink(os);
166 loadPkcs1(*os.buf());
167}
168
169void
170PrivateKey::loadPkcs1Base64(span<const uint8_t> buf)
171{
172 OBufferStream os;
173 bufferSource(buf) >> base64Decode() >> streamSink(os);
174 loadPkcs1(*os.buf());
175}
176
177void
179{
180 OBufferStream os;
181 streamSource(is) >> base64Decode() >> streamSink(os);
182 loadPkcs1(*os.buf());
183}
184
185void
186PrivateKey::loadPkcs8(span<const uint8_t> buf, const char* pw, size_t pwLen)
187{
188 BOOST_ASSERT(std::strlen(pw) == pwLen);
190
191 detail::Bio membio(BIO_s_mem());
192 if (!membio.write(buf))
193 NDN_THROW(Error("Failed to copy buffer"));
194
195 if (d2i_PKCS8PrivateKey_bio(membio, &m_impl->key, nullptr, const_cast<char*>(pw)) == nullptr)
196 NDN_THROW(Error("Failed to load private key"));
197}
198
199static inline int
200passwordCallbackWrapper(char* buf, int size, int rwflag, void* u)
201{
202 BOOST_ASSERT(size >= 0);
203 auto cb = reinterpret_cast<PrivateKey::PasswordCallback*>(u);
204 return (*cb)(buf, static_cast<size_t>(size), rwflag);
205}
206
207void
208PrivateKey::loadPkcs8(span<const uint8_t> buf, PasswordCallback pwCallback)
209{
211
212 detail::Bio membio(BIO_s_mem());
213 if (!membio.write(buf))
214 NDN_THROW(Error("Failed to copy buffer"));
215
216 if (pwCallback)
217 m_impl->key = d2i_PKCS8PrivateKey_bio(membio, nullptr, &passwordCallbackWrapper, &pwCallback);
218 else
219 m_impl->key = d2i_PKCS8PrivateKey_bio(membio, nullptr, nullptr, nullptr);
220
221 if (m_impl->key == nullptr)
222 NDN_THROW(Error("Failed to load private key"));
223}
224
225void
226PrivateKey::loadPkcs8(std::istream& is, const char* pw, size_t pwLen)
227{
228 OBufferStream os;
229 streamSource(is) >> streamSink(os);
230 loadPkcs8(*os.buf(), pw, pwLen);
231}
232
233void
234PrivateKey::loadPkcs8(std::istream& is, PasswordCallback pwCallback)
235{
236 OBufferStream os;
237 streamSource(is) >> streamSink(os);
238 loadPkcs8(*os.buf(), std::move(pwCallback));
239}
240
241void
242PrivateKey::loadPkcs8Base64(span<const uint8_t> buf, const char* pw, size_t pwLen)
243{
244 OBufferStream os;
245 bufferSource(buf) >> base64Decode() >> streamSink(os);
246 loadPkcs8(*os.buf(), pw, pwLen);
247}
248
249void
250PrivateKey::loadPkcs8Base64(span<const uint8_t> buf, PasswordCallback pwCallback)
251{
252 OBufferStream os;
253 bufferSource(buf) >> base64Decode() >> streamSink(os);
254 loadPkcs8(*os.buf(), std::move(pwCallback));
255}
256
257void
258PrivateKey::loadPkcs8Base64(std::istream& is, const char* pw, size_t pwLen)
259{
260 OBufferStream os;
261 streamSource(is) >> base64Decode() >> streamSink(os);
262 loadPkcs8(*os.buf(), pw, pwLen);
263}
264
265void
267{
268 OBufferStream os;
269 streamSource(is) >> base64Decode() >> streamSink(os);
270 loadPkcs8(*os.buf(), std::move(pwCallback));
271}
272
273void
274PrivateKey::savePkcs1(std::ostream& os) const
275{
276 bufferSource(*toPkcs1()) >> streamSink(os);
277}
278
279void
280PrivateKey::savePkcs1Base64(std::ostream& os) const
281{
282 bufferSource(*toPkcs1()) >> base64Encode() >> streamSink(os);
283}
284
285void
286PrivateKey::savePkcs8(std::ostream& os, const char* pw, size_t pwLen) const
287{
288 bufferSource(*toPkcs8(pw, pwLen)) >> streamSink(os);
289}
290
291void
292PrivateKey::savePkcs8(std::ostream& os, PasswordCallback pwCallback) const
293{
294 bufferSource(*toPkcs8(std::move(pwCallback))) >> streamSink(os);
295}
296
297void
298PrivateKey::savePkcs8Base64(std::ostream& os, const char* pw, size_t pwLen) const
299{
300 bufferSource(*toPkcs8(pw, pwLen)) >> base64Encode() >> streamSink(os);
301}
302
303void
304PrivateKey::savePkcs8Base64(std::ostream& os, PasswordCallback pwCallback) const
305{
306 bufferSource(*toPkcs8(std::move(pwCallback))) >> base64Encode() >> streamSink(os);
307}
308
311{
312 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
313
314 uint8_t* pkcs8 = nullptr;
315 int len = i2d_PUBKEY(m_impl->key, &pkcs8);
316 if (len < 0)
317 NDN_THROW(Error("Failed to derive public key"));
318
319 auto result = make_shared<Buffer>(pkcs8, len);
320 OPENSSL_free(pkcs8);
321
322 return result;
323}
324
326PrivateKey::decrypt(span<const uint8_t> cipherText) const
327{
328 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
329
330 int keyType = detail::getEvpPkeyType(m_impl->key);
331 switch (keyType) {
332 case EVP_PKEY_NONE:
333 NDN_THROW(Error("Failed to determine key type"));
334 case EVP_PKEY_RSA:
335 return rsaDecrypt(cipherText);
336 default:
337 NDN_THROW(Error("Decryption is not supported for key type " + to_string(keyType)));
338 }
339}
340
341void*
342PrivateKey::getEvpPkey() const
343{
344 return m_impl->key;
345}
346
348PrivateKey::toPkcs1() const
349{
350 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
351
352 detail::Bio membio(BIO_s_mem());
353 if (!i2d_PrivateKey_bio(membio, m_impl->key))
354 NDN_THROW(Error("Cannot convert key to PKCS #1 format"));
355
356 auto buffer = make_shared<Buffer>(BIO_pending(membio));
357 if (!membio.read(*buffer))
358 NDN_THROW(Error("Read error during PKCS #1 conversion"));
359
360 return buffer;
361}
362
364PrivateKey::toPkcs8(const char* pw, size_t pwLen) const
365{
366 BOOST_ASSERT(std::strlen(pw) == pwLen);
367 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
368
369 detail::Bio membio(BIO_s_mem());
370 if (!i2d_PKCS8PrivateKey_bio(membio, m_impl->key, EVP_aes_256_cbc(), nullptr, 0,
371 nullptr, const_cast<char*>(pw)))
372 NDN_THROW(Error("Cannot convert key to PKCS #8 format"));
373
374 auto buffer = make_shared<Buffer>(BIO_pending(membio));
375 if (!membio.read(*buffer))
376 NDN_THROW(Error("Read error during PKCS #8 conversion"));
377
378 return buffer;
379}
380
382PrivateKey::toPkcs8(PasswordCallback pwCallback) const
383{
384 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
385
386 detail::Bio membio(BIO_s_mem());
387 if (!i2d_PKCS8PrivateKey_bio(membio, m_impl->key, EVP_aes_256_cbc(), nullptr, 0,
388 &passwordCallbackWrapper, &pwCallback))
389 NDN_THROW(Error("Cannot convert key to PKCS #8 format"));
390
391 auto buffer = make_shared<Buffer>(BIO_pending(membio));
392 if (!membio.read(*buffer))
393 NDN_THROW(Error("Read error during PKCS #8 conversion"));
394
395 return buffer;
396}
397
399PrivateKey::rsaDecrypt(span<const uint8_t> cipherText) const
400{
401 detail::EvpPkeyCtx ctx(m_impl->key);
402
403 if (EVP_PKEY_decrypt_init(ctx) <= 0)
404 NDN_THROW(Error("Failed to initialize decryption context"));
405
406 if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
407 NDN_THROW(Error("Failed to set padding"));
408
409 size_t outlen = 0;
410 // Determine buffer length
411 if (EVP_PKEY_decrypt(ctx, nullptr, &outlen, cipherText.data(), cipherText.size()) <= 0)
412 NDN_THROW(Error("Failed to estimate output length"));
413
414 auto out = make_shared<Buffer>(outlen);
415 if (EVP_PKEY_decrypt(ctx, out->data(), &outlen, cipherText.data(), cipherText.size()) <= 0)
416 NDN_THROW(Error("Failed to decrypt ciphertext"));
417
418 out->resize(outlen);
419 return out;
420}
421
422unique_ptr<PrivateKey>
423PrivateKey::generateRsaKey(uint32_t keySize)
424{
425 auto privateKey = make_unique<PrivateKey>();
426 BOOST_ASSERT(privateKey->m_impl->key == nullptr);
427
428#if OPENSSL_VERSION_NUMBER >= 0x30000000L
429 privateKey->m_impl->key = EVP_RSA_gen(keySize);
430
431 if (privateKey->m_impl->key == nullptr)
432 NDN_THROW(Error("Failed to generate RSA key"));
433#else // OPENSSL_VERSION_NUMBER
434 detail::EvpPkeyCtx kctx(EVP_PKEY_RSA);
435
436 if (EVP_PKEY_keygen_init(kctx) <= 0)
437 NDN_THROW(Error("Failed to initialize RSA keygen context"));
438
439 if (EVP_PKEY_CTX_set_rsa_keygen_bits(kctx, static_cast<int>(keySize)) <= 0)
440 NDN_THROW(Error("Failed to set RSA key length"));
441
442 if (EVP_PKEY_keygen(kctx, &privateKey->m_impl->key) <= 0)
443 NDN_THROW(Error("Failed to generate RSA key"));
444#endif // OPENSSL_VERSION_NUMBER
445
446 return privateKey;
447}
448
449unique_ptr<PrivateKey>
450PrivateKey::generateEcKey(uint32_t keySize)
451{
452 auto privateKey = make_unique<PrivateKey>();
453 BOOST_ASSERT(privateKey->m_impl->key == nullptr);
454
455#if OPENSSL_VERSION_NUMBER >= 0x30000000L
456 switch (keySize) {
457 case 224:
458 case 256:
459 case 384:
460 case 521:
461 privateKey->m_impl->key = EVP_EC_gen(("P-" + to_string(keySize)).data());
462 break;
463 default:
464 NDN_THROW(std::invalid_argument("Unsupported EC key length " + to_string(keySize)));
465 }
466
467 if (privateKey->m_impl->key == nullptr)
468 NDN_THROW(Error("Failed to generate EC key"));
469#else // OPENSSL_VERSION_NUMBER
470 EC_KEY* eckey = nullptr;
471 switch (keySize) {
472 case 224:
473 eckey = EC_KEY_new_by_curve_name(NID_secp224r1);
474 break;
475 case 256:
476 eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); // same as secp256r1
477 break;
478 case 384:
479 eckey = EC_KEY_new_by_curve_name(NID_secp384r1);
480 break;
481 case 521:
482 eckey = EC_KEY_new_by_curve_name(NID_secp521r1);
483 break;
484 default:
485 NDN_THROW(std::invalid_argument("Unsupported EC key length " + to_string(keySize)));
486 }
487 if (eckey == nullptr) {
488 NDN_THROW(Error("Failed to set EC curve"));
489 }
490
491 auto guard = make_scope_exit([eckey] { EC_KEY_free(eckey); });
492 if (EC_KEY_generate_key(eckey) != 1) {
493 NDN_THROW(Error("Failed to generate EC key"));
494 }
495
496 privateKey->m_impl->key = EVP_PKEY_new();
497 if (privateKey->m_impl->key == nullptr)
498 NDN_THROW(Error("Failed to create EVP_PKEY"));
499 if (EVP_PKEY_set1_EC_KEY(privateKey->m_impl->key, eckey) != 1)
500 NDN_THROW(Error("Failed to assign EC key"));
501#endif // OPENSSL_VERSION_NUMBER
502
503 return privateKey;
504}
505
506unique_ptr<PrivateKey>
507PrivateKey::generateHmacKey(uint32_t keySize)
508{
509 std::vector<uint8_t> rawKey(keySize / 8);
511
512 auto privateKey = make_unique<PrivateKey>();
513 try {
514 privateKey->loadRaw(KeyType::HMAC, rawKey);
515 }
516 catch (const PrivateKey::Error&) {
517 NDN_THROW(PrivateKey::Error("Failed to generate HMAC key"));
518 }
519
520 return privateKey;
521}
522
523unique_ptr<PrivateKey>
525{
526 switch (keyParams.getKeyType()) {
527 case KeyType::RSA: {
528 const auto& rsaParams = static_cast<const RsaKeyParams&>(keyParams);
529 return PrivateKey::generateRsaKey(rsaParams.getKeySize());
530 }
531 case KeyType::EC: {
532 const auto& ecParams = static_cast<const EcKeyParams&>(keyParams);
533 return PrivateKey::generateEcKey(ecParams.getKeySize());
534 }
535 case KeyType::HMAC: {
536 const auto& hmacParams = static_cast<const HmacKeyParams&>(keyParams);
537 return PrivateKey::generateHmacKey(hmacParams.getKeySize());
538 }
539 default:
540 NDN_THROW(std::invalid_argument("Unsupported key type " +
541 boost::lexical_cast<std::string>(keyParams.getKeyType())));
542 }
543}
544
545} // namespace ndn::security::transform
General-purpose automatically managed/resized buffer.
Definition buffer.hpp:43
Base class for key parameters.
KeyType getKeyType() const
An output stream that writes to a Buffer.
std::shared_ptr< Buffer > buf()
Return a shared pointer to the underlying buffer.
SimplePublicKeyParams is a template for public keys with only one parameter: size.
SimpleSymmetricKeyParams is a template for symmetric keys with only one parameter: size.
void savePkcs8(std::ostream &os, const char *pw, size_t pwLen) const
Save the private key in encrypted PKCS#8 format into a stream os.
void savePkcs1(std::ostream &os) const
Save the private key in PKCS#1 format into a stream os.
std::function< int(char *buf, size_t bufSize, bool shouldConfirm)> PasswordCallback
Callback for application to handle password input.
ConstBufferPtr getKeyDigest(DigestAlgorithm algo) const
Returns a digest of the private key.
void loadRaw(KeyType type, span< const uint8_t > buf)
Load a raw private key from a buffer buf.
void loadPkcs8Base64(span< const uint8_t > buf, const char *pw, size_t pwLen)
Load the private key in base64-encoded encrypted PKCS#8 format from a buffer buf with passphrase pw.
void loadPkcs1Base64(span< const uint8_t > buf)
Load the private key in base64-encoded PKCS#1 format from a buffer buf.
void savePkcs1Base64(std::ostream &os) const
Save the private key in base64-encoded PKCS#1 format into a stream os.
void loadPkcs1(span< const uint8_t > buf)
Load the private key in PKCS#1 format from a buffer buf.
ConstBufferPtr derivePublicKey() const
void loadPkcs8(span< const uint8_t > buf, const char *pw, size_t pwLen)
Load the private key in encrypted PKCS#8 format from a buffer buf with passphrase pw.
void savePkcs8Base64(std::ostream &os, const char *pw, size_t pwLen) const
Save the private key in base64-encoded encrypted PKCS#8 format into a stream os.
ConstBufferPtr decrypt(span< const uint8_t > cipherText) const
KeyType getKeyType() const
Returns the type of the private key.
PrivateKey()
Creates an empty private key instance.
size_t getKeySize() const
Returns the size of the private key in bits.
#define NDN_THROW(e)
Definition exception.hpp:56
std::string to_string(const errinfo_stacktrace &x)
Definition exception.cpp:30
void generateSecureBytes(span< uint8_t > buf)
Fill buffer with cryptographically secure random bytes.
Definition random.cpp:47
unique_ptr< Transform > digestFilter(DigestAlgorithm algo)
unique_ptr< Sink > streamSink(std::ostream &os)
unique_ptr< PrivateKey > generatePrivateKey(const KeyParams &keyParams)
Generate a private key according to keyParams.
unique_ptr< Transform > base64Encode(bool needBreak)
unique_ptr< Transform > base64Decode(bool expectNewlineEvery64Bytes)
KeyType
The type of a cryptographic key.
@ EC
Elliptic Curve key (e.g. for ECDSA), supports sign/verify operations.
@ RSA
RSA key, supports sign/verify and encrypt/decrypt operations.
@ NONE
Unknown or unsupported key type.
@ HMAC
HMAC key, supports sign/verify operations.
std::shared_ptr< const Buffer > ConstBufferPtr
Definition buffer.hpp:140
#define ENSURE_PRIVATE_KEY_LOADED(key)
#define ENSURE_PRIVATE_KEY_NOT_LOADED(key)