security: simplify PrivateKey implementation and improve error handling

Change-Id: I3270e4e9fe3dd942caab6bbe0b17db678b64648b
diff --git a/src/security/detail/openssl-helper.cpp b/src/security/detail/openssl-helper.cpp
index 8f8422a..12d1106 100644
--- a/src/security/detail/openssl-helper.cpp
+++ b/src/security/detail/openssl-helper.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+/*
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -36,26 +36,18 @@
   }
 }
 
-EvpPkey::EvpPkey()
-  : m_key(nullptr)
-{
-}
-
-EvpPkey::~EvpPkey()
-{
-  EVP_PKEY_free(m_key);
-}
-
 EvpPkeyCtx::EvpPkeyCtx(EVP_PKEY* key)
   : m_ctx(EVP_PKEY_CTX_new(key, nullptr))
 {
-  BOOST_ASSERT(m_ctx != nullptr);
+  if (m_ctx == nullptr)
+    BOOST_THROW_EXCEPTION(std::runtime_error("EVP_PKEY_CTX creation failed"));
 }
 
 EvpPkeyCtx::EvpPkeyCtx(int id)
   : m_ctx(EVP_PKEY_CTX_new_id(id, nullptr))
 {
-  BOOST_ASSERT(m_ctx != nullptr);
+  if (m_ctx == nullptr)
+    BOOST_THROW_EXCEPTION(std::runtime_error("EVP_PKEY_CTX creation failed"));
 }
 
 EvpPkeyCtx::~EvpPkeyCtx()
@@ -63,14 +55,11 @@
   EVP_PKEY_CTX_free(m_ctx);
 }
 
-#if OPENSSL_VERSION_NUMBER < 0x1010000fL
-Bio::Bio(BIO_METHOD* method)
-#else
-Bio::Bio(const BIO_METHOD* method)
-#endif // OPENSSL_VERSION_NUMBER < 0x1010000fL
+Bio::Bio(Bio::MethodPtr method)
   : m_bio(BIO_new(method))
 {
-  BOOST_ASSERT(m_bio != nullptr);
+  if (m_bio == nullptr)
+    BOOST_THROW_EXCEPTION(std::runtime_error("BIO creation failed"));
 }
 
 Bio::~Bio()
@@ -78,6 +67,22 @@
   BIO_free_all(m_bio);
 }
 
+bool
+Bio::read(uint8_t* buf, size_t buflen) const noexcept
+{
+  BOOST_ASSERT(buflen <= std::numeric_limits<int>::max());
+  int n = BIO_read(m_bio, buf, static_cast<int>(buflen));
+  return n >= 0 && static_cast<size_t>(n) == buflen;
+}
+
+bool
+Bio::write(const uint8_t* buf, size_t buflen) noexcept
+{
+  BOOST_ASSERT(buflen <= std::numeric_limits<int>::max());
+  int n = BIO_write(m_bio, buf, static_cast<int>(buflen));
+  return n >= 0 && static_cast<size_t>(n) == buflen;
+}
+
 } // namespace detail
 } // namespace security
 } // namespace ndn
diff --git a/src/security/detail/openssl-helper.hpp b/src/security/detail/openssl-helper.hpp
index 50f21bc..ddd1ea6 100644
--- a/src/security/detail/openssl-helper.hpp
+++ b/src/security/detail/openssl-helper.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+/*
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -22,8 +22,8 @@
 #ifndef NDN_CXX_SECURITY_DETAIL_OPENSSL_HELPER_HPP
 #define NDN_CXX_SECURITY_DETAIL_OPENSSL_HELPER_HPP
 
-#include "../security-common.hpp"
 #include "openssl.hpp"
+#include "../security-common.hpp"
 
 namespace ndn {
 namespace security {
@@ -32,30 +32,7 @@
 const EVP_MD*
 toDigestEvpMd(DigestAlgorithm algo);
 
-class EvpPkey
-{
-public:
-  EvpPkey();
-
-  ~EvpPkey();
-
-  EVP_PKEY*
-  get() const
-  {
-    return m_key;
-  }
-
-  EVP_PKEY**
-  operator&()
-  {
-    return &m_key;
-  }
-
-private:
-  EVP_PKEY* m_key;
-};
-
-class EvpPkeyCtx
+class EvpPkeyCtx : noncopyable
 {
 public:
   explicit
@@ -66,8 +43,7 @@
 
   ~EvpPkeyCtx();
 
-  EVP_PKEY_CTX*
-  get() const
+  operator EVP_PKEY_CTX*() const
   {
     return m_ctx;
   }
@@ -76,24 +52,31 @@
   EVP_PKEY_CTX* m_ctx;
 };
 
-class Bio
+class Bio : noncopyable
 {
 public:
-  explicit
 #if OPENSSL_VERSION_NUMBER < 0x1010000fL
-  Bio(BIO_METHOD* method);
+  using MethodPtr = BIO_METHOD*;
 #else
-  Bio(const BIO_METHOD* method);
+  using MethodPtr = const BIO_METHOD*;
 #endif // OPENSSL_VERSION_NUMBER < 0x1010000fL
 
+  explicit
+  Bio(MethodPtr method);
+
   ~Bio();
 
-  BIO*
-  get() const
+  operator BIO*() const
   {
     return m_bio;
   }
 
+  bool
+  read(uint8_t* buf, size_t buflen) const noexcept;
+
+  bool
+  write(const uint8_t* buf, size_t buflen) noexcept;
+
 private:
   BIO* m_bio;
 };
diff --git a/src/security/transform/private-key.cpp b/src/security/transform/private-key.cpp
index c67d3d9..17251c4 100644
--- a/src/security/transform/private-key.cpp
+++ b/src/security/transform/private-key.cpp
@@ -29,14 +29,21 @@
 #include "../key-params.hpp"
 #include "../../encoding/buffer-stream.hpp"
 
+#include <boost/lexical_cast.hpp>
 #include <cstring>
 
 #define ENSURE_PRIVATE_KEY_LOADED(key) \
   do { \
-    if (key == nullptr) \
+    if ((key) == nullptr) \
       BOOST_THROW_EXCEPTION(Error("Private key has not been loaded yet")); \
   } while (false)
 
+#define ENSURE_PRIVATE_KEY_NOT_LOADED(key) \
+  do { \
+    if ((key) != nullptr) \
+      BOOST_THROW_EXCEPTION(Error("Private key has already been loaded")); \
+  } while (false)
+
 namespace ndn {
 namespace security {
 namespace transform {
@@ -56,7 +63,7 @@
 class PrivateKey::Impl
 {
 public:
-  Impl()
+  Impl() noexcept
     : key(nullptr)
   {
   }
@@ -71,7 +78,7 @@
 };
 
 PrivateKey::PrivateKey()
-  : m_impl(new Impl)
+  : m_impl(make_unique<Impl>())
 {
 }
 
@@ -80,12 +87,11 @@
 void
 PrivateKey::loadPkcs1(const uint8_t* buf, size_t size)
 {
-  detail::Bio mem(BIO_s_mem());
-  BIO_write(mem.get(), buf, size);
+  ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
+  opensslInitAlgorithms();
 
-  d2i_PrivateKey_bio(mem.get(), &m_impl->key);
-
-  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
+  if (d2i_AutoPrivateKey(&m_impl->key, &buf, static_cast<long>(size)) == nullptr)
+    BOOST_THROW_EXCEPTION(Error("Failed to load private key"));
 }
 
 void
@@ -116,18 +122,19 @@
 PrivateKey::loadPkcs8(const uint8_t* buf, size_t size, const char* pw, size_t pwLen)
 {
   BOOST_ASSERT(std::strlen(pw) == pwLen);
+  ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
   opensslInitAlgorithms();
 
-  detail::Bio mem(BIO_s_mem());
-  BIO_write(mem.get(), buf, size);
+  detail::Bio membio(BIO_s_mem());
+  if (!membio.write(buf, size))
+    BOOST_THROW_EXCEPTION(Error("Failed to copy buffer"));
 
-  m_impl->key = d2i_PKCS8PrivateKey_bio(mem.get(), &m_impl->key, nullptr, const_cast<char*>(pw));
-
-  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
+  if (d2i_PKCS8PrivateKey_bio(membio, &m_impl->key, nullptr, const_cast<char*>(pw)) == nullptr)
+    BOOST_THROW_EXCEPTION(Error("Failed to load private key"));
 }
 
 static inline int
-passwordCallback(char* buf, int size, int rwflag, void* u)
+passwordCallbackWrapper(char* buf, int size, int rwflag, void* u)
 {
   BOOST_ASSERT(size >= 0);
   auto cb = reinterpret_cast<PrivateKey::PasswordCallback*>(u);
@@ -137,17 +144,20 @@
 void
 PrivateKey::loadPkcs8(const uint8_t* buf, size_t size, PasswordCallback pwCallback)
 {
+  ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
   opensslInitAlgorithms();
 
-  detail::Bio mem(BIO_s_mem());
-  BIO_write(mem.get(), buf, size);
+  detail::Bio membio(BIO_s_mem());
+  if (!membio.write(buf, size))
+    BOOST_THROW_EXCEPTION(Error("Failed to copy buffer"));
 
   if (pwCallback)
-    m_impl->key = d2i_PKCS8PrivateKey_bio(mem.get(), &m_impl->key, passwordCallback, &pwCallback);
+    m_impl->key = d2i_PKCS8PrivateKey_bio(membio, nullptr, &passwordCallbackWrapper, &pwCallback);
   else
-    m_impl->key = d2i_PKCS8PrivateKey_bio(mem.get(), &m_impl->key, nullptr, nullptr);
+    m_impl->key = d2i_PKCS8PrivateKey_bio(membio, nullptr, nullptr, nullptr);
 
-  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
+  if (m_impl->key == nullptr)
+    BOOST_THROW_EXCEPTION(Error("Failed to load private key"));
 }
 
 void
@@ -241,8 +251,7 @@
 
   uint8_t* pkcs8 = nullptr;
   int len = i2d_PUBKEY(m_impl->key, &pkcs8);
-
-  if (len <= 0)
+  if (len < 0)
     BOOST_THROW_EXCEPTION(Error("Failed to derive public key"));
 
   auto result = make_shared<Buffer>(pkcs8, len);
@@ -256,15 +265,20 @@
 {
   ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
 
+  int keyType =
 #if OPENSSL_VERSION_NUMBER < 0x1010000fL
-  switch (EVP_PKEY_type(m_impl->key->type)) {
+    EVP_PKEY_type(m_impl->key->type);
 #else
-  switch (EVP_PKEY_base_id(m_impl->key)) {
+    EVP_PKEY_base_id(m_impl->key);
 #endif // OPENSSL_VERSION_NUMBER < 0x1010000fL
+
+  switch (keyType) {
+    case EVP_PKEY_NONE:
+      BOOST_THROW_EXCEPTION(Error("Failed to determine key type"));
     case EVP_PKEY_RSA:
       return rsaDecrypt(cipherText, cipherLen);
     default:
-      BOOST_THROW_EXCEPTION(Error("Decryption is not supported for this key type"));
+      BOOST_THROW_EXCEPTION(Error("Decryption is not supported for key type " + to_string(keyType)));
   }
 }
 
@@ -280,14 +294,12 @@
   ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
   opensslInitAlgorithms();
 
-  detail::Bio mem(BIO_s_mem());
-  int ret = i2d_PrivateKey_bio(mem.get(), m_impl->key);
-  if (ret != 1)
-    BOOST_THROW_EXCEPTION(Error("Cannot convert key into PKCS1 format"));
+  detail::Bio membio(BIO_s_mem());
+  if (!i2d_PrivateKey_bio(membio, m_impl->key))
+    BOOST_THROW_EXCEPTION(Error("Cannot convert key to PKCS #1 format"));
 
-  int len8 = BIO_pending(mem.get());
-  auto buffer = make_shared<Buffer>(len8);
-  BIO_read(mem.get(), buffer->buf(), len8);
+  auto buffer = make_shared<Buffer>(BIO_pending(membio));
+  membio.read(buffer->buf(), buffer->size());
 
   return buffer;
 }
@@ -299,15 +311,13 @@
   ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
   opensslInitAlgorithms();
 
-  detail::Bio mem(BIO_s_mem());
-  int ret = i2d_PKCS8PrivateKey_bio(mem.get(), m_impl->key, EVP_des_cbc(),
-                                    const_cast<char*>(pw), pwLen, nullptr, nullptr);
-  if (ret != 1)
-    BOOST_THROW_EXCEPTION(Error("Cannot convert key into PKCS8 format"));
+  detail::Bio membio(BIO_s_mem());
+  if (!i2d_PKCS8PrivateKey_bio(membio, m_impl->key, EVP_des_cbc(), nullptr, 0,
+                               nullptr, const_cast<char*>(pw)))
+    BOOST_THROW_EXCEPTION(Error("Cannot convert key to PKCS #8 format"));
 
-  int len8 = BIO_pending(mem.get());
-  auto buffer = make_shared<Buffer>(len8);
-  BIO_read(mem.get(), buffer->buf(), len8);
+  auto buffer = make_shared<Buffer>(BIO_pending(membio));
+  membio.read(buffer->buf(), buffer->size());
 
   return buffer;
 }
@@ -318,16 +328,13 @@
   ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
   opensslInitAlgorithms();
 
-  detail::Bio mem(BIO_s_mem());
-  int ret = i2d_PKCS8PrivateKey_bio(mem.get(), m_impl->key, EVP_des_cbc(),
-                                    nullptr, 0,
-                                    passwordCallback, &pwCallback);
-  if (ret != 1)
-    BOOST_THROW_EXCEPTION(Error("Cannot convert key into PKCS8 format"));
+  detail::Bio membio(BIO_s_mem());
+  if (!i2d_PKCS8PrivateKey_bio(membio, m_impl->key, EVP_des_cbc(), nullptr, 0,
+                               &passwordCallbackWrapper, &pwCallback))
+    BOOST_THROW_EXCEPTION(Error("Cannot convert key to PKCS #8 format"));
 
-  int len8 = BIO_pending(mem.get());
-  auto buffer = make_shared<Buffer>(len8);
-  BIO_read(mem.get(), buffer->buf(), len8);
+  auto buffer = make_shared<Buffer>(BIO_pending(membio));
+  membio.read(buffer->buf(), buffer->size());
 
   return buffer;
 }
@@ -337,101 +344,76 @@
 {
   detail::EvpPkeyCtx ctx(m_impl->key);
 
-  if (EVP_PKEY_decrypt_init(ctx.get()) <= 0)
+  if (EVP_PKEY_decrypt_init(ctx) <= 0)
     BOOST_THROW_EXCEPTION(Error("Failed to initialize decryption context"));
 
-  if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING) <= 0)
+  if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
     BOOST_THROW_EXCEPTION(Error("Failed to set padding"));
 
   size_t outlen = 0;
   // Determine buffer length
-  if (EVP_PKEY_decrypt(ctx.get(), nullptr, &outlen, cipherText, cipherLen) <= 0)
+  if (EVP_PKEY_decrypt(ctx, nullptr, &outlen, cipherText, cipherLen) <= 0)
     BOOST_THROW_EXCEPTION(Error("Failed to estimate output length"));
 
   auto out = make_shared<Buffer>(outlen);
-
-  if (EVP_PKEY_decrypt(ctx.get(), out->buf(), &outlen, cipherText, cipherLen) <= 0)
-    BOOST_THROW_EXCEPTION(Error("Failed to decrypt cipher text"));
+  if (EVP_PKEY_decrypt(ctx, out->buf(), &outlen, cipherText, cipherLen) <= 0)
+    BOOST_THROW_EXCEPTION(Error("Failed to decrypt ciphertext"));
 
   out->resize(outlen);
   return out;
 }
 
-static unique_ptr<PrivateKey>
-generateRsaKey(uint32_t keySize)
+unique_ptr<PrivateKey>
+PrivateKey::generateRsaKey(uint32_t keySize)
 {
   detail::EvpPkeyCtx kctx(EVP_PKEY_RSA);
 
-  int ret = EVP_PKEY_keygen_init(kctx.get());
-  if (ret != 1)
-    BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate RSA key"));
+  if (EVP_PKEY_keygen_init(kctx) <= 0)
+    BOOST_THROW_EXCEPTION(PrivateKey::Error("Failed to initialize RSA keygen context"));
 
-  ret = EVP_PKEY_CTX_set_rsa_keygen_bits(kctx.get(), keySize);
-  if (ret != 1)
-    BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate RSA key"));
-
-  detail::EvpPkey key;
-  ret = EVP_PKEY_keygen(kctx.get(), &key);
-  if (ret != 1)
-    BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate RSA key"));
-
-  detail::Bio mem(BIO_s_mem());
-  i2d_PrivateKey_bio(mem.get(), key.get());
-  int len = BIO_pending(mem.get());
-  Buffer buffer(len);
-  BIO_read(mem.get(), buffer.buf(), len);
+  if (EVP_PKEY_CTX_set_rsa_keygen_bits(kctx, static_cast<int>(keySize)) <= 0)
+    BOOST_THROW_EXCEPTION(PrivateKey::Error("Failed to set RSA key length"));
 
   auto privateKey = make_unique<PrivateKey>();
-  privateKey->loadPkcs1(buffer.buf(), buffer.size());
+  if (EVP_PKEY_keygen(kctx, &privateKey->m_impl->key) <= 0)
+    BOOST_THROW_EXCEPTION(PrivateKey::Error("Failed to generate RSA key"));
 
   return privateKey;
 }
 
-static unique_ptr<PrivateKey>
-generateEcKey(uint32_t keySize)
+unique_ptr<PrivateKey>
+PrivateKey::generateEcKey(uint32_t keySize)
 {
-  detail::EvpPkeyCtx ctx(EVP_PKEY_EC);
+  detail::EvpPkeyCtx pctx(EVP_PKEY_EC);
 
-  int ret = EVP_PKEY_paramgen_init(ctx.get());
-  if (ret != 1)
-    BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate EC key"));
+  if (EVP_PKEY_paramgen_init(pctx) <= 0)
+    BOOST_THROW_EXCEPTION(PrivateKey::Error("Failed to initialize EC paramgen context"));
 
+  int ret;
   switch (keySize) {
     case 256:
-      ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), NID_X9_62_prime256v1);
+      ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1); // same as secp256r1
       break;
     case 384:
-      ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), NID_secp384r1);
+      ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_secp384r1);
       break;
     default:
-      BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate EC key"));
+      BOOST_THROW_EXCEPTION(PrivateKey::Error("Unsupported EC key length"));
   }
-  if (ret != 1)
-    BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate EC key"));
+  if (ret <= 0)
+    BOOST_THROW_EXCEPTION(PrivateKey::Error("Failed to set EC curve"));
 
-  detail::EvpPkey params;
-  ret = EVP_PKEY_paramgen(ctx.get(), &params);
-  if (ret != 1)
-    BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate EC key"));
+  Impl params;
+  if (EVP_PKEY_paramgen(pctx, &params.key) <= 0)
+    BOOST_THROW_EXCEPTION(PrivateKey::Error("Failed to generate EC parameters"));
 
-  detail::EvpPkeyCtx kctx(params.get());
-  ret = EVP_PKEY_keygen_init(kctx.get());
-  if (ret != 1)
-    BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate EC key"));
-
-  detail::EvpPkey key;
-  ret = EVP_PKEY_keygen(kctx.get(), &key);
-  if (ret != 1)
-    BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate EC key"));
-
-  detail::Bio mem(BIO_s_mem());
-  i2d_PrivateKey_bio(mem.get(), key.get());
-  int len = BIO_pending(mem.get());
-  Buffer buffer(len);
-  BIO_read(mem.get(), buffer.buf(), len);
+  detail::EvpPkeyCtx kctx(params.key);
+  if (EVP_PKEY_keygen_init(kctx) <= 0)
+    BOOST_THROW_EXCEPTION(PrivateKey::Error("Failed to initialize EC keygen context"));
 
   auto privateKey = make_unique<PrivateKey>();
-  privateKey->loadPkcs1(buffer.buf(), buffer.size());
+  if (EVP_PKEY_keygen(kctx, &privateKey->m_impl->key) <= 0)
+    BOOST_THROW_EXCEPTION(PrivateKey::Error("Failed to generate EC key"));
 
   return privateKey;
 }
@@ -442,14 +424,15 @@
   switch (keyParams.getKeyType()) {
     case KeyType::RSA: {
       const RsaKeyParams& rsaParams = static_cast<const RsaKeyParams&>(keyParams);
-      return generateRsaKey(rsaParams.getKeySize());
+      return PrivateKey::generateRsaKey(rsaParams.getKeySize());
     }
     case KeyType::EC: {
       const EcKeyParams& ecParams = static_cast<const EcKeyParams&>(keyParams);
-      return generateEcKey(ecParams.getKeySize());
+      return PrivateKey::generateEcKey(ecParams.getKeySize());
     }
     default:
-      BOOST_THROW_EXCEPTION(std::invalid_argument("Unsupported asymmetric key type"));
+      BOOST_THROW_EXCEPTION(std::invalid_argument("Unsupported asymmetric key type " +
+                                                  boost::lexical_cast<std::string>(keyParams.getKeyType())));
   }
 }
 
diff --git a/src/security/transform/private-key.hpp b/src/security/transform/private-key.hpp
index 500386b..98f5bd6 100644
--- a/src/security/transform/private-key.hpp
+++ b/src/security/transform/private-key.hpp
@@ -22,7 +22,6 @@
 #ifndef NDN_CXX_SECURITY_TRANSFORM_PRIVATE_KEY_HPP
 #define NDN_CXX_SECURITY_TRANSFORM_PRIVATE_KEY_HPP
 
-#include "public-key.hpp"
 #include "../../encoding/buffer.hpp"
 
 namespace ndn {
@@ -48,8 +47,6 @@
     }
   };
 
-  friend class SignerFilter;
-
   /**
    * @brief Callback for application to handle password input
    *
@@ -62,9 +59,9 @@
 
 public:
   /**
-   * @brief Create a private key instance
+   * @brief Create an empty private key instance
    *
-   * One must call loadXXXX(...) to load private key.
+   * One must call loadXXXX(...) to load a private key.
    */
   PrivateKey();
 
