diff --git a/CMakeLists.txt b/CMakeLists.txt
index b7cd7d0299b032eeeeedd64f6c53b67ab74ac96b..e6efdb56c1cb7471eb36489364b2ffb803ab4263 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -248,7 +248,7 @@ if (BUILD_TOOLS AND NOT MSVC)
         tools/dnc/main.cpp
         tools/dnc/dnc.cpp
         tools/common.cpp)
-    target_link_libraries(dnc PRIVATE dhtnet fmt::fmt)
+    target_link_libraries(dnc PRIVATE dhtnet fmt::fmt yaml-cpp)
     target_include_directories(dnc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/tools)
     install(TARGETS dnc RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
 
@@ -256,7 +256,7 @@ if (BUILD_TOOLS AND NOT MSVC)
         tools/dsh/main.cpp
         tools/dsh/dsh.cpp
         tools/common.cpp)
-    target_link_libraries(dsh PRIVATE dhtnet fmt::fmt)
+    target_link_libraries(dsh PRIVATE dhtnet fmt::fmt yaml-cpp)
     target_include_directories(dsh PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/tools)
     install(TARGETS dsh RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
 
diff --git a/tools/common.cpp b/tools/common.cpp
index 979274d3549ad82cda67fd704578475aaa29b604..7d338e5b7df98e788f405fa60dce860dc83289ed 100644
--- a/tools/common.cpp
+++ b/tools/common.cpp
@@ -30,14 +30,10 @@
 namespace dhtnet {
 
 dht::crypto::Identity
-loadIdentity(const std::filesystem::path& path)
-{
-    if (!std::filesystem::exists(path)) {
-        std::filesystem::create_directory(path);
-    }
+loadIdentity(const std::filesystem::path& path_id){
     try {
-        for (const auto& path : std::filesystem::directory_iterator(path)) {
-            auto p = path.path();
+        for (const auto& path_id : std::filesystem::directory_iterator(path_id)) {
+            auto p = path_id.path();
             if (p.extension() == ".pem") {
                 auto privateKey = std::make_unique<dht::crypto::PrivateKey>(fileutils::loadFile(p));
                 auto certificate = std::make_unique<dht::crypto::Certificate>(
@@ -45,14 +41,26 @@ loadIdentity(const std::filesystem::path& path)
                 return dht::crypto::Identity(std::move(privateKey), std::move(certificate));
             }
         }
-    } catch (const std::exception& e) {
-        fmt::print(stderr, "Error loadind key from .dhtnetTools: {}\n", e.what());
+    } catch (const std::exception& e) {}
+    return {};
+}
+dht::crypto::Identity
+loadIdentity(const std::filesystem::path& path_id, const std::filesystem::path& path_ca)
+{
+    if (!std::filesystem::exists(path_id)) {
+        std::filesystem::create_directory(path_id);
     }
-
-    auto ca = dht::crypto::generateIdentity("ca");
-    auto id = dht::crypto::generateIdentity("dhtnc", ca);
+    // Load identity
+    auto id = loadIdentity(path_id);
+    if (!id.first or !id.second) {
+        // Load CA
+        auto ca_id = loadIdentity(path_ca);
+        if (!ca_id.first or !ca_id.second)
+            ca_id = dht::crypto::generateIdentity("dhtnet");
+    id = dht::crypto::generateIdentity("dhtnet", ca_id);
     fmt::print("Generated new identity: {}\n", id.first->getPublicKey().getId());
-    dht::crypto::saveIdentity(id, path / "id");
+    dht::crypto::saveIdentity(id, path_id / "id");
+}
     return id;
 }
 
@@ -102,14 +110,13 @@ connectionManagerConfig(const std::filesystem::path& path,
     config->factory = iceFactory;
     config->cachePath = path;
     config->logger = logger;
-    if (!turn_host.empty())
+    if (!turn_host.empty()){
         config->turnEnabled = true;
-    config->turnServer = turn_host;
-    config->turnServerUserName = turn_user;
-    config->turnServerPwd = turn_pass;
-    config->turnServerRealm = turn_realm;
-
-
+        config->turnServer = turn_host;
+        config->turnServerUserName = turn_user;
+        config->turnServerPwd = turn_pass;
+        config->turnServerRealm = turn_realm;
+    }
     return std::move(config);
 }
 template<typename T>
diff --git a/tools/common.h b/tools/common.h
index 3e143b5b865b481b69e21c959832358d3c667fbf..ea5a2782efa0dfc6bb5e2e2b344616b45b53b3bf 100644
--- a/tools/common.h
+++ b/tools/common.h
@@ -30,7 +30,7 @@ constexpr size_t BUFFER_SIZE = 64 * 1024;
  * certification.
  * @return dht::crypto::Identity
  */
-dht::crypto::Identity loadIdentity(const std::filesystem::path& path);
+dht::crypto::Identity loadIdentity(const std::filesystem::path& path_id, const std::filesystem::path& path_ca);
 // add certstore to the config
 std::unique_ptr<ConnectionManager::Config> connectionManagerConfig(
     const std::filesystem::path& path,
diff --git a/tools/dnc/dnc.yaml b/tools/dnc/dnc.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..6e092fa3a5a0d869c477c348776476cc78ffafe9
--- /dev/null
+++ b/tools/dnc/dnc.yaml
@@ -0,0 +1,9 @@
+bootstrap: "bootstrap.jami.net"
+id_path: HOME/.dhtnet # Change this to the path of the id directory
+turn_host: "turn.jami.net"
+turn_user: "ring"
+turn_pass: "ring"
+turn_realm: "ring"
+port: 22
+ip: "127.0.0.1"
+CA: Home/.dhtnet # Change this to the path of the CA directory
diff --git a/tools/dnc/main.cpp b/tools/dnc/main.cpp
index f89661e8379836262581ffc3c1c1ad08fd405a05..0ba8b572ce97b20c08f7d4adbbe81e833715487b 100644
--- a/tools/dnc/main.cpp
+++ b/tools/dnc/main.cpp
@@ -28,6 +28,8 @@
 #include <fmt/ostream.h>
 #endif
 #include <netinet/in.h>
+#include <yaml-cpp/yaml.h>
+#include <fstream>
 
 struct dhtnc_params
 {
@@ -43,27 +45,32 @@ struct dhtnc_params
     std::string turn_user {};
     std::string turn_pass {};
     std::string turn_realm {};
+    std::string ca {};
+    std::string dnc_configuration {};
 };
 
-static const constexpr struct option long_options[] = {{"help", no_argument, nullptr, 'h'},
-                                                       {"version", no_argument, nullptr, 'v'},
-                                                       {"port", required_argument, nullptr, 'p'},
-                                                       {"ip", required_argument, nullptr, 'i'},
-                                                       {"listen", no_argument, nullptr, 'l'},
-                                                       {"bootstrap", required_argument, nullptr, 'b'},
-                                                       {"id_path", required_argument, nullptr, 'I'},
-                                                       {"turn_host", required_argument, nullptr, 't'},
-                                                       {"turn_user", required_argument, nullptr, 'u'},
-                                                       {"turn_pass", required_argument, nullptr, 'w'},
-                                                       {"turn_realm", required_argument, nullptr, 'r'},
-                                                       {nullptr, 0, nullptr, 0}};
+static const constexpr struct option long_options[]
+    = {{"help", no_argument, nullptr, 'h'},
+       {"version", no_argument, nullptr, 'v'},
+       {"port", required_argument, nullptr, 'p'},
+       {"ip", required_argument, nullptr, 'i'},
+       {"listen", no_argument, nullptr, 'l'},
+       {"bootstrap", required_argument, nullptr, 'b'},
+       {"id_path", required_argument, nullptr, 'I'},
+       {"turn_host", required_argument, nullptr, 't'},
+       {"turn_user", required_argument, nullptr, 'u'},
+       {"turn_pass", required_argument, nullptr, 'w'},
+       {"turn_realm", required_argument, nullptr, 'r'},
+       {"CA", required_argument, nullptr, 'C'},
+       {"dnc_configuration", required_argument, nullptr, 'd'},
+       {nullptr, 0, nullptr, 0}};
 
 dhtnc_params
 parse_args(int argc, char** argv)
 {
     dhtnc_params params;
     int opt;
-    while ((opt = getopt_long(argc, argv, "hvlw:r:u:t:I:b:p:i:", long_options, nullptr)) != -1) {
+    while ((opt = getopt_long(argc, argv, "hvlw:r:u:t:I:b:p:i:C:d:", long_options, nullptr)) != -1) {
         switch (opt) {
         case 'h':
             params.help = true;
@@ -98,6 +105,11 @@ parse_args(int argc, char** argv)
         case 'r':
             params.turn_realm = optarg;
             break;
+        case 'C':
+            params.ca = optarg;
+            break;
+        case 'd':
+            params.dnc_configuration = optarg;
         default:
             std::cerr << "Invalid option" << std::endl;
             exit(EXIT_FAILURE);
@@ -115,23 +127,43 @@ parse_args(int argc, char** argv)
         }
     }
 
-    // default values
-    if (params.remote_port == 0)
-        params.remote_port = 22;
-    if (params.remote_host.empty())
-        params.remote_host = "127.0.0.1";
-    if (params.bootstrap.empty())
-        params.bootstrap = "bootstrap.jami.net";
-    if (params.path.empty())
-        params.path = std::filesystem::path(getenv("HOME")) / ".dhtnet";
-    if (params.turn_host.empty())
-        params.turn_host = "turn.jami.net";
-    if (params.turn_user.empty())
-        params.turn_user = "ring";
-    if (params.turn_pass.empty())
-        params.turn_pass = "ring";
-    if (params.turn_realm.empty())
-        params.turn_realm = "ring";
+    // extract values from dnc yaml file
+    if (!params.dnc_configuration.empty()) {
+        printf("read configuration file: %s\n", params.dnc_configuration.c_str());
+        std::ifstream config_file(params.dnc_configuration);
+        if (!config_file.is_open()) {
+            std::cerr << "Error: Could not open configuration file.\n";
+        } else {
+            YAML::Node config = YAML::Load(config_file);
+            if (config["bootstrap"] && params.bootstrap.empty()) {
+                params.bootstrap = config["bootstrap"].as<std::string>();
+            }
+            if (config["id_path"] && params.path.empty()) {
+                params.path = config["id_path"].as<std::string>();
+            }
+            if (config["turn_host"] && params.turn_host.empty()) {
+                params.turn_host = config["turn_host"].as<std::string>();
+            }
+            if (config["turn_user"] && params.turn_user.empty()) {
+                params.turn_user = config["turn_user"].as<std::string>();
+            }
+            if (config["turn_pass"] && params.turn_pass.empty()) {
+                params.turn_pass = config["turn_pass"].as<std::string>();
+            }
+            if (config["turn_realm"] && params.turn_realm.empty()) {
+                params.turn_realm = config["turn_realm"].as<std::string>();
+            }
+            if (config["CA"] && params.ca.empty()) {
+                params.ca = config["CA"].as<std::string>();
+            }
+            if (config["ip"] && params.remote_host.empty()) {
+                params.dnc_configuration = config["ip"].as<std::string>();
+            }
+            if (config["port"] && params.remote_port == 0) {
+                params.remote_port = config["port"].as<int>();
+            }
+        }
+    }
     return params;
 }
 
@@ -156,20 +188,21 @@ main(int argc, char** argv)
     setSipLogLevel();
     auto params = parse_args(argc, argv);
 
-    if (params.help ){
-          fmt::print("Usage: dnc [options] [PEER_ID]\n"
-               "\nOptions:\n"
-               "  -h, --help            Show this help message and exit.\n"
-               "  -v, --version         Display the program version.\n"
-               "  -p, --port            Specify the port option with an argument.\n"
-               "  -i, --ip              Specify the ip option with an argument.\n"
-               "  -l, --listen          Start the program in listen mode.\n"
-               "  -b, --bootstrap       Specify the bootstrap option with an argument.\n"
-               "  -I, --id_path         Specify the id_path option with an argument.\n"
-               "  -t, --turn_host       Specify the turn_host option with an argument.\n"
-               "  -u, --turn_user       Specify the turn_user option with an argument.\n"
-               "  -w, --turn_pass       Specify the turn_pass option with an argument.\n"
-               "  -r, --turn_realm      Specify the turn_realm option with an argument.\n");
+    if (params.help) {
+        fmt::print("Usage: dnc [options] [PEER_ID]\n"
+                   "\nOptions:\n"
+                   "  -h, --help            Show this help message and exit.\n"
+                   "  -v, --version         Display the program version.\n"
+                   "  -p, --port            Specify the port option with an argument.\n"
+                   "  -i, --ip              Specify the ip option with an argument.\n"
+                   "  -l, --listen          Start the program in listen mode.\n"
+                   "  -b, --bootstrap       Specify the bootstrap option with an argument.\n"
+                   "  -I, --id_path         Specify the id_path option with an argument.\n"
+                   "  -t, --turn_host       Specify the turn_host option with an argument.\n"
+                   "  -u, --turn_user       Specify the turn_user option with an argument.\n"
+                   "  -w, --turn_pass       Specify the turn_pass option with an argument.\n"
+                   "  -r, --turn_realm      Specify the turn_realm option with an argument.\n"
+                   "  -C, --CA              Specify the CA option with an argument.\n");
         return EXIT_SUCCESS;
     }
     if (params.version) {
@@ -178,13 +211,19 @@ main(int argc, char** argv)
     }
 
     fmt::print("dnc 1.0\n");
-    auto identity = dhtnet::loadIdentity(params.path);
+    auto identity = dhtnet::loadIdentity(params.path, params.ca);
     fmt::print("Loaded identity: {} from {}\n", identity.second->getId(), params.path);
 
     std::unique_ptr<dhtnet::Dnc> dhtnc;
     if (params.listen) {
         // create dnc instance
-        dhtnc = std::make_unique<dhtnet::Dnc>(params.path, identity, params.bootstrap, params.turn_host, params.turn_user, params.turn_pass, params.turn_realm);
+        dhtnc = std::make_unique<dhtnet::Dnc>(params.path,
+                                              identity,
+                                              params.bootstrap,
+                                              params.turn_host,
+                                              params.turn_user,
+                                              params.turn_pass,
+                                              params.turn_realm);
     } else {
         dhtnc = std::make_unique<dhtnet::Dnc>(params.path,
                                               identity,
@@ -195,8 +234,7 @@ main(int argc, char** argv)
                                               params.turn_host,
                                               params.turn_user,
                                               params.turn_pass,
-                                              params.turn_realm
-                                            );
+                                              params.turn_realm);
     }
     dhtnc->run();
     return EXIT_SUCCESS;
diff --git a/tools/dsh/dsh.yaml b/tools/dsh/dsh.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..54e13abb7252b4ae0745ddcbbebaf612bc70828d
--- /dev/null
+++ b/tools/dsh/dsh.yaml
@@ -0,0 +1,8 @@
+bootstrap: "bootstrap.jami.net"
+id_path: HOME/.dhtnet # Change this to the path of the id directory
+turn_host: "turn.jami.net"
+turn_user: "ring"
+turn_pass: "ring"
+turn_realm: "ring"
+binary: "bash"
+CA: HOME/.dhtnet # Change this to the path of the CA directory
diff --git a/tools/dsh/main.cpp b/tools/dsh/main.cpp
index b6697a3842d0f9e12392b729e2297f207cd079ce..7c5e006a6dbf8cdd2dc01caab4006a65d93f93c9 100644
--- a/tools/dsh/main.cpp
+++ b/tools/dsh/main.cpp
@@ -122,13 +122,40 @@ parse_args(int argc, char** argv)
         }
     }
 
-    // default values
-    if (params.bootstrap.empty())
-        params.bootstrap = "bootstrap.jami.net";
-    if (params.binary.empty())
-        params.binary = "bash";
-    if (params.path.empty())
-        params.path = std::filesystem::path(getenv("HOME")) / ".dhtnet";
+    // extract values from dsh yaml file
+    if (!params.dsh_configuration.empty()) {
+        printf("read configuration file: %s\n", params.dsh_configuration.c_str());
+        std::ifstream config_file(params.dsh_configuration);
+        if (!config_file.is_open()) {
+            std::cerr << "Error: Could not open configuration file.\n";
+        } else {
+            YAML::Node config = YAML::Load(config_file);
+            if (config["bootstrap"] && params.bootstrap.empty()) {
+                params.bootstrap = config["bootstrap"].as<std::string>();
+            }
+            if (config["id_path"] && params.path.empty()) {
+                params.path = config["id_path"].as<std::string>();
+            }
+            if (config["turn_host"] && params.turn_host.empty()) {
+                params.turn_host = config["turn_host"].as<std::string>();
+            }
+            if (config["turn_user"] && params.turn_user.empty()) {
+                params.turn_user = config["turn_user"].as<std::string>();
+            }
+            if (config["turn_pass"] && params.turn_pass.empty()) {
+                params.turn_pass = config["turn_pass"].as<std::string>();
+            }
+            if (config["turn_realm"] && params.turn_realm.empty()) {
+                params.turn_realm = config["turn_realm"].as<std::string>();
+            }
+            if (config["CA"] && params.ca.empty()) {
+                params.ca = config["CA"].as<std::string>();
+            }
+            if (config["binary"] && params.binary.empty()) {
+                params.binary = config["binary"].as<std::string>();
+            }
+        }
+    }
     return params;
 }
 
@@ -179,7 +206,7 @@ main(int argc, char** argv)
 
     fmt::print("dsh 1.0\n");
 
-    auto identity = dhtnet::loadIdentity(params.path);
+    auto identity = dhtnet::loadIdentity(params.path, params.ca);
     fmt::print("Loaded identity: {} from {}\n", identity.second->getId(), params.path);
 
     std::unique_ptr<dhtnet::Dsh> dhtsh;
diff --git a/tools/dvpn/dvpn.yaml b/tools/dvpn/dvpn.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..20929ecd27f4c3e31f7da5efea58c03c40a6e6b9
--- /dev/null
+++ b/tools/dvpn/dvpn.yaml
@@ -0,0 +1,9 @@
+bootstrap: "bootstrap.jami.net"
+id_path: HOME/.dhtnet # Change this to the path of the id directory
+turn_host: "turn.jami.net"
+turn_user: "ring"
+turn_pass: "ring"
+turn_realm: "ring"
+configuration_file: "./test_config.yaml"
+CA: HOME/.dhtnet # Change this to the path of the CA directory
+
diff --git a/tools/dvpn/main.cpp b/tools/dvpn/main.cpp
index 66f8e139de1e86be16749aa5dcc658c4378eda2b..97dd0de06d757114be672282f29ac9aaf6bada05 100644
--- a/tools/dvpn/main.cpp
+++ b/tools/dvpn/main.cpp
@@ -28,6 +28,8 @@
 #include <fmt/ostream.h>
 #endif
 #include <netinet/in.h>
+#include <yaml-cpp/yaml.h>
+#include <fstream>
 
 struct dhtvpn_params
 {
@@ -42,6 +44,8 @@ struct dhtvpn_params
     std::string turn_pass {};
     std::string turn_realm {};
     std::string configuration_file {};
+    std::string ca {};
+    std::string dvpn_configuration_file {};
 };
 
 static const constexpr struct option long_options[]
@@ -54,7 +58,9 @@ static const constexpr struct option long_options[]
        {"turn_user", required_argument, nullptr, 'u'},
        {"turn_pass", required_argument, nullptr, 'w'},
        {"turn_realm", required_argument, nullptr, 'r'},
-       {"configuration_file", required_argument, nullptr, 'c'},
+       {"vpn_configuration_file", required_argument, nullptr, 'c'},
+       {"CA", required_argument, nullptr, 'C'},
+       {"dvpn_configuration_file", required_argument, nullptr, 'd'},
        {nullptr, 0, nullptr, 0}};
 
 dhtvpn_params
@@ -62,7 +68,7 @@ parse_args(int argc, char** argv)
 {
     dhtvpn_params params;
     int opt;
-    while ((opt = getopt_long(argc, argv, "hvlw:r:u:t:I:b:c:", long_options, nullptr)) != -1) {
+    while ((opt = getopt_long(argc, argv, "hvlw:r:u:t:I:b:c:C:d:", long_options, nullptr)) != -1) {
         switch (opt) {
         case 'h':
             params.help = true;
@@ -94,11 +100,51 @@ parse_args(int argc, char** argv)
         case 'c':
             params.configuration_file = optarg;
             break;
+        case 'C':
+            params.ca = optarg;
+            break;
+        case 'd':
+            params.dvpn_configuration_file = optarg;
+            break;
         default:
             std::cerr << "Invalid option" << std::endl;
             exit(EXIT_FAILURE);
         }
     }
+    // extract values from dvpn yaml file
+    if (!params.dvpn_configuration_file.empty()) {
+        printf("read configuration file: %s\n", params.dvpn_configuration_file.c_str());
+        std::ifstream config_file(params.dvpn_configuration_file);
+        if (!config_file.is_open()) {
+            std::cerr << "Error: Could not open configuration file.\n";
+        } else {
+            YAML::Node config = YAML::Load(config_file);
+            if (config["bootstrap"] && params.bootstrap.empty()) {
+                params.bootstrap = config["bootstrap"].as<std::string>();
+            }
+            if (config["id_path"] && params.path.empty()) {
+                params.path = config["id_path"].as<std::string>();
+            }
+            if (config["turn_host"] && params.turn_host.empty()) {
+                params.turn_host = config["turn_host"].as<std::string>();
+            }
+            if (config["turn_user"] && params.turn_user.empty()) {
+                params.turn_user = config["turn_user"].as<std::string>();
+            }
+            if (config["turn_pass"] && params.turn_pass.empty()) {
+                params.turn_pass = config["turn_pass"].as<std::string>();
+            }
+            if (config["turn_realm"] && params.turn_realm.empty()) {
+                params.turn_realm = config["turn_realm"].as<std::string>();
+            }
+            if (config["CA"] && params.ca.empty()) {
+                params.ca = config["CA"].as<std::string>();
+            }
+            if (config["configuration_file"] && params.configuration_file.empty()) {
+                params.configuration_file = config["configuration_file"].as<std::string>();
+            }
+        }
+    }
 
     // If not listening, the peer_id argument is required
     if (!params.listen && !params.help && !params.version) {
@@ -110,24 +156,6 @@ parse_args(int argc, char** argv)
             exit(EXIT_FAILURE);
         }
     }
-
-    // default values
-
-    if (params.bootstrap.empty())
-        params.bootstrap = "bootstrap.jami.net";
-    if (params.path.empty())
-        params.path = std::filesystem::path(getenv("HOME")) / ".dhtnet";
-    if (params.turn_host.empty())
-        params.turn_host = "turn.jami.net";
-    if (params.turn_user.empty())
-        params.turn_user = "ring";
-    if (params.turn_pass.empty())
-        params.turn_pass = "ring";
-    if (params.turn_realm.empty())
-        params.turn_realm = "ring";
-    if (params.configuration_file.empty())
-        params.configuration_file = std::filesystem::path(__FILE__).parent_path()/"test_config.yaml";
-
     return params;
 }
 
@@ -165,7 +193,9 @@ main(int argc, char** argv)
             "  -u, --turn_user       Specify the turn_user option with an argument.\n"
             "  -w, --turn_pass       Specify the turn_pass option with an argument.\n"
             "  -r, --turn_realm      Specify the turn_realm option with an argument.\n"
-            "  -c, --configuration_file Specify the configuration_file path option with an argument.\n"
+            "  -c, --vpn_configuration_file Specify the vpn_configuration_file path option with an argument.\n"
+            "  -C, --CA              Specify the CA path option with an argument.\n"
+            "  -d, --dvpn_configuration_file Specify the dvpn_configuration_file path option with an argument.\n"
             "\n");
         return EXIT_SUCCESS;
     }
@@ -175,7 +205,8 @@ main(int argc, char** argv)
     }
 
     fmt::print("dvpn 1.0\n");
-    auto identity = dhtnet::loadIdentity(params.path);
+
+    auto identity = dhtnet::loadIdentity(params.path, params.ca);
     fmt::print("Loaded identity: {} from {}\n", identity.second->getId(), params.path);
 
     std::unique_ptr<dhtnet::Dvpn> dvpn;