split configuration into pieces

Change-Id: I3039a3e15d9637e05b0f9678a7141258fb49e983
diff --git a/src/ca-module.hpp b/src/ca-module.hpp
index 5a3da6a..e98638b 100644
--- a/src/ca-module.hpp
+++ b/src/ca-module.hpp
@@ -21,7 +21,7 @@
 #ifndef NDNCERT_CA_MODULE_HPP
 #define NDNCERT_CA_MODULE_HPP
 
-#include "configuration.hpp"
+#include "detail/ca-configuration.hpp"
 #include "detail/crypto-helpers.hpp"
 #include "detail/ca-storage.hpp"
 
diff --git a/src/configuration.cpp b/src/configuration.cpp
deleted file mode 100644
index a56f490..0000000
--- a/src/configuration.cpp
+++ /dev/null
@@ -1,262 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2017-2020, Regents of the University of California.
- *
- * This file is part of ndncert, a certificate management system based on NDN.
- *
- * ndncert is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * ndncert 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 General Public License for more details.
- *
- * You should have received copies of the GNU General Public License along with
- * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- *
- * See AUTHORS.md for complete list of ndncert authors and contributors.
- */
-
-#include "configuration.hpp"
-#include "identity-challenge/challenge-module.hpp"
-#include "name-assignment/assignment-func.hpp"
-#include <ndn-cxx/util/io.hpp>
-#include <boost/filesystem.hpp>
-
-namespace ndn {
-namespace ndncert {
-
-// Parse CA Configuration file
-const std::string CONFIG_CA_PREFIX = "ca-prefix";
-const std::string CONFIG_CA_INFO = "ca-info";
-const std::string CONFIG_MAX_VALIDITY_PERIOD = "max-validity-period";
-const std::string CONFIG_MAX_SUFFIX_LENGTH = "max-suffix-length";
-const std::string CONFIG_PROBE_PARAMETERS = "probe-parameters";
-const std::string CONFIG_PROBE_PARAMETER = "probe-parameter-key";
-const std::string CONFIG_SUPPORTED_CHALLENGES = "supported-challenges";
-const std::string CONFIG_CHALLENGE = "challenge";
-const std::string CONFIG_CERTIFICATE = "certificate";
-const std::string CONFIG_REDIRECTION = "redirect-to";
-const std::string CONFIG_NAME_ASSIGNMENT = "name-assignment";
-
-void
-CaProfile::parse(const JsonSection& configJson)
-{
-  // CA prefix
-  m_caPrefix = Name(configJson.get(CONFIG_CA_PREFIX, ""));
-  if (m_caPrefix.empty()) {
-    NDN_THROW(std::runtime_error("Cannot parse ca-prefix from the config file"));
-  }
-  // CA info
-  m_caInfo = configJson.get(CONFIG_CA_INFO, "");
-  // CA max validity period
-  m_maxValidityPeriod = time::seconds(configJson.get(CONFIG_MAX_VALIDITY_PERIOD, 86400));
-  // CA max suffix length
-  m_maxSuffixLength = nullopt;
-  auto maxSuffixLength = configJson.get_optional<size_t>(CONFIG_MAX_SUFFIX_LENGTH);
-  if (maxSuffixLength) {
-    m_maxSuffixLength = *maxSuffixLength;
-  }
-  // probe parameter keys
-  m_probeParameterKeys.clear();
-  auto probeParametersJson = configJson.get_child_optional(CONFIG_PROBE_PARAMETERS);
-  if (probeParametersJson) {
-    for (const auto& item : *probeParametersJson) {
-      auto probeParameter = item.second.get(CONFIG_PROBE_PARAMETER, "");
-      probeParameter = boost::algorithm::to_lower_copy(probeParameter);
-      if (probeParameter == "") {
-        NDN_THROW(std::runtime_error("Probe parameter key cannot be empty."));
-      }
-      m_probeParameterKeys.push_back(probeParameter);
-    }
-  }
-  // supported challenges
-  m_supportedChallenges.clear();
-  auto challengeListJson = configJson.get_child_optional(CONFIG_SUPPORTED_CHALLENGES);
-  if (challengeListJson) {
-    for (const auto& item : *challengeListJson) {
-      auto challengeType = item.second.get(CONFIG_CHALLENGE, "");
-      challengeType = boost::algorithm::to_lower_copy(challengeType);
-      if (challengeType == "") {
-        NDN_THROW(std::runtime_error("Challenge type canont be empty."));
-      }
-      if (!ChallengeModule::isChallengeSupported(challengeType)) {
-        NDN_THROW(std::runtime_error("Challenge " + challengeType + " is not supported."));
-      }
-      m_supportedChallenges.push_back(challengeType);
-    }
-  }
-  // anchor certificate
-  m_cert = nullptr;
-  auto certificateStr = configJson.get(CONFIG_CERTIFICATE, "");
-  if (certificateStr != "") {
-    std::istringstream ss(certificateStr);
-    m_cert = io::load<security::Certificate>(ss);
-  }
-}
-
-JsonSection
-CaProfile::toJson() const
-{
-  JsonSection caItem;
-  caItem.put(CONFIG_CA_PREFIX, m_caPrefix.toUri());
-  caItem.put(CONFIG_CA_INFO, m_caInfo);
-  caItem.put(CONFIG_MAX_VALIDITY_PERIOD, m_maxValidityPeriod.count());
-  if (m_maxSuffixLength) {
-    caItem.put(CONFIG_MAX_SUFFIX_LENGTH, *m_maxSuffixLength);
-  }
-  if (!m_probeParameterKeys.empty()) {
-    JsonSection probeParametersJson;
-    for (const auto& key : m_probeParameterKeys) {
-      JsonSection keyJson;
-      keyJson.put(CONFIG_PROBE_PARAMETER, key);
-      probeParametersJson.push_back(std::make_pair("", keyJson));
-    }
-    caItem.add_child("", probeParametersJson);
-  }
-  if (!m_supportedChallenges.empty()) {
-    JsonSection challengeListJson;
-    for (const auto& challenge : m_supportedChallenges) {
-      JsonSection challengeJson;
-      challengeJson.put(CONFIG_CHALLENGE, challenge);
-      challengeListJson.push_back(std::make_pair("", challengeJson));
-    }
-    caItem.add_child("", challengeListJson);
-  }
-  if (m_cert != nullptr) {
-    std::stringstream ss;
-    io::save(*m_cert, ss);
-    caItem.put("certificate", ss.str());
-  }
-  return caItem;
-}
-
-namespace ca {
-
-void
-CaConfig::load(const std::string& fileName)
-{
-  JsonSection configJson;
-  try {
-    boost::property_tree::read_json(fileName, configJson);
-  }
-  catch (const std::exception& error) {
-    NDN_THROW(std::runtime_error("Failed to parse configuration file " + fileName + ", " + error.what()));
-  }
-  if (configJson.begin() == configJson.end()) {
-    NDN_THROW(std::runtime_error("No JSON configuration found in file: " + fileName));
-  }
-  m_caItem.parse(configJson);
-  if (m_caItem.m_supportedChallenges.size() == 0) {
-    NDN_THROW(std::runtime_error("At least one challenge should be specified."));
-  }
-  // parse redirection section if appears
-  m_redirection = nullopt;
-  auto redirectionItems = configJson.get_child_optional(CONFIG_REDIRECTION);
-  if (redirectionItems) {
-    for (const auto& item : *redirectionItems) {
-      auto caPrefixStr = item.second.get(CONFIG_CA_PREFIX, "");
-      auto caCertStr = item.second.get(CONFIG_CERTIFICATE, "");
-      if (caCertStr == "") {
-        NDN_THROW(std::runtime_error("Redirect-to item's ca-prefix or certificate cannot be empty."));
-      }
-      std::istringstream ss(caCertStr);
-      auto caCert = io::load<security::Certificate>(ss);
-      if (!m_redirection) {
-        m_redirection = std::vector<std::shared_ptr<security::Certificate>>();
-      }
-      m_redirection->push_back(caCert);
-    }
-  }
-  //parse name assignment if appears
-  m_nameAssignmentFuncs.clear();
-  auto nameAssignmentItems = configJson.get_child_optional(CONFIG_NAME_ASSIGNMENT);
-  if (nameAssignmentItems) {
-    for (const auto& item : *nameAssignmentItems) {
-      auto func = NameAssignmentFunc::createNameAssignmentFunc(item.first, item.second.data());
-      if (func == nullptr) {
-        NDN_THROW(std::runtime_error("Error on creating name assignment function"));
-      }
-      m_nameAssignmentFuncs.push_back(std::move(func));
-    }
-  }
-}
-
-} // namespace ca
-
-namespace requester {
-
-void
-ProfileStorage::load(const std::string& fileName)
-{
-  JsonSection configJson;
-  try {
-    boost::property_tree::read_json(fileName, configJson);
-  }
-  catch (const std::exception& error) {
-    NDN_THROW(std::runtime_error("Failed to parse configuration file " + fileName + ", " + error.what()));
-  }
-  if (configJson.begin() == configJson.end()) {
-    NDN_THROW(std::runtime_error("No JSON configuration found in file: " + fileName));
-  }
-  load(configJson);
-}
-
-void
-ProfileStorage::load(const JsonSection& configSection)
-{
-  m_caItems.clear();
-  auto caList = configSection.get_child("ca-list");
-  for (auto item : caList) {
-    CaProfile caItem;
-    caItem.parse(item.second);
-    if (caItem.m_cert == nullptr) {
-      NDN_THROW(std::runtime_error("No CA certificate is loaded from JSON configuration."));
-    }
-    m_caItems.push_back(std::move(caItem));
-  }
-}
-
-void
-ProfileStorage::save(const std::string& fileName) const
-{
-  JsonSection configJson;
-  for (const auto& caItem : m_caItems) {
-    configJson.push_back(std::make_pair("", caItem.toJson()));
-  }
-  std::stringstream ss;
-  boost::property_tree::write_json(ss, configJson);
-  std::ofstream configFile;
-  configFile.open(fileName);
-  configFile << ss.str();
-  configFile.close();
-}
-
-void
-ProfileStorage::removeCaProfile(const Name& caName)
-{
-  m_caItems.remove_if([&](const CaProfile& item) { return item.m_caPrefix == caName; });
-}
-
-void
-ProfileStorage::addCaProfile(const CaProfile& profile)
-{
-  for (auto& item : m_caItems) {
-    if (item.m_caPrefix == profile.m_caPrefix) {
-      item = profile;
-      return;
-    }
-  }
-  m_caItems.push_back(profile);
-}
-
-const std::list<CaProfile>&
-ProfileStorage::getCaItems() const
-{
-  return m_caItems;
-}
-
-} // namespace requester
-} // namespace ndncert
-} // namespace ndn
diff --git a/src/configuration.hpp b/src/configuration.hpp
deleted file mode 100644
index 4e466e2..0000000
--- a/src/configuration.hpp
+++ /dev/null
@@ -1,184 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2017-2020, Regents of the University of California.
- *
- * This file is part of ndncert, a certificate management system based on NDN.
- *
- * ndncert is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * ndncert 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 General Public License for more details.
- *
- * You should have received copies of the GNU General Public License along with
- * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- *
- * See AUTHORS.md for complete list of ndncert authors and contributors.
- */
-
-#ifndef NDNCERT_CONFIGURATION_HPP
-#define NDNCERT_CONFIGURATION_HPP
-
-#include "detail/ca-request-state.hpp"
-#include "name-assignment/assignment-func.hpp"
-
-namespace ndn {
-namespace ndncert {
-
-struct CaProfile {
-public:
-  /**
-   * Parse the configuration json and modify current struct to the result.
-   * @param configJson the configuration json to parse
-   */
-  void
-  parse(const JsonSection& configJson);
-
-  /**
-   * @return the JSON representation of this profile.
-   */
-  JsonSection
-  toJson() const;
-
-public:
-  /**
-   * CA Name prefix (without /CA suffix).
-   */
-  Name m_caPrefix;
-  /**
-   * CA Information.
-   * Default: "".
-   */
-  std::string m_caInfo;
-  /**
-   * A list of parameter-keys for PROBE.
-   * Default: empty list.
-   */
-  std::list<std::string> m_probeParameterKeys;
-  /**
-   * Maximum allowed validity period of the certificate being requested.
-   * The value is in the unit of second.
-   * Default: one day (86400 seconds).
-   */
-  time::seconds m_maxValidityPeriod;
-  /**
-   * Maximum allowed suffix length of requested name.
-   * E.g., When its value is 2, at most 2 name components can be assigned after m_caPrefix.
-   * Default: none.
-   */
-  optional<size_t> m_maxSuffixLength = nullopt;
-  /**
-   * A list of supported challenges. Only CA side will have m_supportedChallenges.
-   * Default: empty list.
-   */
-  std::list<std::string> m_supportedChallenges;
-  /**
-   * CA's certificate. Only Client side will have m_cert.
-   * Default: nullptr.
-   */
-  std::shared_ptr<security::Certificate> m_cert;
-
-private:
-  void
-  parseProbeParameters(const JsonSection& section);
-
-  void
-  parseChallengeList(const JsonSection& configSection);
-};
-
-namespace ca {
-
-/**
- * @brief CA's configuration on NDNCERT.
- * @sa https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3#213-ca-profile
- *
- * The format of CA configuration in JSON
- * {
- *  "ca-prefix": "",
- *  "ca-info": "",
- *  "max-validity-period": "",
- *  "max-suffix-length": "",
- *  "probe-parameters":
- *  [
- *    {"probe-parameter-key": ""},
- *    {"probe-parameter-key": ""}
- *  ]
- *  "supported-challenges":
- *  [
- *    {"challenge": ""},
- *    {"challenge": ""}
- *  ]
- * }
- */
-class CaConfig
-{
-public:
-  /**
-   * Load CA configuration from the file.
-   * @throw std::runtime_error when config file cannot be correctly parsed.
-   */
-  void
-  load(const std::string& fileName);
-
-public:
-  CaProfile m_caItem;
-  /**
-   * Used for CA redirection
-   * @sa https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3-PROBE-Extensions#probe-extension-for-redirection
-   */
-  optional<std::vector<std::shared_ptr<security::Certificate>>> m_redirection = nullopt;
-  /**
-   * Name Assignment Functions
-   */
-  std::vector<std::unique_ptr<NameAssignmentFunc>> m_nameAssignmentFuncs;
-};
-
-} // namespace ca
-
-namespace requester {
-
-/**
- * @brief Represents Client configuration
- * @sa https://github.com/named-data/ndncert/wiki/Client-Configuration-Sample
- */
-class ProfileStorage
-{
-public:
-  /**
-   * @throw std::runtime_error when config file cannot be correctly parsed.
-   */
-  void
-  load(const std::string& fileName);
-
-  /**
-   * @throw std::runtime_error when config file cannot be correctly parsed.
-   */
-  void
-  load(const JsonSection& configSection);
-
-  void
-  save(const std::string& fileName) const;
-
-  void
-  removeCaProfile(const Name& caName);
-
-  /**
-   * Be cautious. This will add a new trust anchor for requesters.
-   */
-  void
-  addCaProfile(const CaProfile& profile);
-
-  const std::list<CaProfile>&
-  getCaItems() const;
-
-private:
-  std::list<CaProfile> m_caItems;
-};
-
-} // namespace requester
-} // namespace ndncert
-} // namespace ndn
-
-#endif // NDNCERT_CONFIGURATION_HPP
diff --git a/src/detail/ca-configuration.cpp b/src/detail/ca-configuration.cpp
new file mode 100644
index 0000000..b88352b
--- /dev/null
+++ b/src/detail/ca-configuration.cpp
@@ -0,0 +1,82 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert 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 General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#include "detail/ca-configuration.hpp"
+#include "identity-challenge/challenge-module.hpp"
+#include "name-assignment/assignment-func.hpp"
+#include <ndn-cxx/util/io.hpp>
+#include <boost/filesystem.hpp>
+
+namespace ndn {
+namespace ndncert {
+namespace ca {
+
+void
+CaConfig::load(const std::string& fileName)
+{
+  JsonSection configJson;
+  try {
+    boost::property_tree::read_json(fileName, configJson);
+  }
+  catch (const std::exception& error) {
+    NDN_THROW(std::runtime_error("Failed to parse configuration file " + fileName + ", " + error.what()));
+  }
+  if (configJson.begin() == configJson.end()) {
+    NDN_THROW(std::runtime_error("No JSON configuration found in file: " + fileName));
+  }
+  m_caItem.parse(configJson);
+  if (m_caItem.m_supportedChallenges.size() == 0) {
+    NDN_THROW(std::runtime_error("At least one challenge should be specified."));
+  }
+  // parse redirection section if appears
+  m_redirection = nullopt;
+  auto redirectionItems = configJson.get_child_optional(CONFIG_REDIRECTION);
+  if (redirectionItems) {
+    for (const auto& item : *redirectionItems) {
+      auto caPrefixStr = item.second.get(CONFIG_CA_PREFIX, "");
+      auto caCertStr = item.second.get(CONFIG_CERTIFICATE, "");
+      if (caCertStr == "") {
+        NDN_THROW(std::runtime_error("Redirect-to item's ca-prefix or certificate cannot be empty."));
+      }
+      std::istringstream ss(caCertStr);
+      auto caCert = io::load<security::Certificate>(ss);
+      if (!m_redirection) {
+        m_redirection = std::vector<std::shared_ptr<security::Certificate>>();
+      }
+      m_redirection->push_back(caCert);
+    }
+  }
+  // parse name assignment if appears
+  m_nameAssignmentFuncs.clear();
+  auto nameAssignmentItems = configJson.get_child_optional(CONFIG_NAME_ASSIGNMENT);
+  if (nameAssignmentItems) {
+    for (const auto& item : *nameAssignmentItems) {
+      auto func = NameAssignmentFunc::createNameAssignmentFunc(item.first, item.second.data());
+      if (func == nullptr) {
+        NDN_THROW(std::runtime_error("Error on creating name assignment function"));
+      }
+      m_nameAssignmentFuncs.push_back(std::move(func));
+    }
+  }
+}
+
+} // namespace ca
+} // namespace ndncert
+} // namespace ndn
diff --git a/src/detail/ca-configuration.hpp b/src/detail/ca-configuration.hpp
new file mode 100644
index 0000000..da170b9
--- /dev/null
+++ b/src/detail/ca-configuration.hpp
@@ -0,0 +1,79 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert 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 General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#ifndef NDNCERT_DETAIL_CA_CONFIGURATION_HPP
+#define NDNCERT_DETAIL_CA_CONFIGURATION_HPP
+
+#include "detail/ca-profile.hpp"
+
+namespace ndn {
+namespace ndncert {
+namespace ca {
+
+/**
+ * @brief CA's configuration on NDNCERT.
+ * @sa https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3
+ *
+ * The format of CA configuration in JSON
+ * {
+ *  "ca-prefix": "",
+ *  "ca-info": "",
+ *  "max-validity-period": "",
+ *  "max-suffix-length": "",
+ *  "probe-parameters":
+ *  [
+ *    {"probe-parameter-key": ""},
+ *    {"probe-parameter-key": ""}
+ *  ]
+ *  "supported-challenges":
+ *  [
+ *    {"challenge": ""},
+ *    {"challenge": ""}
+ *  ]
+ * }
+ */
+class CaConfig
+{
+public:
+  /**
+   * Load CA configuration from the file.
+   * @throw std::runtime_error when config file cannot be correctly parsed.
+   */
+  void
+  load(const std::string& fileName);
+
+public:
+  CaProfile m_caItem;
+  /**
+   * Used for CA redirection
+   * @sa https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3-PROBE-Extensions#probe-extension-for-redirection
+   */
+  optional<std::vector<std::shared_ptr<security::Certificate>>> m_redirection = nullopt;
+  /**
+   * Name Assignment Functions
+   */
+  std::vector<std::unique_ptr<NameAssignmentFunc>> m_nameAssignmentFuncs;
+};
+
+} // namespace ca
+} // namespace ndncert
+} // namespace ndn
+
+#endif // NDNCERT_DETAIL_CA_CONFIGURATION_HPP
diff --git a/src/detail/ca-profile.cpp b/src/detail/ca-profile.cpp
new file mode 100644
index 0000000..4599e06
--- /dev/null
+++ b/src/detail/ca-profile.cpp
@@ -0,0 +1,123 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert 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 General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#include "detail/ca-profile.hpp"
+#include "identity-challenge/challenge-module.hpp"
+#include "name-assignment/assignment-func.hpp"
+#include <ndn-cxx/util/io.hpp>
+#include <boost/filesystem.hpp>
+
+namespace ndn {
+namespace ndncert {
+
+void
+CaProfile::parse(const JsonSection& configJson)
+{
+  // CA prefix
+  m_caPrefix = Name(configJson.get(CONFIG_CA_PREFIX, ""));
+  if (m_caPrefix.empty()) {
+    NDN_THROW(std::runtime_error("Cannot parse ca-prefix from the config file"));
+  }
+  // CA info
+  m_caInfo = configJson.get(CONFIG_CA_INFO, "");
+  // CA max validity period
+  m_maxValidityPeriod = time::seconds(configJson.get(CONFIG_MAX_VALIDITY_PERIOD, 86400));
+  // CA max suffix length
+  m_maxSuffixLength = nullopt;
+  auto maxSuffixLength = configJson.get_optional<size_t>(CONFIG_MAX_SUFFIX_LENGTH);
+  if (maxSuffixLength) {
+    m_maxSuffixLength = *maxSuffixLength;
+  }
+  // probe parameter keys
+  m_probeParameterKeys.clear();
+  auto probeParametersJson = configJson.get_child_optional(CONFIG_PROBE_PARAMETERS);
+  if (probeParametersJson) {
+    for (const auto& item : *probeParametersJson) {
+      auto probeParameter = item.second.get(CONFIG_PROBE_PARAMETER, "");
+      probeParameter = boost::algorithm::to_lower_copy(probeParameter);
+      if (probeParameter == "") {
+        NDN_THROW(std::runtime_error("Probe parameter key cannot be empty."));
+      }
+      m_probeParameterKeys.push_back(probeParameter);
+    }
+  }
+  // supported challenges
+  m_supportedChallenges.clear();
+  auto challengeListJson = configJson.get_child_optional(CONFIG_SUPPORTED_CHALLENGES);
+  if (challengeListJson) {
+    for (const auto& item : *challengeListJson) {
+      auto challengeType = item.second.get(CONFIG_CHALLENGE, "");
+      challengeType = boost::algorithm::to_lower_copy(challengeType);
+      if (challengeType == "") {
+        NDN_THROW(std::runtime_error("Challenge type canont be empty."));
+      }
+      if (!ChallengeModule::isChallengeSupported(challengeType)) {
+        NDN_THROW(std::runtime_error("Challenge " + challengeType + " is not supported."));
+      }
+      m_supportedChallenges.push_back(challengeType);
+    }
+  }
+  // anchor certificate
+  m_cert = nullptr;
+  auto certificateStr = configJson.get(CONFIG_CERTIFICATE, "");
+  if (certificateStr != "") {
+    std::istringstream ss(certificateStr);
+    m_cert = io::load<security::Certificate>(ss);
+  }
+}
+
+JsonSection
+CaProfile::toJson() const
+{
+  JsonSection caItem;
+  caItem.put(CONFIG_CA_PREFIX, m_caPrefix.toUri());
+  caItem.put(CONFIG_CA_INFO, m_caInfo);
+  caItem.put(CONFIG_MAX_VALIDITY_PERIOD, m_maxValidityPeriod.count());
+  if (m_maxSuffixLength) {
+    caItem.put(CONFIG_MAX_SUFFIX_LENGTH, *m_maxSuffixLength);
+  }
+  if (!m_probeParameterKeys.empty()) {
+    JsonSection probeParametersJson;
+    for (const auto& key : m_probeParameterKeys) {
+      JsonSection keyJson;
+      keyJson.put(CONFIG_PROBE_PARAMETER, key);
+      probeParametersJson.push_back(std::make_pair("", keyJson));
+    }
+    caItem.add_child("", probeParametersJson);
+  }
+  if (!m_supportedChallenges.empty()) {
+    JsonSection challengeListJson;
+    for (const auto& challenge : m_supportedChallenges) {
+      JsonSection challengeJson;
+      challengeJson.put(CONFIG_CHALLENGE, challenge);
+      challengeListJson.push_back(std::make_pair("", challengeJson));
+    }
+    caItem.add_child("", challengeListJson);
+  }
+  if (m_cert != nullptr) {
+    std::stringstream ss;
+    io::save(*m_cert, ss);
+    caItem.put("certificate", ss.str());
+  }
+  return caItem;
+}
+
+} // namespace ndncert
+} // namespace ndn
diff --git a/src/detail/ca-profile.hpp b/src/detail/ca-profile.hpp
new file mode 100644
index 0000000..c67f5dc
--- /dev/null
+++ b/src/detail/ca-profile.hpp
@@ -0,0 +1,100 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert 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 General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#ifndef NDNCERT_DETAIL_CA_PROFILE_HPP
+#define NDNCERT_DETAIL_CA_PROFILE_HPP
+
+#include "name-assignment/assignment-func.hpp"
+
+namespace ndn {
+namespace ndncert {
+
+// used in parsing CA configuration file and Client CA profile storage file
+const std::string CONFIG_CA_PREFIX = "ca-prefix";
+const std::string CONFIG_CA_INFO = "ca-info";
+const std::string CONFIG_MAX_VALIDITY_PERIOD = "max-validity-period";
+const std::string CONFIG_MAX_SUFFIX_LENGTH = "max-suffix-length";
+const std::string CONFIG_PROBE_PARAMETERS = "probe-parameters";
+const std::string CONFIG_PROBE_PARAMETER = "probe-parameter-key";
+const std::string CONFIG_SUPPORTED_CHALLENGES = "supported-challenges";
+const std::string CONFIG_CHALLENGE = "challenge";
+const std::string CONFIG_CERTIFICATE = "certificate";
+const std::string CONFIG_REDIRECTION = "redirect-to";
+const std::string CONFIG_NAME_ASSIGNMENT = "name-assignment";
+
+struct CaProfile
+{
+public:
+  /**
+   * Parse the configuration json and modify current struct to the result.
+   * @param configJson the configuration json to parse
+   */
+  void
+  parse(const JsonSection& configJson);
+
+  /**
+   * @return the JSON representation of this profile.
+   */
+  JsonSection
+  toJson() const;
+
+public:
+  /**
+   * CA Name prefix (without /CA suffix).
+   */
+  Name m_caPrefix;
+  /**
+   * CA Information.
+   * Default: "".
+   */
+  std::string m_caInfo;
+  /**
+   * A list of parameter-keys for PROBE.
+   * Default: empty list.
+   */
+  std::list<std::string> m_probeParameterKeys;
+  /**
+   * Maximum allowed validity period of the certificate being requested.
+   * The value is in the unit of second.
+   * Default: one day (86400 seconds).
+   */
+  time::seconds m_maxValidityPeriod;
+  /**
+   * Maximum allowed suffix length of requested name.
+   * E.g., When its value is 2, at most 2 name components can be assigned after m_caPrefix.
+   * Default: none.
+   */
+  optional<size_t> m_maxSuffixLength = nullopt;
+  /**
+   * A list of supported challenges. Only CA side will have m_supportedChallenges.
+   * Default: empty list.
+   */
+  std::list<std::string> m_supportedChallenges;
+  /**
+   * CA's certificate. Only Client side will have m_cert.
+   * Default: nullptr.
+   */
+  std::shared_ptr<security::Certificate> m_cert;
+};
+
+} // namespace ndncert
+} // namespace ndn
+
+#endif // NDNCERT_DETAIL_CA_PROFILE_HPP
diff --git a/src/detail/error-encoder.hpp b/src/detail/error-encoder.hpp
index 99d9dd8..67fe80f 100644
--- a/src/detail/error-encoder.hpp
+++ b/src/detail/error-encoder.hpp
@@ -21,7 +21,7 @@
 #ifndef NDNCERT_DETAIL_ERROR_ENCODER_HPP
 #define NDNCERT_DETAIL_ERROR_ENCODER_HPP
 
-#include "configuration.hpp"
+#include "detail/ca-profile.hpp"
 
 namespace ndn {
 namespace ndncert {
diff --git a/src/detail/info-encoder.hpp b/src/detail/info-encoder.hpp
index 03adfd8..7f9180e 100644
--- a/src/detail/info-encoder.hpp
+++ b/src/detail/info-encoder.hpp
@@ -21,7 +21,7 @@
 #ifndef NDNCERT_DETAIL_INFO_ENCODER_HPP
 #define NDNCERT_DETAIL_INFO_ENCODER_HPP
 
-#include "configuration.hpp"
+#include "detail/ca-profile.hpp"
 
 namespace ndn {
 namespace ndncert {
diff --git a/src/detail/profile-storage.cpp b/src/detail/profile-storage.cpp
new file mode 100644
index 0000000..f880d1b
--- /dev/null
+++ b/src/detail/profile-storage.cpp
@@ -0,0 +1,103 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert 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 General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#include "detail/profile-storage.hpp"
+#include "identity-challenge/challenge-module.hpp"
+#include "name-assignment/assignment-func.hpp"
+#include <ndn-cxx/util/io.hpp>
+#include <boost/filesystem.hpp>
+
+namespace ndn {
+namespace ndncert {
+namespace requester {
+
+void
+ProfileStorage::load(const std::string& fileName)
+{
+  JsonSection configJson;
+  try {
+    boost::property_tree::read_json(fileName, configJson);
+  }
+  catch (const std::exception& error) {
+    NDN_THROW(std::runtime_error("Failed to parse configuration file " + fileName + ", " + error.what()));
+  }
+  if (configJson.begin() == configJson.end()) {
+    NDN_THROW(std::runtime_error("No JSON configuration found in file: " + fileName));
+  }
+  load(configJson);
+}
+
+void
+ProfileStorage::load(const JsonSection& configSection)
+{
+  m_caItems.clear();
+  auto caList = configSection.get_child("ca-list");
+  for (auto item : caList) {
+    CaProfile caItem;
+    caItem.parse(item.second);
+    if (caItem.m_cert == nullptr) {
+      NDN_THROW(std::runtime_error("No CA certificate is loaded from JSON configuration."));
+    }
+    m_caItems.push_back(std::move(caItem));
+  }
+}
+
+void
+ProfileStorage::save(const std::string& fileName) const
+{
+  JsonSection configJson;
+  for (const auto& caItem : m_caItems) {
+    configJson.push_back(std::make_pair("", caItem.toJson()));
+  }
+  std::stringstream ss;
+  boost::property_tree::write_json(ss, configJson);
+  std::ofstream configFile;
+  configFile.open(fileName);
+  configFile << ss.str();
+  configFile.close();
+}
+
+void
+ProfileStorage::removeCaProfile(const Name& caName)
+{
+  m_caItems.remove_if([&](const CaProfile& item) { return item.m_caPrefix == caName; });
+}
+
+void
+ProfileStorage::addCaProfile(const CaProfile& profile)
+{
+  for (auto& item : m_caItems) {
+    if (item.m_caPrefix == profile.m_caPrefix) {
+      item = profile;
+      return;
+    }
+  }
+  m_caItems.push_back(profile);
+}
+
+const std::list<CaProfile>&
+ProfileStorage::getCaItems() const
+{
+  return m_caItems;
+}
+
+} // namespace requester
+} // namespace ndncert
+} // namespace ndn
diff --git a/src/detail/profile-storage.hpp b/src/detail/profile-storage.hpp
new file mode 100644
index 0000000..0f8d1ce
--- /dev/null
+++ b/src/detail/profile-storage.hpp
@@ -0,0 +1,73 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert 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 General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#ifndef NDNCERT_CONFIGURATION_HPP
+#define NDNCERT_CONFIGURATION_HPP
+
+#include "detail/ca-profile.hpp"
+#include "name-assignment/assignment-func.hpp"
+
+namespace ndn {
+namespace ndncert {
+namespace requester {
+
+/**
+ * @brief Represents Client configuration
+ * @sa https://github.com/named-data/ndncert/wiki/Client-Configuration-Sample
+ */
+class ProfileStorage
+{
+public:
+  /**
+   * @throw std::runtime_error when config file cannot be correctly parsed.
+   */
+  void
+  load(const std::string& fileName);
+
+  /**
+   * @throw std::runtime_error when config file cannot be correctly parsed.
+   */
+  void
+  load(const JsonSection& configSection);
+
+  void
+  save(const std::string& fileName) const;
+
+  void
+  removeCaProfile(const Name& caName);
+
+  /**
+   * Be cautious. This will add a new trust anchor for requesters.
+   */
+  void
+  addCaProfile(const CaProfile& profile);
+
+  const std::list<CaProfile>&
+  getCaItems() const;
+
+private:
+  std::list<CaProfile> m_caItems;
+};
+
+} // namespace requester
+} // namespace ndncert
+} // namespace ndn
+
+#endif // NDNCERT_CONFIGURATION_HPP
diff --git a/src/requester-request-state.hpp b/src/requester-request-state.hpp
index c142a34..9c67409 100644
--- a/src/requester-request-state.hpp
+++ b/src/requester-request-state.hpp
@@ -23,7 +23,7 @@
 
 #include "detail/ca-request-state.hpp"
 #include "detail/crypto-helpers.hpp"
-#include "configuration.hpp"
+#include "detail/profile-storage.hpp"
 
 namespace ndn {
 namespace ndncert {
diff --git a/tests/unit-tests/configuration.t.cpp b/tests/unit-tests/configuration.t.cpp
index 317d01b..c2f84e8 100644
--- a/tests/unit-tests/configuration.t.cpp
+++ b/tests/unit-tests/configuration.t.cpp
@@ -18,7 +18,8 @@
  * See AUTHORS.md for complete list of ndncert authors and contributors.
  */
 
-#include "configuration.hpp"
+#include "detail/ca-configuration.hpp"
+#include "detail/profile-storage.hpp"
 #include "detail/info-encoder.hpp"
 #include "test-common.hpp"
 
diff --git a/tests/unit-tests/protocol-encoders.t.cpp b/tests/unit-tests/protocol-encoders.t.cpp
index b70ffa2..94673b8 100644
--- a/tests/unit-tests/protocol-encoders.t.cpp
+++ b/tests/unit-tests/protocol-encoders.t.cpp
@@ -23,9 +23,9 @@
 #include "detail/info-encoder.hpp"
 #include "detail/new-renew-revoke-encoder.hpp"
 #include "detail/probe-encoder.hpp"
+#include "detail/ca-configuration.hpp"
 #include "test-common.hpp"
-#include <configuration.hpp>
-#include <identity-management-fixture.hpp>
+#include "identity-management-fixture.hpp"
 
 namespace ndn {
 namespace ndncert {