@@ -96,7 +93,6 @@
 
   /**
    * @brief Load the private key in encrypted PKCS#8 format from a buffer @p buf with passphrase @p pw
-   *
    * @pre strlen(pw) == pwLen
    */
   void
@@ -113,7 +109,6 @@
 
   /**
    * @brief Load the private key in encrypted PKCS#8 format from a stream @p is with passphrase @p pw
-   *
    * @pre strlen(pw) == pwLen
    */
   void
@@ -131,7 +126,6 @@
   /**
    * @brief Load the private key in base64-encoded encrypted PKCS#8 format from a buffer @p buf
    *        with passphrase @p pw
-   *
    * @pre strlen(pw) == pwLen
    */
   void
@@ -149,7 +143,6 @@
   /**
    * @brief Load the private key in base64-encoded encrypted PKCS#8 format from a stream @p is
    *        with passphrase @p pw
-   *
    * @pre strlen(pw) == pwLen
    */
   void
@@ -213,7 +206,7 @@
   derivePublicKey() const;
 
   /**
-   * @return Plain text of @p cipherText decrypted using the private key.
+   * @return Plain text of @p cipherText decrypted using this private key.
    *
    * Only RSA encryption is supported for now.
    */
@@ -221,10 +214,12 @@
   decrypt(const uint8_t* cipherText, size_t cipherLen) const;
 
 private:
