security: Change the ownership model of Pib and its related entities

Change-Id: I6816a0fd5c7af490f7e98db196e0214219f4b05c
Refs: #3349
diff --git a/src/security/pib/certificate-container.cpp b/src/security/pib/certificate-container.cpp
index fa8d058..e68b0cc 100644
--- a/src/security/pib/certificate-container.cpp
+++ b/src/security/pib/certificate-container.cpp
@@ -21,22 +21,31 @@
 
 #include "certificate-container.hpp"
 #include "pib-impl.hpp"
+#include "util/concepts.hpp"
 
 namespace ndn {
 namespace security {
 namespace pib {
 
+NDN_CXX_ASSERT_FORWARD_ITERATOR(CertificateContainer::const_iterator);
+
+CertificateContainer::const_iterator::const_iterator()
+  : m_container(nullptr)
+{
+}
+
 CertificateContainer::const_iterator::const_iterator(std::set<Name>::const_iterator it,
-                                                     shared_ptr<PibImpl> impl)
+                                                     const CertificateContainer& container)
   : m_it(it)
-  , m_impl(impl)
+  , m_container(&container)
 {
 }
 
 v2::Certificate
 CertificateContainer::const_iterator::operator*()
 {
-  return m_impl->getCertificate(*m_it);
+  BOOST_ASSERT(m_container != nullptr);
+  return m_container->get(*m_it);
 }
 
 CertificateContainer::const_iterator&
@@ -49,50 +58,52 @@
 CertificateContainer::const_iterator
 CertificateContainer::const_iterator::operator++(int)
 {
-  const_iterator it(m_it, m_impl);
+  BOOST_ASSERT(m_container != nullptr);
+  const_iterator it(m_it, *m_container);
   ++m_it;
   return it;
 }
 
 bool
-CertificateContainer::const_iterator::operator==(const const_iterator& other)
+CertificateContainer::const_iterator::operator==(const const_iterator& other) const
 {
-  return (m_impl == other.m_impl && m_it == other.m_it);
+  bool isThisEnd = m_container == nullptr || m_it == m_container->m_certNames.end();
+  bool isOtherEnd = other.m_container == nullptr || other.m_it == other.m_container->m_certNames.end();
+  return ((isThisEnd || isOtherEnd) ?
+          (isThisEnd == isOtherEnd) :
+          m_container->m_impl == other.m_container->m_impl && m_it == other.m_it);
 }
 
 bool
-CertificateContainer::const_iterator::operator!=(const const_iterator& other)
+CertificateContainer::const_iterator::operator!=(const const_iterator& other) const
 {
   return !(*this == other);
 }
 
-CertificateContainer::CertificateContainer()
-{
-}
-
-CertificateContainer::CertificateContainer(std::set<Name>&& certNames,
-                                           shared_ptr<PibImpl> impl)
-  : m_certNames(certNames)
+CertificateContainer::CertificateContainer(const Name& keyName, shared_ptr<PibImpl> impl)
+  : m_keyName(keyName)
   , m_impl(impl)
 {
+  BOOST_ASSERT(impl != nullptr);
+  m_certNames = impl->getCertificatesOfKey(keyName);
 }
 
 CertificateContainer::const_iterator
 CertificateContainer::begin() const
 {
-  return const_iterator(m_certNames.begin(), m_impl);
+  return const_iterator(m_certNames.begin(), *this);
 }
 
 CertificateContainer::const_iterator
 CertificateContainer::end() const
 {
-  return const_iterator(m_certNames.end(), m_impl);
+  return const_iterator();
 }
 
 CertificateContainer::const_iterator
 CertificateContainer::find(const Name& certName) const
 {
-  return const_iterator(m_certNames.find(certName), m_impl);
+  return const_iterator(m_certNames.find(certName), *this);
 }
 
 size_t
@@ -101,6 +112,57 @@
   return m_certNames.size();
 }
 
+void
+CertificateContainer::add(const v2::Certificate& certificate)
+{
+  if (m_keyName != certificate.getKeyName())
+    BOOST_THROW_EXCEPTION(std::invalid_argument("Certificate name `" + certificate.getKeyName().toUri() + "` "
+                                                "does not match key name"));
+
+  const Name& certName = certificate.getName();
+  m_certNames.insert(certName);
+  m_certs[certName] = certificate;
+  m_impl->addCertificate(certificate);
+}
+
+void
+CertificateContainer::remove(const Name& certName)
+{
+  if (!v2::Certificate::isValidName(certName) ||
+      v2::extractKeyNameFromCertName(certName) != m_keyName) {
+    BOOST_THROW_EXCEPTION(std::invalid_argument("Certificate name `" + certName.toUri() + "` "
+                                                "is invalid or does not match key name"));
+  }
+
+  m_certNames.erase(certName);
+  m_certs.erase(certName);
+  m_impl->removeCertificate(certName);
+}
+
+v2::Certificate
+CertificateContainer::get(const Name& certName) const
+{
+  auto it = m_certs.find(certName);
+
+  if (it != m_certs.end())
+    return it->second;
+
+  if (!v2::Certificate::isValidName(certName) ||
+      v2::extractKeyNameFromCertName(certName) != m_keyName) {
+    BOOST_THROW_EXCEPTION(std::invalid_argument("Certificate name `" + certName.toUri() + "` "
+                                                "is invalid or does not match key name"));
+  }
+
+  m_certs[certName] = m_impl->getCertificate(certName);
+  return m_certs[certName];
+}
+
+bool
+CertificateContainer::isConsistent() const
+{
+  return m_certNames == m_impl->getCertificatesOfKey(m_keyName);
+}
+
 } // namespace pib
 } // namespace security
 } // namespace ndn
diff --git a/src/security/pib/certificate-container.hpp b/src/security/pib/certificate-container.hpp
index eba765e..42e9534 100644
--- a/src/security/pib/certificate-container.hpp
+++ b/src/security/pib/certificate-container.hpp
@@ -23,6 +23,7 @@
 #define NDN_SECURITY_PIB_CERTIFICATE_CONTAINER_HPP
 
 #include <set>
+#include <unordered_map>
 #include "../v2/certificate.hpp"
 
 namespace ndn {
@@ -31,16 +32,24 @@
 
 class PibImpl;
 
-/// @brief A handler to search or enumerate certificates of a key.
-class CertificateContainer
+namespace detail {
+class KeyImpl;
+} // namespace detail
+
+/**
+ * @brief Container of certificates of a key
+ *
+ * The container is used to search/enumerate certificates of a key.
+ * The container can be created only by detail::KeyImpl.
+ */
+class CertificateContainer : noncopyable
 {
 public:
-  class const_iterator
+  class const_iterator : public std::iterator<std::forward_iterator_tag, const v2::Certificate>
   {
   public:
-    friend class CertificateContainer;
+    const_iterator();
 
-  public:
     v2::Certificate
     operator*();
 
@@ -51,26 +60,24 @@
     operator++(int);
 
     bool
-    operator==(const const_iterator& other);
+    operator==(const const_iterator& other) const;
 
     bool
-    operator!=(const const_iterator& other);
+    operator!=(const const_iterator& other) const;
 
   private:
-    const_iterator(std::set<Name>::const_iterator it, shared_ptr<PibImpl> impl);
+    const_iterator(std::set<Name>::const_iterator it, const CertificateContainer& container);
 
   private:
     std::set<Name>::const_iterator m_it;
-    shared_ptr<PibImpl> m_impl;
+    const CertificateContainer* m_container;
+
+    friend class CertificateContainer;
   };
 
   typedef const_iterator iterator;
 
 public:
-  CertificateContainer();
-
-  CertificateContainer(std::set<Name>&& certNames, shared_ptr<PibImpl> impl);
-
   const_iterator
   begin() const;
 
@@ -83,9 +90,64 @@
   size_t
   size() const;
 
+  /**
+   * @brief Add @p certificate into the container
+   * @throw std::invalid_argument the name of @p certificate does not match the key name
+   */
+  void
+  add(const v2::Certificate& certificate);
+
+  /**
+   * @brief Remove a certificate with @p certName from the container
+   * @throw std::invalid_argument @p certName does not match the key name
+   */
+  void
+  remove(const Name& certName);
+
+  /**
+   * @brief Get a certificate with @p certName from the container
+   * @throw std::invalid_argument @p certName does not match the key name
+   * @throw Pib::Error the certificate does not exist
+   */
+  v2::Certificate
+  get(const Name& certName) const;
+
+  /**
+   * @brief Check if the container is consistent with the backend storage
+   *
+   * @note this method is heavyweight and should be used in debugging mode only.
+   */
+  bool
+  isConsistent() const;
+
+NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  /**
+   * @brief Create certificate container for a key with @p keyName
+   * @param impl The PIB backend implementation.
+   */
+  CertificateContainer(const Name& keyName, shared_ptr<PibImpl> impl);
+
+  const std::set<Name>&
+  getCertNames() const
+  {
+    return m_certNames;
+  }
+
+  const std::unordered_map<Name, v2::Certificate>&
+  getCache() const
+  {
+    return m_certs;
+  }
+
 private:
+  Name m_keyName;
   std::set<Name> m_certNames;
+  /// @brief Cache of loaded certificates
+  mutable std::unordered_map<Name, v2::Certificate> m_certs;
+
   shared_ptr<PibImpl> m_impl;
+
+  friend class detail::KeyImpl;
 };
 
 } // namespace pib
