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-2019 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 
28 #include "ndn-cxx/security/impl/openssl-helper.hpp"
31 
32 #include <boost/lexical_cast.hpp>
33 #include <cstring>
34 
35 #define ENSURE_PRIVATE_KEY_LOADED(key) \
36  do { \
37  if ((key) == nullptr) \
38  NDN_THROW(Error("Private key has not been loaded yet")); \
39  } while (false)
40 
41 #define ENSURE_PRIVATE_KEY_NOT_LOADED(key) \
42  do { \
43  if ((key) != nullptr) \
44  NDN_THROW(Error("Private key has already been loaded")); \
45  } while (false)
46 
47 namespace ndn {
48 namespace security {
49 namespace transform {
50 
51 static void
53 {
54 #if OPENSSL_VERSION_NUMBER < 0x1010000fL
55  static bool isInitialized = false;
56  if (!isInitialized) {
57  OpenSSL_add_all_algorithms();
58  isInitialized = true;
59  }
60 #endif // OPENSSL_VERSION_NUMBER < 0x1010000fL
61 }
62 
63 class PrivateKey::Impl
64 {
65 public:
66  Impl() noexcept
67  : key(nullptr)
68  {
69  }
70 
71  ~Impl()
72  {
73  EVP_PKEY_free(key);
74  }
75 
76 public:
77  EVP_PKEY* key;
78 };
79 
81  : m_impl(make_unique<Impl>())
82 {
83 }
84 
85 PrivateKey::~PrivateKey() = default;
86 
87 KeyType
89 {
90  if (!m_impl->key)
91  return KeyType::NONE;
92 
93  switch (detail::getEvpPkeyType(m_impl->key)) {
94  case EVP_PKEY_RSA:
95  return KeyType::RSA;
96  case EVP_PKEY_EC:
97  return KeyType::EC;
98  default:
99  return KeyType::NONE;
100  }
101 }
102 
103 void
104 PrivateKey::loadPkcs1(const uint8_t* buf, size_t size)
105 {
106  ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
108 
109  if (d2i_AutoPrivateKey(&m_impl->key, &buf, static_cast<long>(size)) == nullptr)
110  NDN_THROW(Error("Failed to load private key"));
111 }
112 
113 void
114 PrivateKey::loadPkcs1(std::istream& is)
115 {
116  OBufferStream os;
117  streamSource(is) >> streamSink(os);
118  this->loadPkcs1(os.buf()->data(), os.buf()->size());
119 }
120 
121 void
122 PrivateKey::loadPkcs1Base64(const uint8_t* buf, size_t size)
123 {
124  OBufferStream os;
125  bufferSource(buf, size) >> base64Decode() >> streamSink(os);
126  this->loadPkcs1(os.buf()->data(), os.buf()->size());
127 }
128 
129 void
131 {
132  OBufferStream os;
133  streamSource(is) >> base64Decode() >> streamSink(os);
134  this->loadPkcs1(os.buf()->data(), os.buf()->size());
135 }
136 
137 void
138 PrivateKey::loadPkcs8(const uint8_t* buf, size_t size, const char* pw, size_t pwLen)
139 {
140  BOOST_ASSERT(std::strlen(pw) == pwLen);
141  ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
143 
144  detail::Bio membio(BIO_s_mem());
145  if (!membio.write(buf, size))
146  NDN_THROW(Error("Failed to copy buffer"));
147 
148  if (d2i_PKCS8PrivateKey_bio(membio, &m_impl->key, nullptr, const_cast<char*>(pw)) == nullptr)
149  NDN_THROW(Error("Failed to load private key"));
150 }
151 
152 static inline int
153 passwordCallbackWrapper(char* buf, int size, int rwflag, void* u)
154 {
155  BOOST_ASSERT(size >= 0);
156  auto cb = reinterpret_cast<PrivateKey::PasswordCallback*>(u);
157  return (*cb)(buf, static_cast<size_t>(size), rwflag);
158 }
159 
160 void
161 PrivateKey::loadPkcs8(const uint8_t* buf, size_t size, PasswordCallback pwCallback)
162 {
163  ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
165 
166  detail::Bio membio(BIO_s_mem());
167  if (!membio.write(buf, size))
168  NDN_THROW(Error("Failed to copy buffer"));
169 
170  if (pwCallback)
171  m_impl->key = d2i_PKCS8PrivateKey_bio(membio, nullptr, &passwordCallbackWrapper, &pwCallback);
172  else
173  m_impl->key = d2i_PKCS8PrivateKey_bio(membio, nullptr, nullptr, nullptr);
174 
175  if (m_impl->key == nullptr)
176  NDN_THROW(Error("Failed to load private key"));
177 }
178 
179 void
180 PrivateKey::loadPkcs8(std::istream& is, const char* pw, size_t pwLen)
181 {
182  OBufferStream os;
183  streamSource(is) >> streamSink(os);
184  this->loadPkcs8(os.buf()->data(), os.buf()->size(), pw, pwLen);
185 }
186 
187 void
188 PrivateKey::loadPkcs8(std::istream& is, PasswordCallback pwCallback)
189 {
190  OBufferStream os;
191  streamSource(is) >> streamSink(os);
192  this->loadPkcs8(os.buf()->data(), os.buf()->size(), pwCallback);
193 }
194 
195 void
196 PrivateKey::loadPkcs8Base64(const uint8_t* buf, size_t size, const char* pw, size_t pwLen)
197 {
198  OBufferStream os;
199  bufferSource(buf, size) >> base64Decode() >> streamSink(os);
200  this->loadPkcs8(os.buf()->data(), os.buf()->size(), pw, pwLen);
201 }
202 
203 void
204 PrivateKey::loadPkcs8Base64(const uint8_t* buf, size_t size, PasswordCallback pwCallback)
205 {
206  OBufferStream os;
207  bufferSource(buf, size) >> base64Decode() >> streamSink(os);
208  this->loadPkcs8(os.buf()->data(), os.buf()->size(), pwCallback);
209 }
210 
211 void
212 PrivateKey::loadPkcs8Base64(std::istream& is, const char* pw, size_t pwLen)
213 {
214  OBufferStream os;
215  streamSource(is) >> base64Decode() >> streamSink(os);
216  this->loadPkcs8(os.buf()->data(), os.buf()->size(), pw, pwLen);
217 }
218 
219 void
220 PrivateKey::loadPkcs8Base64(std::istream& is, PasswordCallback pwCallback)
221 {
222  OBufferStream os;
223  streamSource(is) >> base64Decode() >> streamSink(os);
224  this->loadPkcs8(os.buf()->data(), os.buf()->size(), pwCallback);
225 }
226 
227 void
228 PrivateKey::savePkcs1(std::ostream& os) const
229 {
230  bufferSource(*this->toPkcs1()) >> streamSink(os);
231 }
232 
233 void
234 PrivateKey::savePkcs1Base64(std::ostream& os) const
235 {
236  bufferSource(*this->toPkcs1()) >> base64Encode() >> streamSink(os);
237 }
238 
239 void
240 PrivateKey::savePkcs8(std::ostream& os, const char* pw, size_t pwLen) const
241 {
242  bufferSource(*this->toPkcs8(pw, pwLen)) >> streamSink(os);
243 }
244 
245 void
246 PrivateKey::savePkcs8(std::ostream& os, PasswordCallback pwCallback) const
247 {
248  bufferSource(*this->toPkcs8(pwCallback)) >> streamSink(os);
249 }
250 
251 void
252 PrivateKey::savePkcs8Base64(std::ostream& os, const char* pw, size_t pwLen) const
253 {
254  bufferSource(*this->toPkcs8(pw, pwLen)) >> base64Encode() >> streamSink(os);
255 }
256 
257 void
258 PrivateKey::savePkcs8Base64(std::ostream& os, PasswordCallback pwCallback) const
259 {
260  bufferSource(*this->toPkcs8(pwCallback)) >> base64Encode() >> streamSink(os);
261 }
262 
265 {
266  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
267 
268  uint8_t* pkcs8 = nullptr;
269  int len = i2d_PUBKEY(m_impl->key, &pkcs8);
270  if (len < 0)
271  NDN_THROW(Error("Failed to derive public key"));
272 
273  auto result = make_shared<Buffer>(pkcs8, len);
274  OPENSSL_free(pkcs8);
275 
276  return result;
277 }
278 
280 PrivateKey::decrypt(const uint8_t* cipherText, size_t cipherLen) const
281 {
282  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
283 
284  int keyType = detail::getEvpPkeyType(m_impl->key);
285  switch (keyType) {
286  case EVP_PKEY_NONE:
287  NDN_THROW(Error("Failed to determine key type"));
288  case EVP_PKEY_RSA:
289  return rsaDecrypt(cipherText, cipherLen);
290  default:
291  NDN_THROW(Error("Decryption is not supported for key type " + to_string(keyType)));
292  }
293 }
294 
295 void*
296 PrivateKey::getEvpPkey() const
297 {
298  return m_impl->key;
299 }
300 
302 PrivateKey::toPkcs1() const
303 {
304  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
306 
307  detail::Bio membio(BIO_s_mem());
308  if (!i2d_PrivateKey_bio(membio, m_impl->key))
309  NDN_THROW(Error("Cannot convert key to PKCS #1 format"));
310 
311  auto buffer = make_shared<Buffer>(BIO_pending(membio));
312  membio.read(buffer->data(), buffer->size());
313 
314  return buffer;
315 }
316 
318 PrivateKey::toPkcs8(const char* pw, size_t pwLen) const
319 {
320  BOOST_ASSERT(std::strlen(pw) == pwLen);
321  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
323 
324  detail::Bio membio(BIO_s_mem());
325  if (!i2d_PKCS8PrivateKey_bio(membio, m_impl->key, EVP_aes_256_cbc(), nullptr, 0,
326  nullptr, const_cast<char*>(pw)))
327  NDN_THROW(Error("Cannot convert key to PKCS #8 format"));
328 
329  auto buffer = make_shared<Buffer>(BIO_pending(membio));
330  membio.read(buffer->data(), buffer->size());
331 
332  return buffer;
333 }
334 
336 PrivateKey::toPkcs8(PasswordCallback pwCallback) const
337 {
338  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
340 
341  detail::Bio membio(BIO_s_mem());
342  if (!i2d_PKCS8PrivateKey_bio(membio, m_impl->key, EVP_aes_256_cbc(), nullptr, 0,
343  &passwordCallbackWrapper, &pwCallback))
344  NDN_THROW(Error("Cannot convert key to PKCS #8 format"));
345 
346  auto buffer = make_shared<Buffer>(BIO_pending(membio));
347  membio.read(buffer->data(), buffer->size());
348 
349  return buffer;
350 }
351 
353 PrivateKey::rsaDecrypt(const uint8_t* cipherText, size_t cipherLen) const
354 {
355  detail::EvpPkeyCtx ctx(m_impl->key);
356 
357  if (EVP_PKEY_decrypt_init(ctx) <= 0)
358  NDN_THROW(Error("Failed to initialize decryption context"));
359 
360  if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
361  NDN_THROW(Error("Failed to set padding"));
362 
363  size_t outlen = 0;
364  // Determine buffer length
365  if (EVP_PKEY_decrypt(ctx, nullptr, &outlen, cipherText, cipherLen) <= 0)
366  NDN_THROW(Error("Failed to estimate output length"));
367 
368  auto out = make_shared<Buffer>(outlen);
369  if (EVP_PKEY_decrypt(ctx, out->data(), &outlen, cipherText, cipherLen) <= 0)
370  NDN_THROW(Error("Failed to decrypt ciphertext"));
371 
372  out->resize(outlen);
373  return out;
374 }
375 
376 unique_ptr<PrivateKey>
377 PrivateKey::generateRsaKey(uint32_t keySize)
378 {
379  detail::EvpPkeyCtx kctx(EVP_PKEY_RSA);
380 
381  if (EVP_PKEY_keygen_init(kctx) <= 0)
382  NDN_THROW(PrivateKey::Error("Failed to initialize RSA keygen context"));
383 
384  if (EVP_PKEY_CTX_set_rsa_keygen_bits(kctx, static_cast<int>(keySize)) <= 0)
385  NDN_THROW(PrivateKey::Error("Failed to set RSA key length"));
386 
387  auto privateKey = make_unique<PrivateKey>();
388  if (EVP_PKEY_keygen(kctx, &privateKey->m_impl->key) <= 0)
389  NDN_THROW(PrivateKey::Error("Failed to generate RSA key"));
390 
391  return privateKey;
392 }
393 
394 unique_ptr<PrivateKey>
395 PrivateKey::generateEcKey(uint32_t keySize)
396 {
397  detail::EvpPkeyCtx pctx(EVP_PKEY_EC);
398 
399  if (EVP_PKEY_paramgen_init(pctx) <= 0)
400  NDN_THROW(PrivateKey::Error("Failed to initialize EC paramgen context"));
401 
402  int ret;
403  switch (keySize) {
404  case 224:
405  ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_secp224r1);
406  break;
407  case 256:
408  ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1); // same as secp256r1
409  break;
410  case 384:
411  ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_secp384r1);
412  break;
413  case 521:
414  ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_secp521r1);
415  break;
416  default:
417  NDN_THROW(std::invalid_argument("Unsupported EC key length " + to_string(keySize)));
418  }
419  if (ret <= 0)
420  NDN_THROW(PrivateKey::Error("Failed to set EC curve"));
421 
422  Impl params;
423  if (EVP_PKEY_paramgen(pctx, &params.key) <= 0)
424  NDN_THROW(PrivateKey::Error("Failed to generate EC parameters"));
425 
426  detail::EvpPkeyCtx kctx(params.key);
427  if (EVP_PKEY_keygen_init(kctx) <= 0)
428  NDN_THROW(PrivateKey::Error("Failed to initialize EC keygen context"));
429 
430  auto privateKey = make_unique<PrivateKey>();
431  if (EVP_PKEY_keygen(kctx, &privateKey->m_impl->key) <= 0)
432  NDN_THROW(PrivateKey::Error("Failed to generate EC key"));
433 
434  return privateKey;
435 }
436 
437 unique_ptr<PrivateKey>
438 generatePrivateKey(const KeyParams& keyParams)
439 {
440  switch (keyParams.getKeyType()) {
441  case KeyType::RSA: {
442  const RsaKeyParams& rsaParams = static_cast<const RsaKeyParams&>(keyParams);
443  return PrivateKey::generateRsaKey(rsaParams.getKeySize());
444  }
445  case KeyType::EC: {
446  const EcKeyParams& ecParams = static_cast<const EcKeyParams&>(keyParams);
447  return PrivateKey::generateEcKey(ecParams.getKeySize());
448  }
449  default:
450  NDN_THROW(std::invalid_argument("Unsupported asymmetric key type " +
451  boost::lexical_cast<std::string>(keyParams.getKeyType())));
452  }
453 }
454 
455 } // namespace transform
456 } // namespace security
457 } // namespace ndn
Definition: data.cpp:26
Unknown key type.
void loadPkcs8(const uint8_t *buf, size_t size, const char *pw, size_t pwLen)
Load the private key in encrypted PKCS#8 format from a buffer buf with passphrase pw...
void loadPkcs1Base64(const uint8_t *buf, size_t size)
Load the private key in base64-encoded PKCS#1 format from a buffer buf.
function< int(char *buf, size_t bufSize, bool shouldConfirm)> PasswordCallback
Callback for application to handle password input.
Definition: private-key.hpp:55
#define ENSURE_PRIVATE_KEY_NOT_LOADED(key)
Definition: private-key.cpp:41
KeyType getKeyType() const
Definition: key-params.hpp:48
uint32_t getKeySize() const
Definition: key-params.hpp:176
void loadPkcs1(const uint8_t *buf, size_t size)
Load the private key in PKCS#1 format from a buffer buf.
unique_ptr< Transform > base64Decode(bool expectNewlineEvery64Bytes)
RSA key, supports sign/verify and encrypt/decrypt operations.
void savePkcs1Base64(std::ostream &os) const
Save the private key in base64-encoded PKCS#1 format into a stream os.
static int passwordCallbackWrapper(char *buf, int size, int rwflag, void *u)
#define ENSURE_PRIVATE_KEY_LOADED(key)
Definition: private-key.cpp:35
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.
#define NDN_THROW(e)
Definition: exception.hpp:61
PrivateKey()
Create an empty private key instance.
Definition: private-key.cpp:80
KeyType
The type of a cryptographic key.
void loadPkcs8Base64(const uint8_t *buf, size_t size, const char *pw, size_t pwLen)
Load the private key in base64-encoded encrypted PKCS#8 format from a buffer buf with passphrase pw...
ConstBufferPtr derivePublicKey() const
KeyType getKeyType() const
Get the type of the private key.
Definition: private-key.cpp:88
void savePkcs1(std::ostream &os) const
Save the private key in PKCS#1 format into a stream os.
unique_ptr< Sink > streamSink(std::ostream &os)
Definition: stream-sink.cpp:53
Elliptic Curve key (e.g. for ECDSA), supports sign/verify operations.
static void opensslInitAlgorithms()
Definition: private-key.cpp:52
shared_ptr< Buffer > buf()
Flush written data to the stream and return shared pointer to the underlying buffer.
friend unique_ptr< PrivateKey > generatePrivateKey(const KeyParams &)
Generate a private key according to keyParams.
Base class of key parameters.
Definition: key-params.hpp:35
ConstBufferPtr decrypt(const uint8_t *cipherText, size_t cipherLen) const
implements an output stream that constructs ndn::Buffer
std::string to_string(const V &v)
Definition: backports.hpp:67
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.
SimplePublicKeyParams is a template for public keys with only one parameter: size.
Definition: key-params.hpp:149
shared_ptr< const Buffer > ConstBufferPtr
Definition: buffer.hpp:126
unique_ptr< Transform > base64Encode(bool needBreak)