+  friend class SignerFilter;
+
   /**
-   * @return A pointer to an EVP_PKEY instance.
+   * @return A pointer to an OpenSSL EVP_PKEY instance.
    *
-   * One need to explicitly cast the return value to EVP_PKEY*.
+   * The caller needs to explicitly cast the return value to `EVP_PKEY*`.
    */
   void*
   getEvpPkey() const;
@@ -243,17 +238,26 @@
   rsaDecrypt(const uint8_t* cipherText, size_t cipherLen) const;
 
 private:
+  friend unique_ptr<PrivateKey> generatePrivateKey(const KeyParams&);
+
+  static unique_ptr<PrivateKey>
+  generateRsaKey(uint32_t keySize);
+
+  static unique_ptr<PrivateKey>
+  generateEcKey(uint32_t keySize);
+
+private:
   class Impl;
   const unique_ptr<Impl> m_impl;
 };
 
 /**
- * @brief generate a private key according to @p keyParams.
+ * @brief Generate a private key according to @p keyParams
  *
- * @note the public key can be derived from the private key
+ * @note The public key can be derived from the private key.
  *
- * @throw std::argument_error if the key type is not supported
- * @throw std::runtime_error when failing to generate the key
+ * @throw std::invalid_argument the specified key type is not supported
+ * @throw std::runtime_error    key generation fails
  */
 unique_ptr<PrivateKey>
 generatePrivateKey(const KeyParams& keyParams);
diff --git a/src/security/transform/public-key.cpp b/src/security/transform/public-key.cpp
index 82edd13..c17fdc0 100644
--- a/src/security/transform/public-key.cpp
+++ b/src/security/transform/public-key.cpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
@@ -141,15 +141,20 @@
 {
   ENSURE_PUBLIC_KEY_LOADED(m_impl->key);
 
+  int keyType =
 #if OPENSSL_VERSION_NUMBER < 0x1010000fL
-  switch (EVP_PKEY_type(m_impl->key->type)) {
+    EVP_PKEY_type(m_impl->key->type);
 #else
-  switch (EVP_PKEY_base_id(m_impl->key)) {
+    EVP_PKEY_base_id(m_impl->key);
 #endif // OPENSSL_VERSION_NUMBER < 0x1010000fL
-  case EVP_PKEY_RSA:
-    return rsaEncrypt(plainText, plainLen);
-  default:
-    BOOST_THROW_EXCEPTION(Error("Encryption is not supported for this key type"));
+
+  switch (keyType) {
+    case EVP_PKEY_NONE:
+      BOOST_THROW_EXCEPTION(Error("Failed to determine key type"));
+    case EVP_PKEY_RSA:
+      return rsaEncrypt(plainText, plainLen);
+    default:
+      BOOST_THROW_EXCEPTION(Error("Encryption is not supported for key type " + to_string(keyType)));
   }
 }
 
@@ -181,20 +186,20 @@
 {
   detail::EvpPkeyCtx ctx(m_impl->key);
 
-  if (EVP_PKEY_encrypt_init(ctx.get()) <= 0)
+  if (EVP_PKEY_encrypt_init(ctx) <= 0)
     BOOST_THROW_EXCEPTION(Error("Failed to initialize encryption context"));
 
-  if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING) <= 0)
+  if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
     BOOST_THROW_EXCEPTION(Error("Failed to set padding"));
 
   size_t outlen = 0;
   // Determine buffer length
-  if (EVP_PKEY_encrypt(ctx.get(), nullptr, &outlen, plainText, plainLen) <= 0)
+  if (EVP_PKEY_encrypt(ctx, nullptr, &outlen, plainText, plainLen) <= 0)
     BOOST_THROW_EXCEPTION(Error("Failed to estimate output length"));
 
   auto out = make_shared<Buffer>(outlen);
 
-  if (EVP_PKEY_encrypt(ctx.get(), out->buf(), &outlen, plainText, plainLen) <= 0)
+  if (EVP_PKEY_encrypt(ctx, out->buf(), &outlen, plainText, plainLen) <= 0)
     BOOST_THROW_EXCEPTION(Error("Failed to encrypt plaintext"));
 
   out->resize(outlen);
diff --git a/tests/unit-tests/security/transform/private-key.t.cpp b/tests/unit-tests/security/transform/private-key.t.cpp
index 49a5ea1..539f3b1 100644
--- a/tests/unit-tests/security/transform/private-key.t.cpp
+++ b/tests/unit-tests/security/transform/private-key.t.cpp
@@ -20,9 +20,10 @@
  */
 
 #include "security/transform/private-key.hpp"
-#include "security/transform.hpp"
-#include "security/key-params.hpp"
+
 #include "encoding/buffer-stream.hpp"
+#include "security/key-params.hpp"
+#include "security/transform.hpp"
 
 #include "boost-test.hpp"
 #include <boost/mpl/vector.hpp>
@@ -38,12 +39,9 @@
 BOOST_AUTO_TEST_SUITE(Transform)
 BOOST_AUTO_TEST_SUITE(TestPrivateKey)
 
-class RsaKeyTestData
+struct RsaKeyTestData
 {
-public:
-  RsaKeyTestData()
-  {
-    privateKeyPkcs1 =
+  const std::string privateKeyPkcs1 =
       "MIIEpAIBAAKCAQEAw0WM1/WhAxyLtEqsiAJgWDZWuzkYpeYVdeeZcqRZzzfRgBQT\n"
       "sNozS5t4HnwTZhwwXbH7k3QN0kRTV826Xobws3iigohnM9yTK+KKiayPhIAm/+5H\n"
       "GT6SgFJhYhqo1/upWdueojil6RP4/AgavHhopxlAVbk6G9VdVnlQcQ5Zv0OcGi73\n"
@@ -69,8 +67,7 @@
       "cuHICmsCgYAtFJ1idqMoHxES3mlRpf2JxyQudP3SCm2WpGmqVzhRYInqeatY5sUd\n"
       "lPLHm/p77RT7EyxQHTlwn8FJPuM/4ZH1rQd/vB+Y8qAtYJCexDMsbvLW+Js+VOvk\n"
       "jweEC0nrcL31j9mF0vz5E6tfRu4hhJ6L4yfWs0gSejskeVB/w8QY4g==\n";
-
-    privateKeyPkcs8 =
+  const std::string privateKeyPkcs8 =
       "MIIFCzA9BgkqhkiG9w0BBQ0wMDAbBgkqhkiG9w0BBQwwDgQIOKYJXvB6p8kCAggA\n"
       "MBEGBSsOAwIHBAiQgMK8kQXTyASCBMjeNiKYYw5/yHgs9BfSGrpqvV0LkkgMQNUW\n"
       "R4ZY8fuNjZynd+PxDuw2pyrv1Yv3jc+tupwUehZEzYOnGd53wQAuLO+Z0TBgRFN7\n"
@@ -98,8 +95,7 @@
       "aEHH2tjEtnTqVyTchr1yHoupcFOCkA0dAA66XqwcssQxJiMGrWTpCbgd9mrTXQaZ\n"
       "U7afFN1jpO78tgBQUUpImXdHLLsqdN5tefqjileZGZ9x3/C6TNAfDwYJdsicNNn5\n"
       "y+JVsbltfLWlJxb9teb3dtQiFlJ7ofprLJnJVqI/Js8lozY+KaxV2vtbZkcD4dM=\n";
-
-    publicKeyPkcs8 =
+  const std::string publicKeyPkcs8 =
       "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0WM1/WhAxyLtEqsiAJg\n"
       "WDZWuzkYpeYVdeeZcqRZzzfRgBQTsNozS5t4HnwTZhwwXbH7k3QN0kRTV826Xobw\n"
       "s3iigohnM9yTK+KKiayPhIAm/+5HGT6SgFJhYhqo1/upWdueojil6RP4/AgavHho\n"
@@ -107,20 +103,11 @@
       "ZwIL5PuE9BiO6I39cL9z7EK1SfZhOWvDe/qH7YhD/BHwcWit8FjRww1glwRVTJsA\n"
       "9rH58ynaAix0tcR/nBMRLUX+e3rURHg6UbSjJbdb9qmKM1fTGHKUzL/5pMG6uBU0\n"
       "ywIDAQAB\n";
-  }
-
-public:
-  std::string privateKeyPkcs1;
-  std::string privateKeyPkcs8;
-  std::string publicKeyPkcs8;
 };
 
-class EcKeyTestData
+struct EcKeyTestData
 {
-public:
-  EcKeyTestData()
-  {
-    privateKeyPkcs1 =
+  const std::string privateKeyPkcs1 =
       "MIIBaAIBAQQgRxwcbzK9RV6AHYFsDcykI86o3M/a1KlJn0z8PcLMBZOggfowgfcC\n"
       "AQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAAAAAAAAAAAAAA////////////////\n"
       "MFsEIP////8AAAABAAAAAAAAAAAAAAAA///////////////8BCBaxjXYqjqT57Pr\n"
@@ -129,8 +116,7 @@
       "K84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA//////////+85vqtpxeehPO5ysL8\n"
       "YyVRAgEBoUQDQgAEaG4WJuDAt0QkEM4t29KDUdzkQlMPGrqWzkWhgt9OGnwc6O7A\n"
       "ZLPSrDyhwyrKS7XLRXml5DisQ93RvByll32y8A==\n";
-
-    privateKeyPkcs8 =
+  const std::string privateKeyPkcs8 =
       "MIIBwzA9BgkqhkiG9w0BBQ0wMDAbBgkqhkiG9w0BBQwwDgQIVHkBzLGtDvICAggA\n"
       "MBEGBSsOAwIHBAhk6g9eI3toNwSCAYDd+LWPDBTrKV7vUyxTvDbpUd0eXfh73DKA\n"
       "MHkdHuVmhpmpBbsF9XvaFuL8J/1xi1Yl2XGw8j3WyrprD2YEhl/+zKjNbdTDJmNO\n"
@@ -141,8 +127,7 @@
       "6cuFItbu4QvbVwailgdUjOYwIJCmIxExlPV0ohS24pFGsO03Yn7W8rBB9VWENYmG\n"
       "HkZIbGsHv7O9Wy7fv+FJgZkjeti0807IsNXSJl8LUK0ZIhAR7OU8uONWMsbHdQnk\n"
       "q1HB1ZKa52ugACl7g/DF9b7CoSAjFeE=\n";
-
-    publicKeyPkcs8 =
+  const std::string publicKeyPkcs8 =
       "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n"
       "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n"
       "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n"
@@ -150,12 +135,6 @@
       "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n"
       "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABGhuFibgwLdEJBDOLdvSg1Hc\n"
       "5EJTDxq6ls5FoYLfThp8HOjuwGSz0qw8ocMqyku1y0V5peQ4rEPd0bwcpZd9svA=\n";
-  }
-
-public:
-  std::string privateKeyPkcs1;
-  std::string privateKeyPkcs8;
-  std::string publicKeyPkcs8;
 };
 
 using KeyTestDataSets = boost::mpl::vector<RsaKeyTestData, EcKeyTestData>;
@@ -167,9 +146,8 @@
   sKey.loadPkcs8(encoding->buf(), encoding->size(), password.c_str(), password.size());
   OBufferStream os;
   sKey.savePkcs1(os);
-  ConstBufferPtr keyPkcs1Str = os.buf();
   BOOST_CHECK_EQUAL_COLLECTIONS(pkcs1->begin(), pkcs1->end(),
-                                keyPkcs1Str->begin(), keyPkcs1Str->end());
+                                os.buf()->begin(), os.buf()->end());
 }
 
 static void