diff --git a/src/security/pib/detail/identity-impl.cpp b/src/security/pib/detail/identity-impl.cpp
new file mode 100644
index 0000000..af01dc5
--- /dev/null
+++ b/src/security/pib/detail/identity-impl.cpp
@@ -0,0 +1,122 @@
+/* -*- 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).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "identity-impl.hpp"
+#include "../pib-impl.hpp"
+#include "../pib.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace detail {
+
+IdentityImpl::IdentityImpl(const Name& identityName, shared_ptr<PibImpl> impl, bool needInit)
+  : m_name(identityName)
+  , m_isDefaultKeyLoaded(false)
+  , m_keys(identityName, impl)
+  , m_impl(impl)
+{
+  BOOST_ASSERT(impl != nullptr);
+
+  if (needInit) {
+    m_impl->addIdentity(m_name);
+  }
+  else if (!m_impl->hasIdentity(m_name)) {
+    BOOST_THROW_EXCEPTION(Pib::Error("Identity " + m_name.toUri() + " does not exist"));
+  }
+}
+
+Key
+IdentityImpl::addKey(const uint8_t* key, size_t keyLen, const Name& keyName)
+{
+  BOOST_ASSERT(m_keys.isConsistent());
+
+  if (m_keys.find(keyName) != m_keys.end()) {
+    BOOST_THROW_EXCEPTION(Pib::Error("Cannot overwrite existing key " + keyName.toUri()));
+  }
+
+  return m_keys.add(key, keyLen, keyName);
+}
+
+void
+IdentityImpl::removeKey(const Name& keyName)
+{
+  BOOST_ASSERT(m_keys.isConsistent());
+
+  if (m_isDefaultKeyLoaded && m_defaultKey.getName() == keyName)
+    m_isDefaultKeyLoaded = false;
+
+  m_keys.remove(keyName);
+}
+
+Key
+IdentityImpl::getKey(const Name& keyName) const
+{
+  BOOST_ASSERT(m_keys.isConsistent());
+
+  return m_keys.get(keyName);
+}
+
+const KeyContainer&
+IdentityImpl::getKeys() const
+{
+  BOOST_ASSERT(m_keys.isConsistent());
+
+  return m_keys;
+}
+
+const Key&
+IdentityImpl::setDefaultKey(const Name& keyName)
+{
+  BOOST_ASSERT(m_keys.isConsistent());
+
+  m_defaultKey = m_keys.get(keyName);
+  m_isDefaultKeyLoaded = true;
+  m_impl->setDefaultKeyOfIdentity(m_name, keyName);
+  return m_defaultKey;
+}
+
+const Key&
+IdentityImpl::setDefaultKey(const uint8_t* key, size_t keyLen, const Name& keyName)
+{
+  addKey(key, keyLen, keyName);
+  return setDefaultKey(keyName);
+}
+
+const Key&
+IdentityImpl::getDefaultKey() const
+{
+  BOOST_ASSERT(m_keys.isConsistent());
+
+  if (!m_isDefaultKeyLoaded) {
+    m_defaultKey = m_keys.get(m_impl->getDefaultKeyOfIdentity(m_name));
+    m_isDefaultKeyLoaded = true;
+  }
+
+  BOOST_ASSERT(m_impl->getDefaultKeyOfIdentity(m_name) == m_defaultKey.getName());
+
+  return m_defaultKey;
+}
+
+} // namespace detail
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/src/security/pib/detail/identity-impl.hpp b/src/security/pib/detail/identity-impl.hpp
new file mode 100644
index 0000000..b654348
--- /dev/null
+++ b/src/security/pib/detail/identity-impl.hpp
@@ -0,0 +1,138 @@
+/* -*- 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).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_SECURITY_PIB_DETAIL_IDENTITY_IMPL_HPP
+#define NDN_SECURITY_PIB_DETAIL_IDENTITY_IMPL_HPP
+
+#include "../key-container.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+
+class PibImpl;
+
+namespace detail {
+
+/**
+ * @brief Backend instance of Identity
+ *
+ * An Identity has only one backend instance, but may have multiple frontend handles.
+ * Each frontend handle is associated with the only one backend IdentityImpl.
+ *
+ * @throw PibImpl::Error when underlying implementation has non-semantic error.
+ */
+class IdentityImpl : noncopyable
+{
+public:
+  /**
+   * @brief Create an Identity with @p identityName.
+   *
+   * @param identityName The name of the Identity.
+   * @param impl The PIB backend implementation.
+   * @param needInit If true, create the identity in backend when the identity does not exist.
+   *                 Otherwise, throw Pib::Error when the identity does not exist.
+   */
+  IdentityImpl(const Name& identityName, shared_ptr<PibImpl> impl, bool needInit = false);
+
+  /// @brief Get the name of the identity.
+  const Name&
+  getName() const
+  {
+    return m_name;
+  }
+
+  /**
+   * @brief Add a @p key of @p keyLen bytes with @p keyName (in PKCS#8 format).
+   *
+   * If no default key is set before, the new key will be set as the default key of the identity.
+   *
+   * @return the added key.
+   * @throw std::invalid_argument key name does not match identity
+   * @throw Pib::Error a key with the same name already exists
+   */
+  Key
+  addKey(const uint8_t* key, size_t keyLen, const Name& keyName);
+
+  /**
+   * @brief Remove a key with @p keyName
+   * @throw std::invalid_argument @p keyName does not match identity
+   */
+  void
+  removeKey(const Name& keyName);
+
+  /**
+   * @brief Get a key with id @p keyName.
+   *
+   * @throw std::invalid_argument @p keyName does not match identity
+   * @throw Pib::Error the key does not exist.
+   */
+  Key
+  getKey(const Name& keyName) const;
+
+  /**
+   * @brief Get all keys for this Identity.
+   */
+  const KeyContainer&
+  getKeys() const;
+
+  /**
+   * @brief Set the key with id @p keyName.
+   * @throw std::invalid_argument @p keyName does not match identity
+   * @throw Pib::Error the key does not exist.
+   * @return The default key
+   */
+  const Key&
+  setDefaultKey(const Name& keyName);
+
+  /**
+   * @brief Add @p key of @p keyLen bytes with @p keyName and set it as the default key
+   * @throw std::invalid_argument @p keyName does not match identity
+   * @throw Pib::Error the key with the same name already exists
+   * @return the default key
+   */
+  const Key&
+  setDefaultKey(const uint8_t* key, size_t keyLen, const Name& keyName);
+
+  /**
+   * @brief Get the default key for this Identity.
+   * @throw Pib::Error the default key does not exist.
+   */
+  const Key&
+  getDefaultKey() const;
+
+private:
+  Name m_name;
+
+  mutable bool m_isDefaultKeyLoaded;
+  mutable Key m_defaultKey;
+
+  mutable KeyContainer m_keys;
+
+  shared_ptr<PibImpl> m_impl;
+};
+
+} // namespace detail
+} // namespace pib
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_SECURITY_PIB_DETAIL_IDENTITY_IMPL_HPP
diff --git a/src/security/pib/detail/key-impl.cpp b/src/security/pib/detail/key-impl.cpp
new file mode 100644
index 0000000..35a0de6
--- /dev/null
+++ b/src/security/pib/detail/key-impl.cpp
@@ -0,0 +1,149 @@
+/* -*- 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).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "key-impl.hpp"
+#include "../pib-impl.hpp"
+#include "../pib.hpp"
+#include "../../transform/public-key.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace detail {
+
+KeyImpl::KeyImpl(const Name& keyName, const uint8_t* key, size_t keyLen, shared_ptr<PibImpl> impl)
+  : m_identity(v2::extractIdentityFromKeyName(keyName))
+  , m_keyName(keyName)
+  , m_key(key, keyLen)
+  , m_isDefaultCertificateLoaded(false)
+  , m_certificates(keyName, impl)
+  , m_impl(impl)
+{
+  BOOST_ASSERT(impl != nullptr);
+
+  if (m_impl->hasKey(m_keyName)) {
+    BOOST_THROW_EXCEPTION(Pib::Error("Cannot overwrite existing key " + m_keyName.toUri()));
+  }
+
+  transform::PublicKey publicKey;
+  try {
+    publicKey.loadPkcs8(key, keyLen);
+  }
+  catch (transform::PublicKey::Error&) {
+    BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid key bits"));
+  }
+  m_keyType = publicKey.getKeyType();
+
+  m_impl->addKey(m_identity, m_keyName, key, keyLen);
+}
+
+KeyImpl::KeyImpl(const Name& keyName, shared_ptr<PibImpl> impl)
+  : m_identity(v2::extractIdentityFromKeyName(keyName))
+  , m_keyName(keyName)
+  , m_isDefaultCertificateLoaded(false)
+  , m_certificates(keyName, impl)
+  , m_impl(impl)
+{
+  BOOST_ASSERT(impl != nullptr);
+
+  m_key = m_impl->getKeyBits(m_keyName);
+
+  transform::PublicKey key;
+  key.loadPkcs8(m_key.buf(), m_key.size());
+  m_keyType = key.getKeyType();
+}
+
+void
+KeyImpl::addCertificate(const v2::Certificate& certificate)
+{
+  BOOST_ASSERT(m_certificates.isConsistent());
+
+  if (m_certificates.find(certificate.getName()) != m_certificates.end()) {
+    BOOST_THROW_EXCEPTION(Pib::Error("Cannot overwrite existing certificate " + certificate.getName().toUri()));
+  }
+
+  m_certificates.add(certificate);
+}
+
+void
+KeyImpl::removeCertificate(const Name& certName)
+{
+  BOOST_ASSERT(m_certificates.isConsistent());
+
+  if (m_isDefaultCertificateLoaded && m_defaultCertificate.getName() == certName)
+    m_isDefaultCertificateLoaded = false;
+
+  m_certificates.remove(certName);
+}
+
+v2::Certificate
+KeyImpl::getCertificate(const Name& certName) const
+{
+  BOOST_ASSERT(m_certificates.isConsistent());
+
+  return m_certificates.get(certName);
+}
+
+const CertificateContainer&
+KeyImpl::getCertificates() const
+{
+  BOOST_ASSERT(m_certificates.isConsistent());
+
+  return m_certificates;
+}
+
+const v2::Certificate&
+KeyImpl::setDefaultCertificate(const Name& certName)
+{
+  BOOST_ASSERT(m_certificates.isConsistent());
+
+  m_defaultCertificate = m_certificates.get(certName);
+  m_impl->setDefaultCertificateOfKey(m_keyName, certName);
+  m_isDefaultCertificateLoaded = true;
+  return m_defaultCertificate;
+}
+
+const v2::Certificate&
+KeyImpl::setDefaultCertificate(const v2::Certificate& certificate)
+{
+  addCertificate(certificate);
+  return setDefaultCertificate(certificate.getName());
+}
+
+const v2::Certificate&
+KeyImpl::getDefaultCertificate() const
+{
+  BOOST_ASSERT(m_certificates.isConsistent());
+
+  if (!m_isDefaultCertificateLoaded) {
+    m_defaultCertificate = m_impl->getDefaultCertificateOfKey(m_keyName);
+    m_isDefaultCertificateLoaded = true;
+  }
+
+  BOOST_ASSERT(m_impl->getDefaultCertificateOfKey(m_keyName).wireEncode() == m_defaultCertificate.wireEncode());
+
+  return m_defaultCertificate;
+}
+
+} // namespace detail
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/src/security/pib/detail/key-impl.hpp b/src/security/pib/detail/key-impl.hpp
new file mode 100644
index 0000000..c05e77e
--- /dev/null
+++ b/src/security/pib/detail/key-impl.hpp
@@ -0,0 +1,179 @@
+/* -*- 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).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_SECURITY_PIB_DETAIL_KEY_IMPL_HPP
+#define NDN_SECURITY_PIB_DETAIL_KEY_IMPL_HPP
+
+#include "../../../data.hpp"
+#include "../certificate-container.hpp"
+#include "../../security-common.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+
+class PibImpl;
+
+namespace detail {
+
+/**
+ * @brief Backend instance of Key
+ *
+ * An Key has only one backend instance, but may have multiple frontend handles.
+ * Each frontend handle is associated with the only one backend KeyImpl.
+ *
+ * @throw PibImpl::Error when underlying implementation has non-semantic error.
+ */
+class KeyImpl : noncopyable
+{
+public:
+  /**
+   * @brief Create a KeyImpl with @p keyName.
+   *
+   * If the key does not exist in the backend, create it in backend.
+   *
+   * @param keyName The name of the key.
+   * @param key The public key to add.
+   * @param keyLen The length of the key.
+   * @param impl The Pib backend implementation.
+   * @throw Pib::Error a key with the same @p keyName already exists.
+   */
+  KeyImpl(const Name& keyName, const uint8_t* key, size_t keyLen, shared_ptr<PibImpl> impl);
+
+  /**
+   * @brief Create a KeyImpl with @p keyName.
+   *
+   * @param keyName The name of the key.
+   * @param impl The Pib backend implementation.
+   * @throw Pib::Error the key does not exist.
+   */
+  KeyImpl(const Name& keyName, shared_ptr<PibImpl> impl);
+
+  /// @brief Get the name of the key.
+  const Name&
+  getName() const
+  {
+    return m_keyName;
+  }
+
+  /**
+   * @brief Get the name of the belonging identity.
+   */
+  const Name&
+  getIdentity() const
+  {
+    return m_identity;
+  }
+
+  /**
+   * @brief Get key type
+   */
+  KeyType
+  getKeyType() const
+  {
+    return m_keyType;
+  }
+
+  /**
+   * @brief Get public key bits
+   */
+  const Buffer&
+  getPublicKey() const
+  {
+    return m_key;
+  }
+
+  /**
+   * @brief Add @p certificate.
+   *
+   * If no default certificate is set before, the new certificate will be set as the default
+   * certificate of the key.
+   *
+   * @throw std::invalid_argument certificate name does not match key name
+   * @throw Pib::Error a certificate with the same name already exists
+   */
+  void
+  addCertificate(const v2::Certificate& certificate);
+
+  /**
+   * @brief Remove a certificate with @p certName
+   * @throw std::invalid_argument @p certName does not match key name
+   */
+  void
+  removeCertificate(const Name& certName);
+
+  /**
+   * @brief Get a certificate with @p certName
+   * @throw std::invalid_argument @p certName does not match key name
+   * @throw Pib::Error the certificate does not exist.
+   */
+  v2::Certificate
+  getCertificate(const Name& certName) const;
+
+  /// @brief Get all the certificates for this key.
+  const CertificateContainer&
+  getCertificates() const;
+
+  /**
+   * @brief Set an existing one with @p certName as the default certificate
+   * @throw std::invalid_argument @p certName does not match key name
+   * @throw Pib::Error the certificate does not exist.
+   * @return the default certificate
+   */
+  const v2::Certificate&
+  setDefaultCertificate(const Name& certName);
+
+  /**
+   * @brief Add @p certificate and set it as the default certificate of the key
+   * @throw std::invalid_argument @p certificate does not match key name
+   * @throw Pib::Error the certificate with the same name already exists.
+   * @return the default certificate
+   */
+  const v2::Certificate&
+  setDefaultCertificate(const v2::Certificate& certificate);
+
+  /**
+   * @brief Get the default certificate for this Key.
+   * @throw Pib::Error the default certificate does not exist.
+   */
+  const v2::Certificate&
+  getDefaultCertificate() const;
+
+private:
+  Name m_identity;
+  Name m_keyName;
+  Buffer m_key;
+  KeyType m_keyType;
+
+  mutable bool m_isDefaultCertificateLoaded;
+  mutable v2::Certificate m_defaultCertificate;
+
+  CertificateContainer m_certificates;
+
+  shared_ptr<PibImpl> m_impl;
+};
+
+} // namespace detail
+} // namespace pib
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_SECURITY_PIB_DETAIL_KEY_IMPL_HPP
diff --git a/src/security/pib/identity-container.cpp b/src/security/pib/identity-container.cpp
index 77076af..7c018ce 100644
--- a/src/security/pib/identity-container.cpp
+++ b/src/security/pib/identity-container.cpp
@@ -21,22 +21,32 @@
 
 #include "identity-container.hpp"
 #include "pib-impl.hpp"
+#include "detail/identity-impl.hpp"
+#include "util/concepts.hpp"
 
 namespace ndn {
 namespace security {
 namespace pib {
 
+NDN_CXX_ASSERT_FORWARD_ITERATOR(IdentityContainer::const_iterator);
+
+IdentityContainer::const_iterator::const_iterator()
+  : m_container(nullptr)
+{
+}
+
 IdentityContainer::const_iterator::const_iterator(std::set<Name>::const_iterator it,
-                                                  shared_ptr<PibImpl> impl)
+                                                  const IdentityContainer& container)
   : m_it(it)
-  , m_impl(impl)
+  , m_container(&container)
 {
 }
 
 Identity
 IdentityContainer::const_iterator::operator*()
 {
-  return Identity(*m_it, m_impl);
+  BOOST_ASSERT(m_container != nullptr);
+  return m_container->get(*m_it);
 }
 
 IdentityContainer::const_iterator&
@@ -57,7 +67,11 @@
 bool
 IdentityContainer::const_iterator::operator==(const const_iterator& other)
 {
-  return (m_impl == other.m_impl && m_it == other.m_it);
+  bool isThisEnd = m_container == nullptr || m_it == m_container->m_identityNames.end();
+  bool isOtherEnd = other.m_container == nullptr || other.m_it == other.m_container->m_identityNames.end();
+  return ((isThisEnd || isOtherEnd) ?
+          (isThisEnd == isOtherEnd) :
+          m_container->m_pibImpl == other.m_container->m_pibImpl && m_it == other.m_it);
 }
 
 bool
@@ -66,39 +80,83 @@
   return !(*this == other);
 }
 
-IdentityContainer::IdentityContainer()
+IdentityContainer::IdentityContainer(shared_ptr<PibImpl> pibImpl)
+  : m_pibImpl(pibImpl)
 {
-}
-
-IdentityContainer::IdentityContainer(std::set<Name>&& identities,
-                                     shared_ptr<PibImpl> impl)
-  : m_identities(identities)
-  , m_impl(impl)
-{
+  BOOST_ASSERT(pibImpl != nullptr);
+  m_identityNames = pibImpl->getIdentities();
 }
 
 IdentityContainer::const_iterator
 IdentityContainer::begin() const
 {
-  return const_iterator(m_identities.begin(), m_impl);
+  return const_iterator(m_identityNames.begin(), *this);
 }
 
 IdentityContainer::const_iterator
 IdentityContainer::end() const
 {
-  return const_iterator(m_identities.end(), m_impl);
+  return const_iterator();
 }
 
 IdentityContainer::const_iterator
 IdentityContainer::find(const Name& identity) const
 {
-  return const_iterator(m_identities.find(identity), m_impl);
+  return const_iterator(m_identityNames.find(identity), *this);
 }
 
 size_t
 IdentityContainer::size() const
 {
-  return m_identities.size();
+  return m_identityNames.size();
+}
+
+Identity
+IdentityContainer::add(const Name& identityName)
+{
+  if (m_identityNames.count(identityName) == 0) {
+    m_identityNames.insert(identityName);
+    m_identities[identityName] =
+      shared_ptr<detail::IdentityImpl>(new detail::IdentityImpl(identityName, m_pibImpl, true));
+  }
+  return get(identityName);
+}
+
+void
+IdentityContainer::remove(const Name& identityName)
+{
+  m_identityNames.erase(identityName);
+  m_identities.erase(identityName);
+  m_pibImpl->removeIdentity(identityName);
+}
+
+Identity
+IdentityContainer::get(const Name& identityName) const
+{
+  shared_ptr<detail::IdentityImpl> id;
+  auto it = m_identities.find(identityName);
+
+  if (it != m_identities.end()) {
+    id = it->second;
+  }
+  else {
+    id = shared_ptr<detail::IdentityImpl>(new detail::IdentityImpl(identityName, m_pibImpl, false));
+    m_identities[identityName] = id;
+  }
+  return Identity(id);
+}
+
+void
+IdentityContainer::reset()
+{
+  m_identities.clear();
+  m_identityNames = m_pibImpl->getIdentities();
+}
+
+bool
+IdentityContainer::isConsistent() const
+{
+  return m_identityNames == m_pibImpl->getIdentities();
 }
 
 } // namespace pib
diff --git a/src/security/pib/identity-container.hpp b/src/security/pib/identity-container.hpp
index 1ac30e1..94cd370 100644
--- a/src/security/pib/identity-container.hpp
+++ b/src/security/pib/identity-container.hpp
@@ -22,25 +22,35 @@
 #ifndef NDN_SECURITY_PIB_IDENTITY_CONTAINER_HPP
 #define NDN_SECURITY_PIB_IDENTITY_CONTAINER_HPP
 
-#include <set>
 #include "identity.hpp"
 