@@ -186,7 +164,6 @@
 
   const uint8_t* sKeyPkcs1Base64 = reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs1.c_str());
   size_t sKeyPkcs1Base64Len = dataSet.privateKeyPkcs1.size();
-
   OBufferStream os;
   bufferSource(sKeyPkcs1Base64, sKeyPkcs1Base64Len) >> base64Decode() >> streamSink(os);
   ConstBufferPtr sKeyPkcs1Buf = os.buf();
@@ -195,45 +172,39 @@
 
   // load key in base64-encoded pkcs1 format
   PrivateKey sKey;
-  BOOST_REQUIRE_NO_THROW(sKey.loadPkcs1Base64(sKeyPkcs1Base64, sKeyPkcs1Base64Len));
+  BOOST_CHECK_NO_THROW(sKey.loadPkcs1Base64(sKeyPkcs1Base64, sKeyPkcs1Base64Len));
 
   std::stringstream ss2(dataSet.privateKeyPkcs1);
   PrivateKey sKey2;
-  BOOST_REQUIRE_NO_THROW(sKey2.loadPkcs1Base64(ss2));
+  BOOST_CHECK_NO_THROW(sKey2.loadPkcs1Base64(ss2));
 
   // load key in pkcs1 format
   PrivateKey sKey3;
-  BOOST_REQUIRE_NO_THROW(sKey3.loadPkcs1(sKeyPkcs1, sKeyPkcs1Len));
+  BOOST_CHECK_NO_THROW(sKey3.loadPkcs1(sKeyPkcs1, sKeyPkcs1Len));
 
   std::stringstream ss4;
   ss4.write(reinterpret_cast<const char*>(sKeyPkcs1), sKeyPkcs1Len);
   PrivateKey sKey4;
-  BOOST_REQUIRE_NO_THROW(sKey4.loadPkcs1(ss4));
+  BOOST_CHECK_NO_THROW(sKey4.loadPkcs1(ss4));
 
   // save key in base64-encoded pkcs1 format
   OBufferStream os2;
   BOOST_REQUIRE_NO_THROW(sKey.savePkcs1Base64(os2));
-  ConstBufferPtr keyPkcs1Base64Str = os2.buf();
   BOOST_CHECK_EQUAL_COLLECTIONS(sKeyPkcs1Base64, sKeyPkcs1Base64 + sKeyPkcs1Base64Len,
-                                keyPkcs1Base64Str->begin(), keyPkcs1Base64Str->end());
+                                os2.buf()->begin(), os2.buf()->end());
 
   // save key in pkcs1 format
   OBufferStream os3;
   BOOST_REQUIRE_NO_THROW(sKey.savePkcs1(os3));
-  ConstBufferPtr keyPkcs1Str = os3.buf();
   BOOST_CHECK_EQUAL_COLLECTIONS(sKeyPkcs1, sKeyPkcs1 + sKeyPkcs1Len,
-                                keyPkcs1Str->begin(), keyPkcs1Str->end());
-
-
+                                os3.buf()->begin(), os3.buf()->end());
 
   const uint8_t* sKeyPkcs8Base64 = reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs8.c_str());
   size_t sKeyPkcs8Base64Len = dataSet.privateKeyPkcs8.size();
-
   OBufferStream os4;
   bufferSource(sKeyPkcs8Base64, sKeyPkcs8Base64Len) >> base64Decode() >> streamSink(os4);
-  ConstBufferPtr sKeyPkcs8Buf = os4.buf();
-  const uint8_t* sKeyPkcs8 = sKeyPkcs8Buf->buf();
-  size_t sKeyPkcs8Len = sKeyPkcs8Buf->size();
+  const uint8_t* sKeyPkcs8 = os4.buf()->buf();
+  size_t sKeyPkcs8Len = os4.buf()->size();
 
   std::string password("password");
   std::string wrongpw("wrongpw");
@@ -245,65 +216,59 @@
 
   // load key in base64-encoded pkcs8 format
   PrivateKey sKey5;
-  BOOST_REQUIRE_NO_THROW(sKey5.loadPkcs8Base64(sKeyPkcs8Base64, sKeyPkcs8Base64Len,
-                                               password.c_str(), password.size()));
+  BOOST_CHECK_NO_THROW(sKey5.loadPkcs8Base64(sKeyPkcs8Base64, sKeyPkcs8Base64Len,
+                                             password.c_str(), password.size()));
 
   PrivateKey sKey6;
-  BOOST_REQUIRE_NO_THROW(sKey6.loadPkcs8Base64(sKeyPkcs8Base64, sKeyPkcs8Base64Len, pwCallback));
+  BOOST_CHECK_NO_THROW(sKey6.loadPkcs8Base64(sKeyPkcs8Base64, sKeyPkcs8Base64Len, pwCallback));
 
   std::stringstream ss7(dataSet.privateKeyPkcs8);
   PrivateKey sKey7;
-  BOOST_REQUIRE_NO_THROW(sKey7.loadPkcs8Base64(ss7, password.c_str(), password.size()));
+  BOOST_CHECK_NO_THROW(sKey7.loadPkcs8Base64(ss7, password.c_str(), password.size()));
 
   std::stringstream ss8(dataSet.privateKeyPkcs8);
   PrivateKey sKey8;
-  BOOST_REQUIRE_NO_THROW(sKey8.loadPkcs8Base64(ss8, pwCallback));
+  BOOST_CHECK_NO_THROW(sKey8.loadPkcs8Base64(ss8, pwCallback));
 
   // load key in pkcs8 format
   PrivateKey sKey9;
-  BOOST_REQUIRE_NO_THROW(sKey9.loadPkcs8(sKeyPkcs8, sKeyPkcs8Len,
-                                         password.c_str(), password.size()));
+  BOOST_CHECK_NO_THROW(sKey9.loadPkcs8(sKeyPkcs8, sKeyPkcs8Len, password.c_str(), password.size()));
 
   PrivateKey sKey10;
-  BOOST_REQUIRE_NO_THROW(sKey10.loadPkcs8(sKeyPkcs8, sKeyPkcs8Len, pwCallback));
+  BOOST_CHECK_NO_THROW(sKey10.loadPkcs8(sKeyPkcs8, sKeyPkcs8Len, pwCallback));
 
   std::stringstream ss11;
   ss11.write(reinterpret_cast<const char*>(sKeyPkcs8), sKeyPkcs8Len);
   PrivateKey sKey11;
-  BOOST_REQUIRE_NO_THROW(sKey11.loadPkcs8(ss11, password.c_str(), password.size()));
+  BOOST_CHECK_NO_THROW(sKey11.loadPkcs8(ss11, password.c_str(), password.size()));
 
   std::stringstream ss12;
   ss12.write(reinterpret_cast<const char*>(sKeyPkcs8), sKeyPkcs8Len);
   PrivateKey sKey12;
-  BOOST_REQUIRE_NO_THROW(sKey12.loadPkcs8(ss12, pwCallback));
+  BOOST_CHECK_NO_THROW(sKey12.loadPkcs8(ss12, pwCallback));
 
-  // load key using wrong password, Error is expected.
+  // load key using wrong password, Error is expected
   PrivateKey sKey13;
-  BOOST_CHECK_THROW(sKey13.loadPkcs8Base64(sKeyPkcs8Base64, sKeyPkcs8Base64Len,
-                                           wrongpw.c_str(), wrongpw.size()),
+  BOOST_CHECK_THROW(sKey13.loadPkcs8Base64(sKeyPkcs8Base64, sKeyPkcs8Base64Len, wrongpw.c_str(), wrongpw.size()),
                     PrivateKey::Error);
 
   // save key in base64-encoded pkcs8 format
   OBufferStream os14;
   BOOST_REQUIRE_NO_THROW(sKey.savePkcs8Base64(os14, password.c_str(), password.size()));
-  ConstBufferPtr encoded14 = os14.buf();
-  checkPkcs8Base64Encoding(encoded14, password, sKeyPkcs1Buf);
+  checkPkcs8Base64Encoding(os14.buf(), password, sKeyPkcs1Buf);
 
   OBufferStream os15;
   BOOST_REQUIRE_NO_THROW(sKey.savePkcs8Base64(os15, pwCallback));
-  ConstBufferPtr encoded15 = os15.buf();
-  checkPkcs8Base64Encoding(encoded15, password, sKeyPkcs1Buf);
+  checkPkcs8Base64Encoding(os15.buf(), password, sKeyPkcs1Buf);
 
   // save key in pkcs8 format
   OBufferStream os16;
   BOOST_REQUIRE_NO_THROW(sKey.savePkcs8(os16, password.c_str(), password.size()));
-  ConstBufferPtr encoded16 = os16.buf();
-  checkPkcs8Encoding(encoded16, password, sKeyPkcs1Buf);
+  checkPkcs8Encoding(os16.buf(), password, sKeyPkcs1Buf);
 
   OBufferStream os17;
   BOOST_REQUIRE_NO_THROW(sKey.savePkcs8(os17, pwCallback));
-  ConstBufferPtr encoded17 = os17.buf();
-  checkPkcs8Encoding(encoded17, password, sKeyPkcs1Buf);
+  checkPkcs8Encoding(os17.buf(), password, sKeyPkcs1Buf);
 }
 
 BOOST_AUTO_TEST_CASE_TEMPLATE(DerivePublicKey, T, KeyTestDataSets)
@@ -312,12 +277,11 @@
 
   const uint8_t* sKeyPkcs1Base64 = reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs1.c_str());
   size_t sKeyPkcs1Base64Len = dataSet.privateKeyPkcs1.size();
-
   PrivateKey sKey;
-  BOOST_REQUIRE_NO_THROW(sKey.loadPkcs1Base64(sKeyPkcs1Base64, sKeyPkcs1Base64Len));
+  sKey.loadPkcs1Base64(sKeyPkcs1Base64, sKeyPkcs1Base64Len);
 
   // derive public key and compare
-  ConstBufferPtr pKeyBits= sKey.derivePublicKey();
+  ConstBufferPtr pKeyBits = sKey.derivePublicKey();
   OBufferStream os;
   bufferSource(dataSet.publicKeyPkcs8) >> base64Decode() >> streamSink(os);
   BOOST_CHECK_EQUAL_COLLECTIONS(pKeyBits->begin(), pKeyBits->end(),
@@ -332,9 +296,7 @@
   sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs1.c_str()),
                        dataSet.privateKeyPkcs1.size());
 
-  const uint8_t plainText[] = {
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
-  };
+  const uint8_t plainText[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
 
   const std::string cipherTextBase64 =
     "i2XNpZ2JbLa4JmBTdDrGmsd4/0C+p+BSCpW3MuPBNe5uChQ0eRO1dvjTnEqwSECY\n"
@@ -343,17 +305,15 @@
     "5PNsqlLXabSGr+jz4EwOsSCgPkiDf9U6tXoSPRA2/YvqFQdaiUXIVlomESvaqqZ8\n"
     "FxPs2BON0lobM8gT+xdzbRKofp+rNjNK+5uWyeOnXJwzCszh17cdJl2BH1dZwaVD\n"
     "PmTiSdeDQXZ94U5boDQ4Aw==\n";
-
   OBufferStream os;
   bufferSource(cipherTextBase64) >> base64Decode() >> streamSink(os);
 
-  ConstBufferPtr decryptText = sKey.decrypt(os.buf()->buf(), os.buf()->size());
-
+  auto decrypted = sKey.decrypt(os.buf()->buf(), os.buf()->size());
   BOOST_CHECK_EQUAL_COLLECTIONS(plainText, plainText + sizeof(plainText),
-                                decryptText->begin(), decryptText->end());
+                                decrypted->begin(), decrypted->end());
 }
 
-BOOST_AUTO_TEST_CASE(RsaEncryption)
+BOOST_AUTO_TEST_CASE(RsaEncryptDecrypt)
 {
   RsaKeyTestData dataSet;
 
@@ -365,23 +325,32 @@
   sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs1.c_str()),
                        dataSet.privateKeyPkcs1.size());
 
-  const uint8_t plainText[] = {
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
-  };
+  const uint8_t plainText[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
 
-  ConstBufferPtr cipherText = pKey.encrypt(plainText, sizeof(plainText));
-  ConstBufferPtr decryptText = sKey.decrypt(cipherText->buf(), cipherText->size());
-
+  auto cipherText = pKey.encrypt(plainText, sizeof(plainText));
+  auto decrypted = sKey.decrypt(cipherText->buf(), cipherText->size());
   BOOST_CHECK_EQUAL_COLLECTIONS(plainText, plainText + sizeof(plainText),
-                                decryptText->begin(), decryptText->end());
+                                decrypted->begin(), decrypted->end());
+}
+
+BOOST_AUTO_TEST_CASE(UnsupportedEcDecryption)
+{
+  EcKeyTestData dataSet;
+
+  PrivateKey sKey;
+  sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs1.c_str()),
+                       dataSet.privateKeyPkcs1.size());
+
+  OBufferStream os;
+  bufferSource("Y2lhbyFob2xhIWhlbGxvIQ==") >> base64Decode() >> streamSink(os);
+
+  BOOST_CHECK_THROW(sKey.decrypt(os.buf()->buf(), os.buf()->size()), PrivateKey::Error);
 }
 
 using KeyParams = boost::mpl::vector<RsaKeyParams, EcKeyParams>;
 
 BOOST_AUTO_TEST_CASE_TEMPLATE(GenerateKey, T, KeyParams)
 {
-  BOOST_REQUIRE_NO_THROW(generatePrivateKey(T()));
-
   unique_ptr<PrivateKey> sKey = generatePrivateKey(T());
   PublicKey pKey;
   ConstBufferPtr pKeyBits = sKey->derivePublicKey();
@@ -413,6 +382,11 @@
   BOOST_CHECK(*key1Pkcs1 != *key2Pkcs1);
 }
 
+BOOST_AUTO_TEST_CASE(UnsupportedKeyType)
+{
+  BOOST_CHECK_THROW(generatePrivateKey(AesKeyParams()), std::invalid_argument);
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestPrivateKey
 BOOST_AUTO_TEST_SUITE_END() // Transform
 BOOST_AUTO_TEST_SUITE_END() // Security