+#include <set>
+#include <unordered_map>
+
 namespace ndn {
 namespace security {
 namespace pib {
 
 class PibImpl;
 
-/// @brief A handler to search or enumerate identities in PIB.
-class IdentityContainer
+namespace detail {
+class IdentityImpl;
+} // namespace detail
+
+/**
+ * @brief Container of identities of a Pib
+ *
+ * The container is used to search/enumerate identities of a Pib.
+ * The container can be created only by Pib.
+ */
+class IdentityContainer : noncopyable
 {
 public:
-  class const_iterator
+  class const_iterator : public std::iterator<std::forward_iterator_tag, const Identity>
   {
   public:
-    friend class IdentityContainer;
+    const_iterator();
 
-  public:
     Identity
     operator*();
 
@@ -57,21 +67,18 @@
     operator!=(const const_iterator& other);
 
   private:
-    const_iterator(std::set<Name>::const_iterator it, shared_ptr<PibImpl> impl);
+    const_iterator(std::set<Name>::const_iterator it, const IdentityContainer& container);
 
   private:
-    Name m_identity;
     std::set<Name>::const_iterator m_it;
-    shared_ptr<PibImpl> m_impl;
+    const IdentityContainer* m_container;
+
+    friend class IdentityContainer;
   };
 
   typedef const_iterator iterator;
 
 public:
-  IdentityContainer();
-
-  IdentityContainer(std::set<Name>&& identities, shared_ptr<PibImpl> impl);
-
   const_iterator
   begin() const;
 
@@ -84,9 +91,69 @@
   size_t
   size() const;
 
+  /**
+   * @brief Add @p identity into the container
+   */
+  Identity
+  add(const Name& identityName);
+
+  /**
+   * @brief Remove @p identity from the container
+   */
+  void
+  remove(const Name& identity);
+
+  /**
+   * @brief Get @p identity from the container
+   * @throw Pib::Error @p identity does not exist
+   */
+  Identity
+  get(const Name& identity) const;
+
+  /**
+   * @brief Reset state of the container
+   *
+   * This method removes all loaded identities and retrieves identity names from the PIB
+   * implementation.
+   */
+  void
+  reset();
+
+  /**
+   * @brief Check if the container is consistent with the backend storage
+   *
+   * @note this method is heavyweight and should be used in debugging mode only.
+   */
+  bool
+  isConsistent() const;
+
+NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  /**
+   * @brief Create identity container
+   * @param impl The PIB backend implementation.
+   */
+  explicit
+  IdentityContainer(shared_ptr<PibImpl> pibImpl);
+
+  const std::set<Name>&
+  getIdentityNames() const
+  {
+    return m_identityNames;
+  }
+
+  const std::unordered_map<Name, shared_ptr<detail::IdentityImpl>>&
+  getLoadedIdentities() const
+  {
+    return m_identities;
+  }
+
 private:
-  std::set<Name> m_identities;
-  shared_ptr<PibImpl> m_impl;
+  std::set<Name> m_identityNames;
+  /// @brief Cache of loaded detail::IdentityImpl.
+  mutable std::unordered_map<Name, shared_ptr<detail::IdentityImpl>> m_identities;
+
+  shared_ptr<PibImpl> m_pibImpl;
+  friend class Pib;
 };
 
 } // namespace pib
diff --git a/src/security/pib/identity.cpp b/src/security/pib/identity.cpp
index 11cad88..4797cb6 100644
--- a/src/security/pib/identity.cpp
+++ b/src/security/pib/identity.cpp
@@ -20,123 +20,65 @@
  */
 
 #include "identity.hpp"
-#include "pib-impl.hpp"
-#include "pib.hpp"
+#include "detail/identity-impl.hpp"
 
 namespace ndn {
 namespace security {
 namespace pib {
 
-Identity::Identity()
-  : m_hasDefaultKey(false)
-  , m_needRefreshKeys(false)
-  , m_impl(nullptr)
-{
-}
+Identity::Identity() = default;
 
-Identity::Identity(const Name& identityName, shared_ptr<PibImpl> impl, bool needInit)
-  : m_name(identityName)
-  , m_hasDefaultKey(false)
-  , m_needRefreshKeys(true)
-  , m_impl(impl)
+Identity::Identity(weak_ptr<detail::IdentityImpl> impl)
+  : m_impl(impl)
 {
-  validityCheck();
-
-  if (needInit)
-    m_impl->addIdentity(m_name);
-  else if (!m_impl->hasIdentity(m_name))
-    BOOST_THROW_EXCEPTION(Pib::Error("Identity: " + m_name.toUri() + " does not exist"));
 }
 
 const Name&
 Identity::getName() const
 {
-  return m_name;
+  return lock()->getName();
 }
 
 Key
 Identity::addKey(const uint8_t* key, size_t keyLen, const Name& keyName)
 {
-  if (m_name != v2::extractIdentityFromKeyName(keyName)) {
-    BOOST_THROW_EXCEPTION(Pib::Error("Key name `" + keyName.toUri() + "` does not match identity "
-                                     "`" + m_name.toUri() + "`"));
-  }
-
-  // if we have already loaded all the keys, but the new key is not one of them the
-  // KeyContainer should be refreshed
-  m_needRefreshKeys = m_needRefreshKeys || m_keys.find(keyName) == m_keys.end();
-
-  return Key(keyName, key, keyLen, m_impl);
+  return lock()->addKey(key, keyLen, keyName);
 }
 
 void
 Identity::removeKey(const Name& keyName)
 {
-  if (m_name != v2::extractIdentityFromKeyName(keyName)) {
-    BOOST_THROW_EXCEPTION(Pib::Error("Key name `" + keyName.toUri() + "` does not match identity "
-                                     "`" + m_name.toUri() + "`"));
-  }
-
-  validityCheck();
-
-  if (m_hasDefaultKey && m_defaultKey.getName() == keyName)
-    m_hasDefaultKey = false;
-
-  m_impl->removeKey(keyName);
-  m_needRefreshKeys = true;
+  return lock()->removeKey(keyName);
 }
 
 Key
 Identity::getKey(const Name& keyName) const
 {
-  return Key(keyName, m_impl);
+  return lock()->getKey(keyName);
 }
 
 const KeyContainer&
 Identity::getKeys() const
 {
-  validityCheck();
-
-  if (m_needRefreshKeys) {
-    m_keys = KeyContainer(m_name, m_impl->getKeysOfIdentity(m_name), m_impl);
-    m_needRefreshKeys = false;
-  }
-
-  return m_keys;
+  return lock()->getKeys();
 }
 
-Key&
+const Key&
 Identity::setDefaultKey(const Name& keyName)
 {
-  validityCheck();
-
-  m_defaultKey = Key(keyName, m_impl);
-  m_hasDefaultKey = true;
-
-  m_impl->setDefaultKeyOfIdentity(m_name, keyName);
-  return m_defaultKey;
+  return lock()->setDefaultKey(keyName);
 }
 
-Key&
+const Key&
 Identity::setDefaultKey(const uint8_t* key, size_t keyLen, const Name& keyName)
 {
-  validityCheck();
-
-  addKey(key, keyLen, keyName);
-  return setDefaultKey(keyName);
+  return lock()->setDefaultKey(key, keyLen, keyName);
 }
 
-Key&
+const Key&
 Identity::getDefaultKey() const
 {
-  validityCheck();
-
-  if (!m_hasDefaultKey) {
-    m_defaultKey = Key(m_impl->getDefaultKeyOfIdentity(m_name), m_impl);
-    m_hasDefaultKey = true;
-  }
-
-  return m_defaultKey;
+  return lock()->getDefaultKey();
 }
 
 Identity::operator bool() const
@@ -147,15 +89,18 @@
 bool
 Identity::operator!() const
 {
-  return (m_impl == nullptr);
+  return m_impl.expired();
 }
 
-void
-Identity::validityCheck() const
+shared_ptr<detail::IdentityImpl>
+Identity::lock() const
 {
-  if (m_impl == nullptr) {
-    BOOST_THROW_EXCEPTION(std::domain_error("Invalid identity instance"));
-  }
+  auto impl = m_impl.lock();
+
+  if (impl == nullptr)
+    BOOST_THROW_EXCEPTION(std::domain_error("Invalid Identity instance"));
+
+  return impl;
 }
 
 } // namespace pib
diff --git a/src/security/pib/identity.hpp b/src/security/pib/identity.hpp
index 0a6ca60..3d0117d 100644
--- a/src/security/pib/identity.hpp
+++ b/src/security/pib/identity.hpp
@@ -28,14 +28,16 @@
 namespace security {
 namespace pib {
 
+namespace detail {
+class IdentityImpl;
+} // namespace detail
+
 /**
- * @brief represents an identity
+ * @brief A frontend handle of an Identity
  *
  * Identity is at the top level in PIB's Identity-Key-Certificate hierarchy.  An identity has a
  * Name, and contains zero or more keys, at most one of which is set as the default key of this
  * identity.  Properties of a key can be accessed after obtaining a Key object.
- *
- * @throw Pib::Error when underlying implementation has non-semantic error.
  */
 class Identity
 {
@@ -44,7 +46,7 @@
    * @brief Default Constructor
    *
    * Identity created using this default constructor is just a place holder.
-   * It must obtain an actual instance from Pib::getIdentity(...).  A typical
+   * It can obtain an actual instance from Pib::getIdentity(...).  A typical
    * usage would be for exception handling:
    *
    *   Identity id;
@@ -61,105 +63,97 @@
   Identity();
 
   /**
-   * @brief Create an Identity with @p identityName.
+   * @brief Create an Identity with a backend implementation @p impl.
    *
-   * @param identityName The name of the Identity.
-   * @param impl The backend implementation.
-   * @param needInit If true, create the identity in backend when the identity does not exist.
-   *                 Otherwise, throw Pib::Error when the identity does not exist.
+   * This method should only be used by IdentityContainer.
    */
-  Identity(const Name& identityName, shared_ptr<PibImpl> impl, bool needInit = false);
+  explicit
+  Identity(weak_ptr<detail::IdentityImpl> impl);
 
-  /// @brief Get the name of the identity.
+  /**
+   * @brief Get the name of the identity.
+   */
   const Name&
   getName() const;
 
   /**
-   * @brief Get a key with @p keyName.
-   * @throw Pib::Error if the key does not exist.
+   * @brief Get a key with id @p keyName.
+   * @throw std::invalid_argument @p keyName does not match identity
+   * @throw Pib::Error the key does not exist.
    */
   Key
   getKey(const Name& keyName) const;
 
-  /// @brief Get all the keys for this Identity.
+  /**
+   * @brief Get all keys for this identity.
+   */
   const KeyContainer&
   getKeys() const;
 
   /**
    * @brief Get the default key for this Identity.
-   *
-   * @throws Pib::Error if the default key does not exist.
+   * @throw Pib::Error the default key does not exist.
    */
-  Key&
+  const Key&
   getDefaultKey() const;
 
-  /// @brief Check if the Identity instance is valid
+  /*
+   * @return True if the identity instance is valid
+   */
   operator bool() const;
 
-  /// @brief Check if the Identity instance is invalid
+  /**
+   * @return True if the identity instance is invalid
+   */
   bool
   operator!() const;
 
 NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE: // write operations should be private
   /**
-   * @brief Add a @p key (in PKCS#8 format) with @p keyName.
-   *
-   * If the key already exists, do nothing.
-   *
-   * If no default key is set before, the new key will be set as the default key of the identity.
-   *
-   * @return the added key or existing key with the same key id.
+   * @brief Add a @p key of @p keyLen bytes (in PKCS#8 format) with @p keyName.
+   * @return the handle of added key
+   * @throw std::invalid_argument key name does not match identity
+   * @throw Pib::Error a key with the same name already exists
    */
   Key
   addKey(const uint8_t* key, size_t keyLen, const Name& keyName);
 
   /**
    * @brief Remove a key with @p keyName
+   * @throw std::invalid_argument @p keyName does not match identity
    */
   void
   removeKey(const Name& keyName);
 
   /**
-   * @brief Set the key with id @p keyName.
-   *
+   * @brief Set an existing key with @p keyName as the default key.
+   * @throw std::invalid_argument @p keyName does not match identity
+   * @throw Pib::Error the key does not exist.
    * @return The default key
-   * @throws Pib::Error if the key does not exist.
    */
-  Key&
+  const Key&
   setDefaultKey(const Name& keyName);
 
   /**
-   * @brief Set the default key with @p keyName (in PKCS#8 format).
-   *
-   * If the key does not exist, add the key and set it as the default of the Identity.
-   * If the key exists, simply set it as the default key of the Identity.
-   *
-   * @param key The public key to add.
-   * @param keyLen The length of the key.
+   * @brief Add a @p key of @p keyLen bytes with @p keyName and set it as the default key
+   * @throw std::invalid_argument @p keyName does not match identity
+   * @throw Pib::Error the key with the same name already exists.
    * @return the default key
    */
-  Key&
+  const Key&
   setDefaultKey(const uint8_t* key, size_t keyLen, const Name& keyName);
 
-NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+private:
   /**
-   * @brief Check the validity of this instance
-   *
-   * @throws std::domain_error if the instance is invalid
+   * @brief Check the validity of the instance
+   * @return a shared_ptr when the instance is valid
+   * @throw std::domain_error the instance is invalid
    */
-  void
-  validityCheck() const;
+  shared_ptr<detail::IdentityImpl>
+  lock() const;
 
 private:
-  Name m_name;
-
-  mutable bool m_hasDefaultKey;
-  mutable Key m_defaultKey;
-
-  mutable bool m_needRefreshKeys;
-  mutable KeyContainer m_keys;
-
-  shared_ptr<PibImpl> m_impl;
+  weak_ptr<detail::IdentityImpl> m_impl;
 };
 
 } // namespace pib
diff --git a/src/security/pib/key-container.cpp b/src/security/pib/key-container.cpp
index 7653955..34f96d0 100644
--- a/src/security/pib/key-container.cpp
+++ b/src/security/pib/key-container.cpp
@@ -21,24 +21,32 @@
 
 #include "key-container.hpp"
 #include "pib-impl.hpp"
+#include "detail/key-impl.hpp"
+#include "util/concepts.hpp"
 
 namespace ndn {
 namespace security {
 namespace pib {
 
-KeyContainer::const_iterator::const_iterator(const Name& identity,
-                                             std::set<Name>::const_iterator it,
-                                             shared_ptr<PibImpl> impl)
-  : m_identity(identity)
-  , m_it(it)
-  , m_impl(impl)
+NDN_CXX_ASSERT_FORWARD_ITERATOR(KeyContainer::const_iterator);
+
+KeyContainer::const_iterator::const_iterator()
+  : m_container(nullptr)
+{
+}
+
+KeyContainer::const_iterator::const_iterator(std::set<Name>::const_iterator it,
+                                             const KeyContainer& container)
+  : m_it(it)
+  , m_container(&container)
 {
 }
 
 Key
 KeyContainer::const_iterator::operator*()
 {
-  return Key(*m_it, m_impl);
+  BOOST_ASSERT(m_container != nullptr);
+  return m_container->get(*m_it);
 }
 
 KeyContainer::const_iterator&
@@ -59,7 +67,11 @@
 bool
 KeyContainer::const_iterator::operator==(const const_iterator& other)
 {
-  return (m_impl == other.m_impl && m_identity == other.m_identity && m_it == other.m_it);
+  bool isThisEnd = m_container == nullptr || m_it == m_container->m_keyNames.end();
+  bool isOtherEnd = other.m_container == nullptr || other.m_it == other.m_container->m_keyNames.end();
+  return ((isThisEnd || isOtherEnd) ?
+          (isThisEnd == isOtherEnd) :
+          m_container->m_impl == other.m_container->m_impl && m_it == other.m_it);
 }
 
 bool
@@ -68,33 +80,30 @@
   return !(*this == other);
 }
 
-KeyContainer::KeyContainer()
-{
-}
-
-KeyContainer::KeyContainer(const Name& identity, std::set<Name>&& keyNames, shared_ptr<PibImpl> impl)
+KeyContainer::KeyContainer(const Name& identity, shared_ptr<PibImpl> impl)
   : m_identity(identity)
-  , m_keyNames(keyNames)
   , m_impl(impl)
 {
+  BOOST_ASSERT(impl != nullptr);
+  m_keyNames = impl->getKeysOfIdentity(identity);
 }
 
 KeyContainer::const_iterator
 KeyContainer::begin() const
 {
-  return const_iterator(m_identity, m_keyNames.begin(), m_impl);
+  return const_iterator(m_keyNames.begin(), *this);
 }
 
 KeyContainer::const_iterator
 KeyContainer::end() const
 {
-  return const_iterator(m_identity, m_keyNames.end(), m_impl);
+  return const_iterator();
 }
 
 KeyContainer::const_iterator
 KeyContainer::find(const Name& keyName) const
 {
-  return const_iterator(m_identity, m_keyNames.find(keyName), m_impl);
+  return const_iterator(m_keyNames.find(keyName), *this);
 }
 
 size_t
@@ -103,6 +112,63 @@
   return m_keyNames.size();
 }
 
+Key
+KeyContainer::add(const uint8_t* key, size_t keyLen, const Name& keyName)
+{
+  if (m_identity != v2::extractIdentityFromKeyName(keyName)) {
+    BOOST_THROW_EXCEPTION(std::invalid_argument("Key name `" + keyName.toUri() + "` does not match identity "
+                                                "`" + m_identity.toUri() + "`"));
+  }
+
+  if (m_keyNames.count(keyName) == 0) {
+    m_keyNames.insert(keyName);
+    m_keys[keyName] = shared_ptr<detail::KeyImpl>(new detail::KeyImpl(keyName, key, keyLen, m_impl));
+  }
+
+  return get(keyName);
+}
+
+void
+KeyContainer::remove(const Name& keyName)
+{
+  if (m_identity != v2::extractIdentityFromKeyName(keyName)) {
+    BOOST_THROW_EXCEPTION(std::invalid_argument("Key name `" + keyName.toUri() + "` does not match identity "
+                                                "`" + m_identity.toUri() + "`"));
+  }
+
+  m_keyNames.erase(keyName);
+  m_keys.erase(keyName);
+  m_impl->removeKey(keyName);
+}
+
+Key
+KeyContainer::get(const Name& keyName) const
+{
+  if (m_identity != v2::extractIdentityFromKeyName(keyName)) {
+    BOOST_THROW_EXCEPTION(std::invalid_argument("Key name `" + keyName.toUri() + "` does not match identity "
+                                                "`" + m_identity.toUri() + "`"));
+  }
+
+  shared_ptr<detail::KeyImpl> key;
+  auto it = m_keys.find(keyName);
+
+  if (it != m_keys.end()) {
+    key = it->second;
+  }
+  else {
+    key = shared_ptr<detail::KeyImpl>(new detail::KeyImpl(keyName, m_impl));
+    m_keys[keyName] = key;
+  }
+
+  return Key(key);
+}
+
+bool
+KeyContainer::isConsistent() const
+{
+  return m_keyNames == m_impl->getKeysOfIdentity(m_identity);
+}
+
 } // namespace pib
 } // namespace security
 } // namespace ndn
diff --git a/src/security/pib/key-container.hpp b/src/security/pib/key-container.hpp
index a593132..45d9127 100644
--- a/src/security/pib/key-container.hpp
+++ b/src/security/pib/key-container.hpp
@@ -23,6 +23,7 @@
 #define NDN_SECURITY_PIB_KEY_CONTAINER_HPP
 
 #include <set>
+#include <unordered_map>
 #include "key.hpp"
 
 namespace ndn {
@@ -31,13 +32,25 @@
 
 class PibImpl;
 
-/// @brief A handler to search or enumerate keys of an identity.
-class KeyContainer
+namespace detail {
+class KeyImpl;
+class IdentityImpl;
+} // namespace detail
+
+/**
+ * @brief Container of keys of an identity
+ *
+ * The container is used to search/enumerate keys of an identity.
+ * The container can be created only by detail::IdentityImpl.
+ */
+class KeyContainer : noncopyable
 {
 public:
-  class const_iterator
+  class const_iterator : public std::iterator<std::forward_iterator_tag, const Key>
   {
   public:
+    const_iterator();
+
     Key
     operator*();
 
@@ -54,12 +67,11 @@
     operator!=(const const_iterator& other);
 
   private:
-    const_iterator(const Name& identity, std::set<Name>::const_iterator it, shared_ptr<PibImpl> impl);
+    const_iterator(std::set<Name>::const_iterator it, const KeyContainer& container);
 
   private:
-    Name m_identity;
     std::set<Name>::const_iterator m_it;
-    shared_ptr<PibImpl> m_impl;
+    const KeyContainer* m_container;
 
     friend class KeyContainer;
   };
@@ -67,10 +79,6 @@
   typedef const_iterator iterator;
 
 public:
-  KeyContainer();
-
-  KeyContainer(const Name& identity, std::set<Name>&& keyNames, shared_ptr<PibImpl> impl);
-
   const_iterator
   begin() const;
 
@@ -83,10 +91,63 @@
   size_t
   size() const;
 
+  /**
+   * @brief Add @p key of @p keyLen bytes with @p keyName into the container
+   * @throw std::invalid_argument @p keyName does not match the identity
+   */
+  Key
+  add(const uint8_t* key, size_t keyLen, const Name& keyName);
+
+  /**
+   * @brief Remove a key with @p keyName from the container
+   * @throw std::invalid_argument @p keyName does not match the identity
+   */
+  void
+  remove(const Name& keyName);
+
+  /**
+   * @brief Get a key with @p keyName from the container
+   * @throw std::invalid_argument @p keyName does not match the identity
+   * @throw Pib::Error the key does not exist
+   */
+  Key
+  get(const Name& keyName) const;
+
+  /**
+   * @brief Check if the container is consistent with the backend storage
+   *
+   * @note this method is heavyweight and should be used in debugging mode only.
+   */
+  bool
+  isConsistent() const;
+
+NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  /**
+   * @brief Create key container for @p identity
+   * @param impl The PIB backend implementation.
+   */
+  KeyContainer(const Name& identity, shared_ptr<PibImpl> impl);
+
+  const std::set<Name>&
+  getKeyNames() const
+  {
+    return m_keyNames;
+  }
+
+  const std::unordered_map<Name, shared_ptr<detail::KeyImpl>>&
+  getLoadedKeys() const
+  {
+    return m_keys;
+  }
+
 private:
   Name m_identity;
   std::set<Name> m_keyNames;
+  /// @brief Cache of loaded detail::KeyImpl.
+  mutable std::unordered_map<Name, shared_ptr<detail::KeyImpl>> m_keys;
+
   shared_ptr<PibImpl> m_impl;
+  friend class detail::IdentityImpl;
 };
 
 } // namespace pib
diff --git a/src/security/pib/key.cpp b/src/security/pib/key.cpp
index 5890a9c..b4e6daf 100644
--- a/src/security/pib/key.cpp
+++ b/src/security/pib/key.cpp
@@ -20,161 +20,84 @@
  */
 
 #include "key.hpp"
-#include "pib-impl.hpp"
-#include "pib.hpp"
+#include "detail/key-impl.hpp"
 #include "../v2/certificate.hpp"
-#include "../transform/public-key.hpp"
 
 namespace ndn {
 namespace security {
 namespace pib {
 
-Key::Key()
-  : m_keyType(KeyType::NONE)
-  , m_hasDefaultCertificate(false)
-  , m_needRefreshCerts(false)
-  , m_impl(nullptr)
+Key::Key() = default;
+
+Key::Key(weak_ptr<detail::KeyImpl> impl)
+  : m_impl(impl)
 {
 }
 
-Key::Key(const Name& keyName, const uint8_t* key, size_t keyLen, shared_ptr<PibImpl> impl)
-  : m_keyName(keyName)
-  , m_key(key, keyLen)
-  , m_hasDefaultCertificate(false)
-  , m_needRefreshCerts(true)
-  , m_impl(impl)
-{
-  validityCheck();
-
-  m_identity = v2::extractIdentityFromKeyName(keyName);
-  m_impl->addIdentity(m_identity);
-  m_impl->addKey(m_identity, m_keyName, key, keyLen);
-
-  transform::PublicKey publicKey;
-  publicKey.loadPkcs8(key, keyLen);
-  m_keyType = publicKey.getKeyType();
-}
-
-Key::Key(const Name& keyName, shared_ptr<PibImpl> impl)
-  : m_keyName(keyName)
-  , m_hasDefaultCertificate(false)
-  , m_needRefreshCerts(true)
-  , m_impl(impl)
-{
-  validityCheck();
-
-  m_identity = v2::extractIdentityFromKeyName(keyName);
-  m_key = m_impl->getKeyBits(m_keyName);
-
-  transform::PublicKey key;
-  key.loadPkcs8(m_key.buf(), m_key.size());
-  m_keyType = key.getKeyType();
-}
-
 const Name&
 Key::getName() const
 {
-  validityCheck();
-
-  return m_keyName;
+  return lock()->getName();
 }
 
 const Name&
 Key::getIdentity() const
 {
-  validityCheck();
+  return lock()->getIdentity();
+}
 
-  return m_identity;
+KeyType
+Key::getKeyType() const
+{
+  return lock()->getKeyType();
 }
 
 const Buffer&
 Key::getPublicKey() const
 {
-  validityCheck();
-
-  return m_key;
+  return lock()->getPublicKey();
 }
 
 void
 Key::addCertificate(const v2::Certificate& certificate)
 {
-  validityCheck();
-
-  if (certificate.getKeyName() != m_keyName)
-    BOOST_THROW_EXCEPTION(Pib::Error("Certificate name does not match key name"));
-
-  if (!m_needRefreshCerts &&
-      m_certificates.find(certificate.getName()) == m_certificates.end()) {
-    // if we have already loaded all the certificate, but the new certificate is not one of them
-    // the CertificateContainer should be refreshed
-    m_needRefreshCerts = true;
-  }
-
-  m_impl->addCertificate(certificate);
+  return lock()->addCertificate(certificate);
 }
 
 void
 Key::removeCertificate(const Name& certName)
 {
-  validityCheck();
-
-  if (m_hasDefaultCertificate && m_defaultCertificate.getName() == certName)
-    m_hasDefaultCertificate = false;
-
-  m_impl->removeCertificate(certName);
-  m_needRefreshCerts = true;
+  return lock()->removeCertificate(certName);
 }
 
 v2::Certificate
 Key::getCertificate(const Name& certName) const
 {
-  validityCheck();
-
-  return m_impl->getCertificate(certName);
+  return lock()->getCertificate(certName);
 }
 
 const CertificateContainer&
 Key::getCertificates() const
 {
-  validityCheck();
-
-  if (m_needRefreshCerts) {
-    m_certificates = CertificateContainer(m_impl->getCertificatesOfKey(m_keyName), m_impl);
-    m_needRefreshCerts = false;
-  }
-
-  return m_certificates;
+  return lock()->getCertificates();
 }
 
 const v2::Certificate&
 Key::setDefaultCertificate(const Name& certName)
 {
-  validityCheck();
-
-  m_impl->setDefaultCertificateOfKey(m_keyName, certName);
-  m_defaultCertificate = m_impl->getCertificate(certName);
-  m_hasDefaultCertificate = true;
-  return m_defaultCertificate;
+  return lock()->setDefaultCertificate(certName);
 }
 
 const v2::Certificate&
 Key::setDefaultCertificate(const v2::Certificate& certificate)
 {
-  addCertificate(certificate);
-  return setDefaultCertificate(certificate.getName());
+  return lock()->setDefaultCertificate(certificate);
 }
 
 const v2::Certificate&
 Key::getDefaultCertificate() const
 {
-  validityCheck();
-
-  if (!m_hasDefaultCertificate) {
-    m_defaultCertificate = m_impl->getDefaultCertificateOfKey(m_keyName);
-    m_hasDefaultCertificate = true;
-  }
-
-  return m_defaultCertificate;
+  return lock()->getDefaultCertificate();
 }
 
 Key::operator bool() const
@@ -185,14 +108,19 @@
 bool
 Key::operator!() const
 {
-  return (m_impl == nullptr);
+  return m_impl.expired();
 }
 
-void
-Key::validityCheck() const
+shared_ptr<detail::KeyImpl>
+Key::lock() const
 {
-  if (m_impl == nullptr)
-    BOOST_THROW_EXCEPTION(std::domain_error("Invalid Key instance"));
+  auto impl = m_impl.lock();
+
+  if (impl == nullptr) {
+    BOOST_THROW_EXCEPTION(std::domain_error("Invalid key instance"));
+  }
+
+  return impl;
 }
 
 } // namespace pib
diff --git a/src/security/pib/key.hpp b/src/security/pib/key.hpp
index d99e427..69ceec8 100644
--- a/src/security/pib/key.hpp
+++ b/src/security/pib/key.hpp
@@ -22,7 +22,6 @@
 #ifndef NDN_SECURITY_PIB_KEY_HPP
 #define NDN_SECURITY_PIB_KEY_HPP
 
-#include "../../data.hpp"
 #include "certificate-container.hpp"
 #include "../security-common.hpp"
 
@@ -33,10 +32,12 @@
 
 namespace pib {
 
-class PibImpl;
+namespace detail {
+class KeyImpl;
+} // namespace detail
 
 /**
- * @brief represents a key
+ * @brief A frontend handle of a key instance
  *
  * Key is at the second level in PIB's Identity-Key-Certificate hierarchy.  A Key has a Name
  * (identity + "KEY" + keyId), and contains one or more certificates, one of which is set as
@@ -50,7 +51,7 @@
    * @brief Default Constructor
    *
    * Key created using this default constructor is just a place holder.
-   * It must obtain an actual instance from Identity::getKey(...).  A typical
+   * It can obtain an actual instance from Identity::getKey(...).  A typical
    * usage would be for exception handling:
    *
    *   Key key;
@@ -67,134 +68,114 @@
   Key();
 
   /**
-   * @brief Create a Key with @p keyName
+   * @brief Create a Key with a backend implementation @p impl.
    *
-   * If the key/identity does not exist in the backend, create it in backend.
-   *
-   * @param keyName Key name
-   * @param key The public key to add.
-   * @param keyLen The length of the key.
-   * @param impl The actual backend implementation.
+   * This method should only be used by KeyContainer.
    */
-  Key(const Name& keyName, const uint8_t* key, size_t keyLen, shared_ptr<PibImpl> impl);
+  explicit
+  Key(weak_ptr<detail::KeyImpl> impl);
 
-  /**
-   * @brief Create a Key with @p keyName
-   *
-   * @param keyName Key name
-   * @param impl The actual backend implementation.
-   * @throws Pib::Error if the key does not exist.
+  /*
+   * @brief Get key name.
    */
-  Key(const Name& keyName, shared_ptr<PibImpl> impl);
-
-  /// @brief Get the key name.
   const Name&
   getName() const;
 
-  /// @brief Get the name of the belonging identity.
+  /**
+   * @brief Get the name of the belonging identity.
+   */
   const Name&
   getIdentity() const;
 
-  /// @brief Get key type.
+  /**
+   * @brief Get key type.
+   */
   KeyType
-  getKeyType() const
-  {
-    return m_keyType;
-  }
+  getKeyType() const;
 
-  /// @brief Get public key.
+  /**
+   * @brief Get public key bits.
+   */
   const Buffer&
   getPublicKey() const;
 
   /**
-   * @brief Get a certificate.
-   *
-   * @return the certificate
-   * @throws Pib::Error if the certificate does not exist.
+   * @brief Get a certificate with @p certName
+   * @throw std::invalid_argument @p certName does not match key name
+   * @throw Pib::Error the certificate does not exist.
    */
   v2::Certificate
   getCertificate(const Name& certName) const;
 
-  /// @brief Get all certificates for this key.
+  /**
+   * @brief Get all certificates for this key.
+   */
   const CertificateContainer&
   getCertificates() const;
 
   /**
    * @brief Get the default certificate for this Key.
-   *
-   * @throws Pib::Error if the default certificate does not exist.
+   * @throw Pib::Error the default certificate does not exist.
    */
   const v2::Certificate&
   getDefaultCertificate() const;
 
-  /// @brief Check if the Key instance is valid
+  /**
+   * @brief Check if the Key instance is valid.
+   */
   operator bool() const;
 
-  /// @brief Check if the Key instance is invalid
+  /**
+   * @brief Check if the Key instance is invalid.
+   */
   bool
   operator!() const;
 
 NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE: // write operations should be private
   /**
-   * @brief Add a certificate.
-   *
-   * @param certificate The certificate to add.
+   * @brief Add @p certificate.
+   * @throw std::invalid_argument certificate name does not match key name
+   * @throw Pib::Error a certificate with the same name already exists
    */
   void
   addCertificate(const v2::Certificate& certificate);
 
   /**
-   * @brief Remove a certificate.
-   *
-   * @param certName The name of the certificate to delete.
+   * @brief Remove a certificate with @p certName
+   * @throw std::invalid_argument @p certName does not match key name
    */
   void
   removeCertificate(const Name& certName);
 
   /**
-   * @brief Set the default certificate.
-   *
-   * @param certName The name of the default certificate of the key.
+   * @brief Set an existing certificate with @p certName as the default certificate
+   * @throw std::invalid_argument @p certName does not match key name
+   * @throw Pib::Error the certificate does not exist.
    * @return the default certificate
-   * @throws Pib::Error if the certificate does not exist.
    */
   const v2::Certificate&
   setDefaultCertificate(const Name& certName);
 
   /**
-   * @brief Set the default certificate.
-   *
-   * If the certificate does not exist, add it and set it as the default certificate of the key.
-   * If the certificate exists, simply set it as the default certificate of the key.
-   *
-   * @param certificate The certificate to add.
+   * @brief Add @p certificate and set it as the default certificate of the key
+   * @throw std::invalid_argument @p certificate does not match key name
+   * @throw Pib::Error the certificate with the same name already exists.
    * @return the default certificate
    */
   const v2::Certificate&
   setDefaultCertificate(const v2::Certificate& certificate);
 
-NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+private:
   /**
-   * @brief Check the validity of this instance
-   *
-   * @throws std::domain_error if the instance is invalid
+   * @brief Check the validity of the instance
+   * @return a shared_ptr when the instance is valid
+   * @throw std::domain_error the instance is invalid
    */
-  void
-  validityCheck() const;
+  shared_ptr<detail::KeyImpl>
+  lock() const;
 
 private:
-  Name m_identity;
-  Name m_keyName;
-  Buffer m_key;
-  KeyType m_keyType;
-
-  mutable bool m_hasDefaultCertificate;
-  mutable v2::Certificate m_defaultCertificate;
-
-  mutable bool m_needRefreshCerts;
-  mutable CertificateContainer m_certificates;
-
-  shared_ptr<PibImpl> m_impl;
+  weak_ptr<detail::KeyImpl> m_impl;
 };
 
 } // namespace pib
diff --git a/src/security/pib/pib-impl.hpp b/src/security/pib/pib-impl.hpp
index ef859a6..dc384a8 100644
--- a/src/security/pib/pib-impl.hpp
+++ b/src/security/pib/pib-impl.hpp
@@ -22,6 +22,7 @@
 #ifndef NDN_SECURITY_PIB_PIB_IMPL_HPP
 #define NDN_SECURITY_PIB_PIB_IMPL_HPP
 
+#include "pib.hpp"
 #include <set>
 #include "../v2/certificate.hpp"
 
@@ -131,7 +132,7 @@
    * @brief Get the default identity.
    *
    * @return The name for the default identity.
-   * @throws Pib::Error if no default identity.
+   * @throw Pib::Error no default identity.
    */
   virtual Name
   getDefaultIdentity() const = 0;
@@ -173,7 +174,7 @@
    * @brief Get the key bits of a key with name @p keyName.
    *
    * @return key bits
-   * @throws Pib::Error if the key does not exist.
+   * @throw Pib::Error the key does not exist.
    */
   virtual Buffer
   getKeyBits(const Name& keyName) const = 0;
@@ -192,7 +193,7 @@
   /**
    * @brief Set an key with @p keyName as the default key of an identity with name @p identity.
    *
-   * @throws Pib::Error if the key does not exist.
+   * @throw Pib::Error the key does not exist.
    */
   virtual void
   setDefaultKeyOfIdentity(const Name& identity, const Name& keyName) = 0;
@@ -200,7 +201,7 @@
   /**
    * @return The name of the default key of an identity with name @p identity.
    *
-   * @throws Pib::Error if no default key or the identity does not exist.
+   * @throw Pib::Error no default key or the identity does not exist.
    */
   virtual Name
   getDefaultKeyOfIdentity(const Name& identity) const = 0;
@@ -245,7 +246,7 @@
    *
    * @param certName The name of the certificate.
    * @return the certificate.
-   * @throws Pib::Error if the certificate does not exist.
+   * @throw Pib::Error the certificate does not exist.
    */
   virtual v2::Certificate
   getCertificate(const Name& certName) const = 0;
@@ -264,7 +265,7 @@
   /**
    * @brief Set a cert with name @p certName as the default of a key with @p keyName.
    *
-   * @throws Pib::Error if the certificate with name @p certName does not exist.
+   * @throw Pib::Error the certificate with name @p certName does not exist.
    */
   virtual void
   setDefaultCertificateOfKey(const Name& keyName, const Name& certName) = 0;
@@ -272,7 +273,7 @@
   /**
    * @return Get the default certificate of a key with @p keyName.
    *
-   * @throws Pib::Error if the default certificate does not exist.
+   * @throw Pib::Error the default certificate does not exist.
    */
   virtual v2::Certificate
   getDefaultCertificateOfKey(const Name& keyName) const = 0;
diff --git a/src/security/pib/pib-sqlite3.hpp b/src/security/pib/pib-sqlite3.hpp
index 8c47176..5571657 100644
--- a/src/security/pib/pib-sqlite3.hpp
+++ b/src/security/pib/pib-sqlite3.hpp
@@ -49,7 +49,7 @@
    *
    * @param dir The directory where the database file is located. By default, it points to the
    *        $HOME/.ndn directory.
-   * @throws PibImpl::Error when initialization fails.
+   * @throw PibImpl::Error when initialization fails.
    */
   explicit
   PibSqlite3(const std::string& dir = "");
diff --git a/src/security/pib/pib.cpp b/src/security/pib/pib.cpp
index a29018e..6c5f316 100644
--- a/src/security/pib/pib.cpp
+++ b/src/security/pib/pib.cpp
@@ -29,10 +29,11 @@
 Pib::Pib(const std::string& scheme, const std::string& location, shared_ptr<PibImpl> impl)
   : m_scheme(scheme)
   , m_location(location)
-  , m_hasDefaultIdentity(false)
-  , m_needRefreshIdentities(true)
+  , m_isDefaultIdentityLoaded(false)
+  , m_identities(impl)
   , m_impl(impl)
 {
+  BOOST_ASSERT(impl != nullptr);
 }
 
 Pib::~Pib() = default;
@@ -68,67 +69,69 @@
 {
   m_impl->clearIdentities();
   m_impl->setTpmLocator("");
-
-  m_hasDefaultIdentity = false;
-  m_needRefreshIdentities = true;
+  m_isDefaultIdentityLoaded = false;
+  m_identities.reset();
 }
 
 Identity
 Pib::addIdentity(const Name& identity)
 {
-  if (!m_needRefreshIdentities && m_identities.find(identity) == m_identities.end()) {
-    // if we have already loaded all the identities, but the new identity is not one of them
-    // the IdentityContainer should be refreshed
-    m_needRefreshIdentities = true;
-  }
-  return Identity(identity, m_impl, true);
+  BOOST_ASSERT(m_identities.isConsistent());
+
+  return m_identities.add(identity);
 }
 
 void
 Pib::removeIdentity(const Name& identity)
 {
-  if (m_hasDefaultIdentity && m_defaultIdentity.getName() == identity)
-    m_hasDefaultIdentity = false;
+  BOOST_ASSERT(m_identities.isConsistent());
 
-  m_impl->removeIdentity(identity);
-  m_needRefreshIdentities = true;
+  if (m_isDefaultIdentityLoaded && m_defaultIdentity.getName() == identity)
+    m_isDefaultIdentityLoaded = false;
+
+  m_identities.remove(identity);
 }
 
 Identity
 Pib::getIdentity(const Name& identity) const
 {
-  return Identity(identity, m_impl, false);
+  BOOST_ASSERT(m_identities.isConsistent());
+
+  return m_identities.get(identity);
 }
 
 const IdentityContainer&
 Pib::getIdentities() const
 {
-  if (m_needRefreshIdentities) {
-    m_identities = IdentityContainer(m_impl->getIdentities(), m_impl);
-    m_needRefreshIdentities = false;
-  }
+  BOOST_ASSERT(m_identities.isConsistent());
 
   return m_identities;
 }
 
-Identity&
+const Identity&
 Pib::setDefaultIdentity(const Name& identityName)
 {
-  m_defaultIdentity = addIdentity(identityName);
-  m_hasDefaultIdentity = true;
+  BOOST_ASSERT(m_identities.isConsistent());
+
+  m_defaultIdentity = m_identities.add(identityName);
+  m_isDefaultIdentityLoaded = true;
 
   m_impl->setDefaultIdentity(identityName);
   return m_defaultIdentity;
 }
 
-Identity&
+const Identity&
 Pib::getDefaultIdentity() const
 {
-  if (!m_hasDefaultIdentity) {
-    m_defaultIdentity = Identity(m_impl->getDefaultIdentity(), m_impl, false);
-    m_hasDefaultIdentity = true;
+  BOOST_ASSERT(m_identities.isConsistent());
+
+  if (!m_isDefaultIdentityLoaded) {
+    m_defaultIdentity = m_identities.get(m_impl->getDefaultIdentity());
+    m_isDefaultIdentityLoaded = true;
   }
 
+  BOOST_ASSERT(m_impl->getDefaultIdentity() == m_defaultIdentity.getName());
+
   return m_defaultIdentity;
 }
 
diff --git a/src/security/pib/pib.hpp b/src/security/pib/pib.hpp
index 06f100d..47dbd5c 100644
--- a/src/security/pib/pib.hpp
+++ b/src/security/pib/pib.hpp
@@ -51,9 +51,6 @@
 class Pib : noncopyable
 {
 public:
-  friend class KeyChain;
-
-public:
   /// @brief represents a semantic error
   class Error : public std::runtime_error
   {
@@ -107,8 +104,6 @@
 
   /**
    * @brief Get an identity with name @p identityName.
-   *
-   * @param identityName The name for the identity to get.
    * @throw Pib::Error if the identity does not exist.
    */
   Identity
@@ -120,15 +115,22 @@
 
   /**
    * @brief Get the default identity.
-   *
-   * @return the default identity.
-   * @throws Pib::Error if no default identity.
+   * @throw Pib::Error if no default identity.
    */
-  Identity&
+  const Identity&
   getDefaultIdentity() const;
 
 NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE: // write operations should be private
   /*
+   * @brief Create a Pib instance
+   *
+   * @param scheme The scheme for the Pib
+   * @param location The location for the Pib
+   * @param impl The backend implementation
+   */
+  Pib(const std::string& scheme, const std::string& location, shared_ptr<PibImpl> impl);
+
+  /*
    * @brief Create an identity with name @p identityName and return a reference to it.
    *
    * If there already exists an identity for the name @p identityName, then it is returned.
@@ -136,39 +138,34 @@
    *
    * @param identityName The name for the identity to be added
    */
-  Identity
-  addIdentity(const Name& identityName);
-
-  /*
-   * @brief Remove an identity with name @p identityName.
-   *
-   * If the default identity is being removed, no default identity will be selected.
-   *
-   * @param identityName The name for the identity to be deleted
-   */
-  void
-  removeIdentity(const Name& identityName);
 
   /**
-   * @brief Set an identity with name @p identityName as the default identity.
+   * @brief Add an @p identity.
    *
-   * Also create the identity if it does not exist.
+   * If no default identity is set before, the new identity will be set as the default identity
    *
-   * @param identityName The name for the default identity.
-   * @return the default identity
+   * @return handle of the added identity.
    */
-  Identity&
-  setDefaultIdentity(const Name& identityName);
+  Identity
+  addIdentity(const Name& identity);
 
-NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   /*
-   * @brief Create a new Pib with the specified @p location
+   * @brief Remove an @p identity.
    *
-   * @param scheme The scheme for the Pib
-   * @param location The location for the Pib
-   * @param impl The backend implementation
+   * If the default identity is being removed, no default identity will be selected.
    */
-  Pib(const std::string& scheme, const std::string& location, shared_ptr<PibImpl> impl);
+  void
+  removeIdentity(const Name& identity);
+
+  /**
+   * @brief Set an @p identity as the default identity.
+   *
+   * Create the identity if it does not exist.
+   *
+   * @return handle of the default identity
+   */
+  const Identity&
+  setDefaultIdentity(const Name& identity);
 
   shared_ptr<PibImpl>
   getImpl()
@@ -180,13 +177,14 @@
   std::string m_scheme;
   std::string m_location;
 
-  mutable bool m_hasDefaultIdentity;
+  mutable bool m_isDefaultIdentityLoaded;
   mutable Identity m_defaultIdentity;
 
-  mutable bool m_needRefreshIdentities;
-  mutable IdentityContainer m_identities;
+  IdentityContainer m_identities;
 
   shared_ptr<PibImpl> m_impl;
+
+  friend class KeyChain;
 };
 
 } // namespace pib
diff --git a/src/util/concepts.hpp b/src/util/concepts.hpp
index 8d4567e..a8c7fdd 100644
--- a/src/util/concepts.hpp
+++ b/src/util/concepts.hpp
@@ -1,6 +1,12 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2015 Regents of the University of California.
+ * Copyright (c) 2014-2017,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -96,6 +102,41 @@
   }
 };
 
+// NDN_CXX_ASSERT_DEFAULT_CONSTRUCTIBLE and NDN_CXX_ASSERT_FORWARD_ITERATOR
+// originally written as part of NFD codebase
+
+namespace detail {
+
+// As of Boost 1.54.0, the internal implementation of BOOST_CONCEPT_ASSERT does not allow
+// multiple assertions on the same line, so we have to combine multiple concepts together.
+
+template<typename T>
+class StlForwardIteratorConcept : public boost::ForwardIterator<T>
+                                , public boost::DefaultConstructible<T>
+{
+};
+
+} // namespace detail
+
+/** \brief assert T is default constructible
+ *  \sa http://en.cppreference.com/w/cpp/concept/DefaultConstructible
+ */
+#define NDN_CXX_ASSERT_DEFAULT_CONSTRUCTIBLE(T) \
+  static_assert(std::is_default_constructible<T>::value, \
+                #T " must be default-constructible"); \
+  BOOST_CONCEPT_ASSERT((boost::DefaultConstructible<T>))
+
+/** \brief assert T is a forward iterator
+ *  \sa http://en.cppreference.com/w/cpp/concept/ForwardIterator
+ *  \note A forward iterator should be default constructible, but boost::ForwardIterator follows
+ *        SGI standard which doesn't require DefaultConstructible, so a separate check is needed.
+ */
+#define NDN_CXX_ASSERT_FORWARD_ITERATOR(T) \
+  BOOST_CONCEPT_ASSERT((::ndn::detail::StlForwardIteratorConcept<T>)); \
+  static_assert(std::is_default_constructible<T>::value, \
+                #T " must be default-constructible")
+
+
 } // namespace ndn
 
 #endif // NDN_UTIL_CONCEPTS_HPP
diff --git a/tests/unit-tests/security/pib/certificate-container.t.cpp b/tests/unit-tests/security/pib/certificate-container.t.cpp
index 5edbfb2..4eeaa4a 100644
--- a/tests/unit-tests/security/pib/certificate-container.t.cpp
+++ b/tests/unit-tests/security/pib/certificate-container.t.cpp
@@ -35,25 +35,100 @@
 
 BOOST_AUTO_TEST_SUITE(Security)
 BOOST_AUTO_TEST_SUITE(Pib)
-BOOST_AUTO_TEST_SUITE(TestCertificateContainer)
+BOOST_FIXTURE_TEST_SUITE(TestCertificateContainer, PibDataFixture)
 
 using pib::Pib;
 
-BOOST_FIXTURE_TEST_CASE(TestCertificateContainer, PibDataFixture)
+BOOST_AUTO_TEST_CASE(Basic)
 {
   auto pibImpl = make_shared<PibMemory>();
-  Pib pib("pib-memory", "", pibImpl);
 
-  Identity identity1 = pib.addIdentity(id1);
-  Key key11 = identity1.addKey(id1Key1.buf(), id1Key1.size(), id1Key1Name);
-  key11.addCertificate(id1Key1Cert1);
-  key11.addCertificate(id1Key1Cert2);
+  // start with an empty container
+  CertificateContainer container(id1Key1Name, pibImpl);
+  BOOST_CHECK_EQUAL(container.size(), 0);
+  BOOST_CHECK_EQUAL(container.getCache().size(), 0);
 
-  CertificateContainer container = key11.getCertificates();
+  // add one cert
+  container.add(id1Key1Cert1);
+  BOOST_CHECK_EQUAL(container.size(), 1);
+  BOOST_CHECK_EQUAL(container.getCache().size(), 1);
+  BOOST_CHECK(container.find(id1Key1Cert1.getName()) != container.end());
+
+  // add the same cert again
+  container.add(id1Key1Cert1);
+  BOOST_CHECK_EQUAL(container.size(), 1);
+  BOOST_CHECK_EQUAL(container.getCache().size(), 1);
+  BOOST_CHECK(container.find(id1Key1Cert1.getName()) != container.end());
+
+  // add another cert
+  container.add(id1Key1Cert2);
   BOOST_CHECK_EQUAL(container.size(), 2);
+  BOOST_CHECK_EQUAL(container.getCache().size(), 2);
   BOOST_CHECK(container.find(id1Key1Cert1.getName()) != container.end());
   BOOST_CHECK(container.find(id1Key1Cert2.getName()) != container.end());
 
+  // get certs
+  BOOST_REQUIRE_NO_THROW(container.get(id1Key1Cert1.getName()));
+  BOOST_REQUIRE_NO_THROW(container.get(id1Key1Cert2.getName()));
+  Name id1Key1Cert3Name = id1Key1Name;
+  id1Key1Cert3Name.append("issuer").appendVersion(3);
+  BOOST_CHECK_THROW(container.get(id1Key1Cert3Name), Pib::Error);
+
+  // check cert
+  v2::Certificate cert1 = container.get(id1Key1Cert1.getName());
+  v2::Certificate cert2 = container.get(id1Key1Cert2.getName());
+  BOOST_CHECK_EQUAL(cert1, id1Key1Cert1);
+  BOOST_CHECK_EQUAL(cert2, id1Key1Cert2);
+
+  // create another container from the same PibImpl
+  // cache should be empty
+  CertificateContainer container2(id1Key1Name, pibImpl);
+  BOOST_CHECK_EQUAL(container2.size(), 2);
+  BOOST_CHECK_EQUAL(container2.getCache().size(), 0);
+
+  // get certificate, cache should be filled
+  BOOST_REQUIRE_NO_THROW(container2.get(id1Key1Cert1.getName()));
+  BOOST_CHECK_EQUAL(container2.size(), 2);
+  BOOST_CHECK_EQUAL(container2.getCache().size(), 1);
+
+  BOOST_REQUIRE_NO_THROW(container2.get(id1Key1Cert2.getName()));
+  BOOST_CHECK_EQUAL(container2.size(), 2);
+  BOOST_CHECK_EQUAL(container2.getCache().size(), 2);
+
+  // remove a certificate
+  container2.remove(id1Key1Cert1.getName());
+  BOOST_CHECK_EQUAL(container2.size(), 1);
+  BOOST_CHECK_EQUAL(container2.getCache().size(), 1);
+  BOOST_CHECK(container2.find(id1Key1Cert1.getName()) == container2.end());
+  BOOST_CHECK(container2.find(id1Key1Cert2.getName()) != container2.end());
+
+  // remove another certificate
+  container2.remove(id1Key1Cert2.getName());
+  BOOST_CHECK_EQUAL(container2.size(), 0);
+  BOOST_CHECK_EQUAL(container2.getCache().size(), 0);
+  BOOST_CHECK(container2.find(id1Key1Cert2.getName()) == container2.end());
+}
+
+BOOST_AUTO_TEST_CASE(Errors)
+{
+  auto pibImpl = make_shared<PibMemory>();
+
+  CertificateContainer container(id1Key1Name, pibImpl);
+
+  BOOST_CHECK_THROW(container.add(id1Key2Cert1), std::invalid_argument);
+  BOOST_CHECK_THROW(container.remove(id1Key2Cert1.getName()), std::invalid_argument);
+  BOOST_CHECK_THROW(container.get(id1Key2Cert1.getName()), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(Iterator)
+{
+  auto pibImpl = make_shared<PibMemory>();
+
+  // start with an empty container
+  CertificateContainer container(id1Key1Name, pibImpl);
+  container.add(id1Key1Cert1);
+  container.add(id1Key1Cert2);
+
   std::set<Name> certNames;
   certNames.insert(id1Key1Cert1.getName());
   certNames.insert(id1Key1Cert2.getName());
@@ -76,6 +151,10 @@
     count++;
   }
   BOOST_CHECK_EQUAL(count, 2);
+
+  BOOST_CHECK(CertificateContainer::const_iterator() == CertificateContainer::const_iterator());
+  BOOST_CHECK(CertificateContainer::const_iterator() == container.end());
+  BOOST_CHECK(container.end() == CertificateContainer::const_iterator());
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestCertificateContainer
diff --git a/tests/unit-tests/security/pib/detail/identity-impl.t.cpp b/tests/unit-tests/security/pib/detail/identity-impl.t.cpp
new file mode 100644
index 0000000..10c7bb8
--- /dev/null
+++ b/tests/unit-tests/security/pib/detail/identity-impl.t.cpp
@@ -0,0 +1,149 @@
+/* -*- 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).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/pib/detail/identity-impl.hpp"
+#include "security/pib/pib.hpp"
+#include "security/pib/pib-memory.hpp"
+#include "../pib-data-fixture.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace detail {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_AUTO_TEST_SUITE(Detail)
+BOOST_FIXTURE_TEST_SUITE(TestIdentityImpl, ndn::security::tests::PibDataFixture)
+
+using security::Pib;
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  auto pibImpl = make_shared<pib::PibMemory>();
+  IdentityImpl identity1(id1, pibImpl, true);
+
+  BOOST_CHECK_EQUAL(identity1.getName(), id1);
+}
+
+BOOST_AUTO_TEST_CASE(KeyOperation)
+{
+  auto pibImpl = make_shared<pib::PibMemory>();
+  IdentityImpl identity1(id1, pibImpl, true);
+  BOOST_CHECK_NO_THROW(IdentityImpl(id1, pibImpl, false));
+
+  // identity does not have any key
+  BOOST_CHECK_EQUAL(identity1.getKeys().size(), 0);
+
+  // get non-existing key, throw Pib::Error
+  BOOST_CHECK_THROW(identity1.getKey(id1Key1Name), Pib::Error);
+  // get default key, throw Pib::Error
+  BOOST_CHECK_THROW(identity1.getDefaultKey(), Pib::Error);
+  // set non-existing key as default key, throw Pib::Error
+  BOOST_REQUIRE_THROW(identity1.setDefaultKey(id1Key1Name), Pib::Error);
+
+  // add key
+  identity1.addKey(id1Key1.buf(), id1Key1.size(), id1Key1Name);
+  BOOST_CHECK_NO_THROW(identity1.getKey(id1Key1Name));
+
+  // new key becomes default key when there is no default key
+  BOOST_REQUIRE_NO_THROW(identity1.getDefaultKey());
+  const Key& defaultKey0 = identity1.getDefaultKey();
+  BOOST_CHECK_EQUAL(defaultKey0.getName(), id1Key1Name);
+  BOOST_CHECK(defaultKey0.getPublicKey() == id1Key1);
+
+  // remove key
+  identity1.removeKey(id1Key1Name);
+  BOOST_CHECK_THROW(identity1.getKey(id1Key1Name), Pib::Error);
+  BOOST_CHECK_THROW(identity1.getDefaultKey(), Pib::Error);
+
+  // set default key directly
+  BOOST_REQUIRE_NO_THROW(identity1.setDefaultKey(id1Key1.buf(), id1Key1.size(), id1Key1Name));
+  BOOST_REQUIRE_NO_THROW(identity1.getDefaultKey());
+  BOOST_CHECK_NO_THROW(identity1.getKey(id1Key1Name));
+
+  // check default key
+  const Key& defaultKey1 = identity1.getDefaultKey();
+  BOOST_CHECK_EQUAL(defaultKey1.getName(), id1Key1Name);
+  BOOST_CHECK(defaultKey1.getPublicKey() == id1Key1);
+
+  // add another key
+  identity1.addKey(id1Key2.buf(), id1Key2.size(), id1Key2Name);
+  BOOST_CHECK_EQUAL(identity1.getKeys().size(), 2);
+
+  // set default key through name
+  BOOST_REQUIRE_NO_THROW(identity1.setDefaultKey(id1Key2Name));
+  BOOST_REQUIRE_NO_THROW(identity1.getDefaultKey());
+  const Key& defaultKey2 = identity1.getDefaultKey();
+  BOOST_CHECK_EQUAL(defaultKey2.getName(), id1Key2Name);
+  BOOST_CHECK(defaultKey2.getPublicKey() == id1Key2);
+
+  // remove key
+  identity1.removeKey(id1Key1Name);
+  BOOST_CHECK_THROW(identity1.getKey(id1Key1Name), Pib::Error);
+  BOOST_CHECK_EQUAL(identity1.getKeys().size(), 1);
+
+  // set default key directly again, change the default setting
+  BOOST_REQUIRE_NO_THROW(identity1.setDefaultKey(id1Key1.buf(), id1Key1.size(), id1Key1Name));
+  const Key& defaultKey3 = identity1.getDefaultKey();
+  BOOST_CHECK_EQUAL(defaultKey3.getName(), id1Key1Name);
+  BOOST_CHECK(defaultKey3.getPublicKey() == id1Key1);
+  BOOST_CHECK_EQUAL(identity1.getKeys().size(), 2);
+
+  // remove all keys
+  identity1.removeKey(id1Key1Name);
+  BOOST_CHECK_THROW(identity1.getKey(id1Key1Name), Pib::Error);
+  BOOST_CHECK_EQUAL(identity1.getKeys().size(), 1);
+  identity1.removeKey(id1Key2Name);
+  BOOST_CHECK_THROW(identity1.getKey(id1Key2Name), Pib::Error);
+  BOOST_CHECK_EQUAL(identity1.getKeys().size(), 0);
+  BOOST_CHECK_THROW(identity1.getDefaultKey(), Pib::Error);
+}
+
+BOOST_AUTO_TEST_CASE(Errors)
+{
+  auto pibImpl = make_shared<pib::PibMemory>();
+
+  BOOST_CHECK_THROW(IdentityImpl(id1, pibImpl, false), Pib::Error);
+  IdentityImpl identity1(id1, pibImpl, true);
+
+  identity1.addKey(id1Key1.buf(), id1Key1.size(), id1Key1Name);
+  BOOST_CHECK_THROW(identity1.addKey(id1Key1.buf(), id1Key1.size(), id1Key1Name), Pib::Error);
+  BOOST_CHECK_THROW(identity1.addKey(id2Key1.buf(), id2Key1.size(), id2Key1Name), std::invalid_argument);
+  BOOST_CHECK_THROW(identity1.removeKey(id2Key1Name), std::invalid_argument);
+  BOOST_CHECK_THROW(identity1.getKey(id2Key1Name), std::invalid_argument);
+  BOOST_CHECK_THROW(identity1.setDefaultKey(id2Key1.buf(), id2Key1.size(), id2Key1Name), std::invalid_argument);
+  BOOST_CHECK_THROW(identity1.setDefaultKey(id2Key1Name), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestIdentityImpl
+BOOST_AUTO_TEST_SUITE_END() // Detail
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace detail
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit-tests/security/pib/detail/key-impl.t.cpp b/tests/unit-tests/security/pib/detail/key-impl.t.cpp
new file mode 100644
index 0000000..b25a870
--- /dev/null
+++ b/tests/unit-tests/security/pib/detail/key-impl.t.cpp
@@ -0,0 +1,164 @@
+/* -*- 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).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/pib/detail/key-impl.hpp"
+#include "security/pib/pib.hpp"
+#include "security/pib/pib-memory.hpp"
+#include "../pib-data-fixture.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace detail {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_AUTO_TEST_SUITE(Detail)
+BOOST_FIXTURE_TEST_SUITE(TestKeyImpl, ndn::security::tests::PibDataFixture)
+
+using security::Pib;
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  auto pibImpl = make_shared<pib::PibMemory>();
+  KeyImpl key11(id1Key1Name, id1Key1.buf(), id1Key1.size(), pibImpl);
+
+  BOOST_CHECK_EQUAL(key11.getName(), id1Key1Name);
+  BOOST_CHECK_EQUAL(key11.getIdentity(), id1);
+  BOOST_CHECK_EQUAL(key11.getKeyType(), KeyType::EC);
+  BOOST_CHECK(key11.getPublicKey() == id1Key1);
+
+  KeyImpl key11Bak(id1Key1Name, pibImpl);
+  BOOST_CHECK_EQUAL(key11Bak.getName(), id1Key1Name);
+  BOOST_CHECK_EQUAL(key11Bak.getIdentity(), id1);
+  BOOST_CHECK_EQUAL(key11Bak.getKeyType(), KeyType::EC);
+  BOOST_CHECK(key11Bak.getPublicKey() == id1Key1);
+}
+
+BOOST_AUTO_TEST_CASE(CertificateOperation)
+{
+  auto pibImpl = make_shared<pib::PibMemory>();
+  KeyImpl key11(id1Key1Name, id1Key1.buf(), id1Key1.size(), pibImpl);
+  BOOST_CHECK_NO_THROW(KeyImpl(id1Key1Name, pibImpl));
+
+  // key does not have any certificate
+  BOOST_CHECK_EQUAL(key11.getCertificates().size(), 0);
+
+  // get non-existing certificate, throw Pib::Error
+  BOOST_CHECK_THROW(key11.getCertificate(id1Key1Cert1.getName()), Pib::Error);
+  // get default certificate, throw Pib::Error
+  BOOST_CHECK_THROW(key11.getDefaultCertificate(), Pib::Error);
+  // set non-existing certificate as default certificate, throw Pib::Error
+  BOOST_REQUIRE_THROW(key11.setDefaultCertificate(id1Key1Cert1.getName()), Pib::Error);
+
+  // add certificate
+  key11.addCertificate(id1Key1Cert1);
+  BOOST_CHECK_NO_THROW(key11.getCertificate(id1Key1Cert1.getName()));
+
+  // new certificate becomes default certificate when there was no default certificate
+  BOOST_REQUIRE_NO_THROW(key11.getDefaultCertificate());
+  const auto& defaultCert0 = key11.getDefaultCertificate();
+  BOOST_CHECK_EQUAL(defaultCert0.getName(), id1Key1Cert1.getName());
+  BOOST_CHECK_EQUAL(defaultCert0, id1Key1Cert1);
+
+  // remove certificate
+  key11.removeCertificate(id1Key1Cert1.getName());
+  BOOST_CHECK_THROW(key11.getCertificate(id1Key1Cert1.getName()), Pib::Error);
+  BOOST_CHECK_THROW(key11.getDefaultCertificate(), Pib::Error);
+
+  // set default certificate directly
+  BOOST_REQUIRE_NO_THROW(key11.setDefaultCertificate(id1Key1Cert1));
+  BOOST_REQUIRE_NO_THROW(key11.getDefaultCertificate());
+  BOOST_CHECK_NO_THROW(key11.getCertificate(id1Key1Cert1.getName()));
+
+  // check default cert
+  const auto& defaultCert1 = key11.getDefaultCertificate();
+  BOOST_CHECK_EQUAL(defaultCert1.getName(), id1Key1Cert1.getName());
+  BOOST_CHECK_EQUAL(defaultCert1, id1Key1Cert1);
+
+  // add another certificate
+  key11.addCertificate(id1Key1Cert2);
+  BOOST_CHECK_EQUAL(key11.getCertificates().size(), 2);
+
+  // set default certificate through name
+  BOOST_REQUIRE_NO_THROW(key11.setDefaultCertificate(id1Key1Cert2.getName()));
+  BOOST_REQUIRE_NO_THROW(key11.getDefaultCertificate());
+  const auto& defaultCert2 = key11.getDefaultCertificate();
+  BOOST_CHECK_EQUAL(defaultCert2.getName(), id1Key1Cert2.getName());
+  BOOST_CHECK_EQUAL(defaultCert2, id1Key1Cert2);
+
+  // remove certificate
+  key11.removeCertificate(id1Key1Cert1.getName());
+  BOOST_CHECK_THROW(key11.getCertificate(id1Key1Cert1.getName()), Pib::Error);
+  BOOST_CHECK_EQUAL(key11.getCertificates().size(), 1);
+
+  // set default certificate directly again, change the default setting
+  BOOST_REQUIRE_NO_THROW(key11.setDefaultCertificate(id1Key1Cert1));
+  const auto& defaultCert3 = key11.getDefaultCertificate();
+  BOOST_CHECK_EQUAL(defaultCert3.getName(), id1Key1Cert1.getName());
+  BOOST_CHECK_EQUAL(defaultCert3, id1Key1Cert1);
+  BOOST_CHECK_EQUAL(key11.getCertificates().size(), 2);
+
+  // remove all certificates
+  key11.removeCertificate(id1Key1Cert1.getName());
+  BOOST_CHECK_THROW(key11.getCertificate(id1Key1Cert1.getName()), Pib::Error);
+  BOOST_CHECK_EQUAL(key11.getCertificates().size(), 1);
+  key11.removeCertificate(id1Key1Cert2.getName());
+  BOOST_CHECK_THROW(key11.getCertificate(id1Key1Cert2.getName()), Pib::Error);
+  BOOST_CHECK_THROW(key11.getDefaultCertificate(), Pib::Error);
+  BOOST_CHECK_EQUAL(key11.getCertificates().size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(Errors)
+{
+  auto pibImpl = make_shared<pib::PibMemory>();
+
+  BOOST_CHECK_THROW(KeyImpl(id1Key1Name, pibImpl), Pib::Error);
+  KeyImpl key11(id1Key1Name, id1Key1.buf(), id1Key1.size(), pibImpl);
+  BOOST_CHECK_THROW(KeyImpl(id1Key1Name, id1Key1.buf(), id1Key1.size(), pibImpl), Pib::Error);
+
+  BOOST_CHECK_THROW(KeyImpl(Name("/wrong"), pibImpl), std::invalid_argument);
+  BOOST_CHECK_THROW(KeyImpl(Name("/wrong"), id1Key1.buf(), id1Key1.size(), pibImpl), std::invalid_argument);
+  Buffer wrongKey;
+  BOOST_CHECK_THROW(KeyImpl(id1Key2Name, wrongKey.buf(), wrongKey.size(), pibImpl), std::invalid_argument);
+
+  key11.addCertificate(id1Key1Cert1);
+  BOOST_CHECK_THROW(key11.addCertificate(id1Key1Cert1), Pib::Error);
+  BOOST_CHECK_THROW(key11.addCertificate(id1Key2Cert1), std::invalid_argument);
+  BOOST_CHECK_THROW(key11.removeCertificate(id1Key2Cert1.getName()), std::invalid_argument);
+  BOOST_CHECK_THROW(key11.getCertificate(id1Key2Cert1.getName()), std::invalid_argument);
+  BOOST_CHECK_THROW(key11.setDefaultCertificate(id1Key2Cert1), std::invalid_argument);
+  BOOST_CHECK_THROW(key11.setDefaultCertificate(id1Key2Cert1.getName()), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestKeyImpl
+BOOST_AUTO_TEST_SUITE_END() // Detail
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace detail
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit-tests/security/pib/identity-container.t.cpp b/tests/unit-tests/security/pib/identity-container.t.cpp
index b9af408..92420db 100644
--- a/tests/unit-tests/security/pib/identity-container.t.cpp
+++ b/tests/unit-tests/security/pib/identity-container.t.cpp
@@ -35,23 +35,89 @@
 
 BOOST_AUTO_TEST_SUITE(Security)
 BOOST_AUTO_TEST_SUITE(Pib)
-BOOST_AUTO_TEST_SUITE(TestIdentityContainer)
+BOOST_FIXTURE_TEST_SUITE(TestIdentityContainer, PibDataFixture)
 
 using pib::Pib;
 
-BOOST_FIXTURE_TEST_CASE(Basic, PibDataFixture)
+BOOST_AUTO_TEST_CASE(Basic)
 {
   auto pibImpl = make_shared<PibMemory>();
-  Pib pib("pib-memory", "", pibImpl);
 
-  Identity identity1 = pib.addIdentity(id1);
-  Identity identity2 = pib.addIdentity(id2);
+  // start with an empty container
+  IdentityContainer container(pibImpl);
+  BOOST_CHECK_EQUAL(container.size(), 0);
+  BOOST_CHECK_EQUAL(container.getLoadedIdentities().size(), 0);
 
-  IdentityContainer container = pib.getIdentities();
+  // add the first identity
+  Identity identity11 = container.add(id1);
+  BOOST_CHECK_EQUAL(identity11.getName(), id1);
+  BOOST_CHECK_EQUAL(container.size(), 1);
+  BOOST_CHECK_EQUAL(container.getLoadedIdentities().size(), 1);
+  BOOST_CHECK(container.find(id1) != container.end());
+
+  // add the same identity again
+  Identity identity12 = container.add(id1);
+  BOOST_CHECK_EQUAL(identity12.getName(), id1);
+  BOOST_CHECK_EQUAL(container.size(), 1);
+  BOOST_CHECK_EQUAL(container.getLoadedIdentities().size(), 1);
+  BOOST_CHECK(container.find(id1) != container.end());
+
+  // add the second identity
+  Identity identity21 = container.add(id2);
+  BOOST_CHECK_EQUAL(identity21.getName(), id2);
   BOOST_CHECK_EQUAL(container.size(), 2);
+  BOOST_CHECK_EQUAL(container.getLoadedIdentities().size(), 2);
   BOOST_CHECK(container.find(id1) != container.end());
   BOOST_CHECK(container.find(id2) != container.end());
 
+  // get identities
+  BOOST_REQUIRE_NO_THROW(container.get(id1));
+  BOOST_REQUIRE_NO_THROW(container.get(id2));
+  BOOST_CHECK_THROW(container.get(Name("/non-existing")), Pib::Error);
+
+  // check identity
+  Identity identity1 = container.get(id1);
+  Identity identity2 = container.get(id2);
+  BOOST_CHECK_EQUAL(identity1.getName(), id1);
+  BOOST_CHECK_EQUAL(identity2.getName(), id2);
+
+  // create another container from the same PibImpl
+  // cache should be empty
+  IdentityContainer container2(pibImpl);
+  BOOST_CHECK_EQUAL(container2.size(), 2);
+  BOOST_CHECK_EQUAL(container2.getLoadedIdentities().size(), 0);
+
+  // get key, cache should be filled
+  BOOST_REQUIRE_NO_THROW(container2.get(id1));
+  BOOST_CHECK_EQUAL(container2.size(), 2);
+  BOOST_CHECK_EQUAL(container2.getLoadedIdentities().size(), 1);
+
+  BOOST_REQUIRE_NO_THROW(container2.get(id2));
+  BOOST_CHECK_EQUAL(container2.size(), 2);
+  BOOST_CHECK_EQUAL(container2.getLoadedIdentities().size(), 2);
+
+  // remove a key
+  container2.remove(id1);
+  BOOST_CHECK_EQUAL(container2.size(), 1);
+  BOOST_CHECK_EQUAL(container2.getLoadedIdentities().size(), 1);
+  BOOST_CHECK(container2.find(id1) == container2.end());
+  BOOST_CHECK(container2.find(id2) != container2.end());
+
+  // remove another key
+  container2.remove(id2);
+  BOOST_CHECK_EQUAL(container2.size(), 0);
+  BOOST_CHECK_EQUAL(container2.getLoadedIdentities().size(), 0);
+  BOOST_CHECK(container2.find(id2) == container2.end());
+
+}
+
+BOOST_AUTO_TEST_CASE(Iterator)
+{
+  auto pibImpl = make_shared<PibMemory>();
+  IdentityContainer container(pibImpl);
+  container.add(id1);
+  container.add(id2);
+
   std::set<Name> idNames;
   idNames.insert(id1);
   idNames.insert(id2);
@@ -74,6 +140,10 @@
     count++;
   }
   BOOST_CHECK_EQUAL(count, 2);
+
+  BOOST_CHECK(IdentityContainer::const_iterator() == IdentityContainer::const_iterator());
+  BOOST_CHECK(IdentityContainer::const_iterator() == container.end());
+  BOOST_CHECK(container.end() == IdentityContainer::const_iterator());
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestIdentityContainer
diff --git a/tests/unit-tests/security/pib/identity.t.cpp b/tests/unit-tests/security/pib/identity.t.cpp
index 3704d92..49bd8da 100644
--- a/tests/unit-tests/security/pib/identity.t.cpp
+++ b/tests/unit-tests/security/pib/identity.t.cpp
@@ -22,6 +22,7 @@
 #include "security/pib/identity.hpp"
 #include "security/pib/pib.hpp"
 #include "security/pib/pib-memory.hpp"
+#include "security/pib/detail/identity-impl.hpp"
 
 #include "boost-test.hpp"
 #include "pib-data-fixture.hpp"
@@ -41,7 +42,8 @@
 
 BOOST_AUTO_TEST_CASE(ValidityChecking)
 {
-  // identity
+  using security::pib::detail::IdentityImpl;
+
   Identity id;
 
   BOOST_CHECK_EQUAL(static_cast<bool>(id), false);
@@ -52,7 +54,8 @@
   else
     BOOST_CHECK(true);
 
-  id = Identity(id1, make_shared<PibMemory>(), true);
+  auto identityImpl = make_shared<IdentityImpl>(id1, make_shared<PibMemory>(), true);
+  id = Identity(identityImpl);
 
   BOOST_CHECK_EQUAL(static_cast<bool>(id), true);
   BOOST_CHECK_EQUAL(!id, false);
@@ -63,41 +66,26 @@
     BOOST_CHECK(false);
 }
 
-BOOST_AUTO_TEST_CASE(KeyOperations)
+/**
+ * pib::Identity is a wrapper of pib::detail::IdentityImpl.  Since the functionalities of
+ * IdentityImpl have already been tested in detail/identity-impl.t.cpp, we only test the shared
+ * property of pib::Identity in this test case.
+ */
+BOOST_AUTO_TEST_CASE(Share)
 {
-  Identity identity1(id1, make_shared<PibMemory>(), true);
+  using security::pib::detail::IdentityImpl;
 
-  // Key does not exist, throw Error
+  auto identityImpl = make_shared<IdentityImpl>(id1, make_shared<pib::PibMemory>(), true);
+  Identity identity1(identityImpl);
+  Identity identity2(identityImpl);
+
+  identity1.addKey(id1Key1.buf(), id1Key1.size(), id1Key1Name);
+  BOOST_CHECK_NO_THROW(identity2.getKey(id1Key1Name));
+  identity2.removeKey(id1Key1Name);
   BOOST_CHECK_THROW(identity1.getKey(id1Key1Name), Pib::Error);
-  // Key name does not match identity name, throw Error
-  BOOST_CHECK_THROW(identity1.getKey(id2Key1Name), Pib::Error);
 
-  // Add key
-  Key key11 = identity1.addKey(id1Key1.buf(), id1Key1.size(), id1Key1Name);
-  BOOST_CHECK_NO_THROW(identity1.getKey(id1Key1Name));
-  // Key name does not match identity name, throw Error
-  BOOST_CHECK_THROW(identity1.addKey(id2Key1.buf(), id2Key1.size(), id2Key1Name), Pib::Error);
-
-  // Remove key
-  identity1.removeKey(id1Key1Name);
-  BOOST_CHECK_THROW(identity1.getKey(id1Key1Name), Pib::Error);
-  // Key name does not match identity name, throw Error
-  BOOST_CHECK_THROW(identity1.removeKey(id2Key1Name), Pib::Error);
-
-  // Default key does not exist, throw Error
-  BOOST_CHECK_THROW(identity1.getDefaultKey(), Pib::Error);
-
-  // Set default key but the key does not exist, throw Error
-  BOOST_CHECK_THROW(identity1.setDefaultKey(id1Key1Name), Pib::Error);
-  // Set default key
-  BOOST_REQUIRE_NO_THROW(identity1.setDefaultKey(id1Key1.buf(), id1Key1.size(), id1Key1Name));
-  BOOST_CHECK_NO_THROW(identity1.getDefaultKey());
-  BOOST_CHECK_EQUAL(identity1.getDefaultKey().getName(), id1Key1Name);
-
-  // Remove the default key
-  identity1.removeKey(id1Key1Name);
-  BOOST_CHECK_THROW(identity1.getKey(id1Key1Name), Pib::Error);
-  BOOST_CHECK_THROW(identity1.getDefaultKey(), Pib::Error);
+  identity1.setDefaultKey(id1Key1.buf(), id1Key1.size(), id1Key1Name);
+  BOOST_CHECK_NO_THROW(identity2.getDefaultKey());
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestIdentity
diff --git a/tests/unit-tests/security/pib/key-container.t.cpp b/tests/unit-tests/security/pib/key-container.t.cpp
index 812fab4..5b7961e 100644
--- a/tests/unit-tests/security/pib/key-container.t.cpp
+++ b/tests/unit-tests/security/pib/key-container.t.cpp
@@ -35,25 +35,106 @@
 
 BOOST_AUTO_TEST_SUITE(Security)
 BOOST_AUTO_TEST_SUITE(Pib)
-BOOST_AUTO_TEST_SUITE(TestKeyContainer)
+BOOST_FIXTURE_TEST_SUITE(TestKeyContainer, PibDataFixture)
 
 using pib::Pib;
 
-BOOST_FIXTURE_TEST_CASE(Basic, PibDataFixture)
+BOOST_AUTO_TEST_CASE(Basic)
 {
   auto pibImpl = make_shared<PibMemory>();
-  Pib pib("pib-memory", "", pibImpl);
 
-  Identity identity1 = pib.addIdentity(id1);
+  // start with an empty container
+  KeyContainer container(id1, pibImpl);
+  BOOST_CHECK_EQUAL(container.size(), 0);
+  BOOST_CHECK_EQUAL(container.getLoadedKeys().size(), 0);
 
-  Key key11 = identity1.addKey(id1Key1.buf(), id1Key1.size(), id1Key1Name);
-  Key key12 = identity1.addKey(id1Key2.buf(), id1Key2.size(), id1Key2Name);
+  // add the first key
+  Key key11 = container.add(id1Key1.buf(), id1Key1.size(), id1Key1Name);
+  BOOST_CHECK_EQUAL(key11.getName(), id1Key1Name);
+  BOOST_CHECK(key11.getPublicKey() == id1Key1);
+  BOOST_CHECK_EQUAL(container.size(), 1);
+  BOOST_CHECK_EQUAL(container.getLoadedKeys().size(), 1);
+  BOOST_CHECK(container.find(id1Key1Name) != container.end());
 
-  KeyContainer container = identity1.getKeys();
+  // add the same key again
+  Key key12 = container.add(id1Key1.buf(), id1Key1.size(), id1Key1Name);
+  BOOST_CHECK_EQUAL(key12.getName(), id1Key1Name);
+  BOOST_CHECK(key12.getPublicKey() == id1Key1);
+  BOOST_CHECK_EQUAL(container.size(), 1);
+  BOOST_CHECK_EQUAL(container.getLoadedKeys().size(), 1);
+  BOOST_CHECK(container.find(id1Key1Name) != container.end());
+
+  // add the second key
+  Key key21 = container.add(id1Key2.buf(), id1Key2.size(), id1Key2Name);
+  BOOST_CHECK_EQUAL(key21.getName(), id1Key2Name);
+  BOOST_CHECK(key21.getPublicKey() == id1Key2);
   BOOST_CHECK_EQUAL(container.size(), 2);
+  BOOST_CHECK_EQUAL(container.getLoadedKeys().size(), 2);
   BOOST_CHECK(container.find(id1Key1Name) != container.end());
   BOOST_CHECK(container.find(id1Key2Name) != container.end());
 
+  // get keys
+  BOOST_REQUIRE_NO_THROW(container.get(id1Key1Name));
+  BOOST_REQUIRE_NO_THROW(container.get(id1Key2Name));
+  Name id1Key3Name = v2::constructKeyName(id1, name::Component("non-existing-id"));
+  BOOST_CHECK_THROW(container.get(id1Key3Name), Pib::Error);
+
+  // check key
+  Key key1 = container.get(id1Key1Name);
+  Key key2 = container.get(id1Key2Name);
+  BOOST_CHECK_EQUAL(key1.getName(), id1Key1Name);
+  BOOST_CHECK(key1.getPublicKey() == id1Key1);
+  BOOST_CHECK_EQUAL(key2.getName(), id1Key2Name);
+  BOOST_CHECK(key2.getPublicKey() == id1Key2);
+
+  // create another container from the same PibImpl
+  // cache should be empty
+  KeyContainer container2(id1, pibImpl);
+  BOOST_CHECK_EQUAL(container2.size(), 2);
+  BOOST_CHECK_EQUAL(container2.getLoadedKeys().size(), 0);
+
+  // get key, cache should be filled
+  BOOST_REQUIRE_NO_THROW(container2.get(id1Key1Name));
+  BOOST_CHECK_EQUAL(container2.size(), 2);
+  BOOST_CHECK_EQUAL(container2.getLoadedKeys().size(), 1);
+
+  BOOST_REQUIRE_NO_THROW(container2.get(id1Key2Name));
+  BOOST_CHECK_EQUAL(container2.size(), 2);
+  BOOST_CHECK_EQUAL(container2.getLoadedKeys().size(), 2);
+
+  // remove a key
+  container2.remove(id1Key1Name);
+  BOOST_CHECK_EQUAL(container2.size(), 1);
+  BOOST_CHECK_EQUAL(container2.getLoadedKeys().size(), 1);
+  BOOST_CHECK(container2.find(id1Key1Name) == container2.end());
+  BOOST_CHECK(container2.find(id1Key2Name) != container2.end());
+
+  // remove another key
+  container2.remove(id1Key2Name);
+  BOOST_CHECK_EQUAL(container2.size(), 0);
+  BOOST_CHECK_EQUAL(container2.getLoadedKeys().size(), 0);
+  BOOST_CHECK(container2.find(id1Key2Name) == container2.end());
+}
+
+BOOST_AUTO_TEST_CASE(Errors)
+{
+  auto pibImpl = make_shared<PibMemory>();
+
+  KeyContainer container(id1, pibImpl);
+
+  BOOST_CHECK_THROW(container.add(id2Key1.buf(), id2Key1.size(), id2Key1Name), std::invalid_argument);
+  BOOST_CHECK_THROW(container.remove(id2Key1Name), std::invalid_argument);
+  BOOST_CHECK_THROW(container.get(id2Key1Name), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(Iterator)
+{
+  auto pibImpl = make_shared<PibMemory>();
+  KeyContainer container(id1, pibImpl);
+
+  container.add(id1Key1.buf(), id1Key1.size(), id1Key1Name);
+  container.add(id1Key2.buf(), id1Key2.size(), id1Key2Name);
+
   std::set<Name> keyNames;
   keyNames.insert(id1Key1Name);
   keyNames.insert(id1Key2Name);
@@ -77,6 +158,10 @@
     count++;
   }
   BOOST_CHECK_EQUAL(count, 2);
+
+  BOOST_CHECK(KeyContainer::const_iterator() == KeyContainer::const_iterator());
+  BOOST_CHECK(KeyContainer::const_iterator() == container.end());
+  BOOST_CHECK(container.end() == KeyContainer::const_iterator());
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestKeyContainer
diff --git a/tests/unit-tests/security/pib/key.t.cpp b/tests/unit-tests/security/pib/key.t.cpp
index 9391f53..afa65e7 100644
--- a/tests/unit-tests/security/pib/key.t.cpp
+++ b/tests/unit-tests/security/pib/key.t.cpp
@@ -22,6 +22,7 @@
 #include "security/pib/key.hpp"
 #include "security/pib/pib.hpp"
 #include "security/pib/pib-memory.hpp"
+#include "security/pib/detail/key-impl.hpp"
 
 #include "boost-test.hpp"
 #include "pib-data-fixture.hpp"
@@ -41,6 +42,8 @@
 
 BOOST_AUTO_TEST_CASE(ValidityChecking)
 {
+  using security::pib::detail::KeyImpl;
+
   Key key;
 
   BOOST_CHECK_EQUAL(static_cast<bool>(key), false);
@@ -51,7 +54,8 @@
   else
     BOOST_CHECK(true);
 
-  key = Key(id1Key1Name, id1Key1.buf(), id1Key1.size(), make_shared<PibMemory>());
+  auto keyImpl = make_shared<KeyImpl>(id1Key1Name, id1Key1.buf(), id1Key1.size(), make_shared<pib::PibMemory>());
+  key = Key(keyImpl);
 
   BOOST_CHECK_EQUAL(static_cast<bool>(key), true);
   BOOST_CHECK_EQUAL(!key, false);
@@ -62,27 +66,27 @@
     BOOST_CHECK(false);
 }
 
-BOOST_AUTO_TEST_CASE(CertificateOperations)
+/**
+ * pib::Key is a wrapper of pib::detail::KeyImpl.  Since the functionalities of KeyImpl
+ * have already been tested in detail/key-impl.t.cpp, we only test the shared property
+ * of pib::Key in this test case.
+ */
+
+BOOST_AUTO_TEST_CASE(Share)
 {
-  Key key11(id1Key1Name, id1Key1.buf(), id1Key1.size(), make_shared<PibMemory>());
+  using security::pib::detail::KeyImpl;
 
-  BOOST_CHECK_THROW(key11.getCertificate(id1Key1Cert1.getName()), Pib::Error);
-  key11.addCertificate(id1Key1Cert1);
-  BOOST_CHECK_NO_THROW(key11.getCertificate(id1Key1Cert1.getName()));
-  key11.removeCertificate(id1Key1Cert1.getName());
-  BOOST_CHECK_THROW(key11.getCertificate(id1Key1Cert1.getName()), Pib::Error);
+  auto keyImpl = make_shared<KeyImpl>(id1Key1Name, id1Key1.buf(), id1Key1.size(), make_shared<pib::PibMemory>());
+  Key key1(keyImpl);
+  Key key2(keyImpl);
 
-  BOOST_CHECK_THROW(key11.getDefaultCertificate(), Pib::Error);
-  BOOST_REQUIRE_THROW(key11.setDefaultCertificate(id1Key1Cert1.getName()), Pib::Error);
-  BOOST_REQUIRE_NO_THROW(key11.setDefaultCertificate(id1Key1Cert1));
-  BOOST_REQUIRE_NO_THROW(key11.getDefaultCertificate());
+  key1.addCertificate(id1Key1Cert1);
+  BOOST_CHECK_NO_THROW(key2.getCertificate(id1Key1Cert1.getName()));
+  key2.removeCertificate(id1Key1Cert1.getName());
+  BOOST_CHECK_THROW(key1.getCertificate(id1Key1Cert1.getName()), Pib::Error);
 
-  const auto& defaultCert = key11.getDefaultCertificate();
-  BOOST_CHECK(defaultCert.wireEncode() == id1Key1Cert1.wireEncode());
-
-  key11.removeCertificate(id1Key1Cert1.getName());
-  BOOST_CHECK_THROW(key11.getCertificate(id1Key1Cert1.getName()), Pib::Error);
-  BOOST_CHECK_THROW(key11.getDefaultCertificate(), Pib::Error);
+  key1.setDefaultCertificate(id1Key1Cert1);
+  BOOST_CHECK_NO_THROW(key2.getDefaultCertificate());
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestKey
diff --git a/tests/unit-tests/security/pib/pib-impl.t.cpp b/tests/unit-tests/security/pib/pib-impl.t.cpp
index 3103416..2677e9d 100644
--- a/tests/unit-tests/security/pib/pib-impl.t.cpp
+++ b/tests/unit-tests/security/pib/pib-impl.t.cpp
@@ -169,7 +169,7 @@
   BOOST_CHECK_EQUAL(this->pib.hasKey(this->id1Key1Name), true);
   BOOST_CHECK_EQUAL(this->pib.hasIdentity(this->id1), true);
   const Buffer& keyBits = this->pib.getKeyBits(this->id1Key1Name);
-  BOOST_CHECK_EQUAL_COLLECTIONS(keyBits.begin(), keyBits.end(), this->id1Key1.begin(), this->id1Key1.end());
+  BOOST_CHECK(keyBits == this->id1Key1);
   BOOST_CHECK_NO_THROW(this->pib.getDefaultKeyOfIdentity(this->id1));
   BOOST_CHECK_EQUAL(this->pib.getDefaultKeyOfIdentity(this->id1), this->id1Key1Name);
 
diff --git a/tests/unit-tests/security/pib/pib.t.cpp b/tests/unit-tests/security/pib/pib.t.cpp
index f947fed..de436b4 100644
--- a/tests/unit-tests/security/pib/pib.t.cpp
+++ b/tests/unit-tests/security/pib/pib.t.cpp
@@ -91,20 +91,46 @@
 BOOST_AUTO_TEST_CASE(IdentityOperations)
 {
   Pib pib("pib-memory", "", make_shared<PibMemory>());
+  BOOST_CHECK_EQUAL(pib.getIdentities().size(), 0);
 
+  // get non-existing identity, throw Pib::Error
   BOOST_CHECK_THROW(pib.getIdentity(id1), Pib::Error);
-  Identity identity1 = pib.addIdentity(id1);
+  // get default identity when it is not set yet, throw Pib::Error
+  BOOST_CHECK_THROW(pib.getDefaultIdentity(), Pib::Error);
+
+  // add identity
+  pib.addIdentity(id1);
   BOOST_CHECK_NO_THROW(pib.getIdentity(id1));
+  BOOST_CHECK_EQUAL(pib.getIdentities().size(), 1);
+
+  // new key becomes default key when there was no default key
+  BOOST_REQUIRE_NO_THROW(pib.getDefaultIdentity());
+  BOOST_CHECK_EQUAL(pib.getDefaultIdentity().getName(), id1);
+
+  // remove identity
   pib.removeIdentity(id1);
   BOOST_CHECK_THROW(pib.getIdentity(id1), Pib::Error);
-
   BOOST_CHECK_THROW(pib.getDefaultIdentity(), Pib::Error);
+  BOOST_CHECK_EQUAL(pib.getIdentities().size(), 0);
+
+  // set default identity
   BOOST_REQUIRE_NO_THROW(pib.setDefaultIdentity(id1));
   BOOST_REQUIRE_NO_THROW(pib.getDefaultIdentity());
   BOOST_CHECK_EQUAL(pib.getDefaultIdentity().getName(), id1);
+  BOOST_CHECK_EQUAL(pib.getIdentities().size(), 1);
+  BOOST_REQUIRE_NO_THROW(pib.setDefaultIdentity(id2));
+  BOOST_REQUIRE_NO_THROW(pib.getDefaultIdentity());
+  BOOST_CHECK_EQUAL(pib.getDefaultIdentity().getName(), id2);
+  BOOST_CHECK_EQUAL(pib.getIdentities().size(), 2);
+
+  // remove default identity
+  pib.removeIdentity(id2);
+  BOOST_CHECK_THROW(pib.getIdentity(id2), Pib::Error);
+  BOOST_CHECK_THROW(pib.getDefaultIdentity(), Pib::Error);
+  BOOST_CHECK_EQUAL(pib.getIdentities().size(), 1);
   pib.removeIdentity(id1);
   BOOST_CHECK_THROW(pib.getIdentity(id1), Pib::Error);
-  BOOST_CHECK_THROW(pib.getDefaultIdentity(), Pib::Error);
+  BOOST_CHECK_EQUAL(pib.getIdentities().size(), 0);
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestPib