From 1857096ff0657e742062628e7e95be2f675034b4 Mon Sep 17 00:00:00 2001 From: James Chapman Date: Fri, 16 Feb 2024 16:34:09 +0000 Subject: [PATCH 1/2] feat: optional additions to a CSR subject alternative name can be added with DNS name and/or IPv4 address Signed-off-by: James Chapman --- README.md | 7 ++ .../crypto/interface/crypto_types.hpp | 7 ++ lib/evse_security/CMakeLists.txt | 12 +++ .../crypto/openssl/openssl_supplier.cpp | 21 +++++ lib/evse_security/evse_security.cpp | 11 +++ tests/openssl_supplier_test.cpp | 77 ++++++++++++++++++- tests/openssl_supplier_test_tpm.cpp | 2 + tests/tests.cpp | 12 ++- 8 files changed, 143 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6c52ab6..14f4aec 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,13 @@ make -j$(nproc) install make test ``` +## Certificate Signing Request +There are two configuration options that will add a DNS name and IP address to the +subject alternative name in the certificate signing request. +By default they are not added. +- `cmake -DCSR_DNS_NAME=charger.pionix.de ...` to include a DNS name +- `cmake -DCSR_IP_ADDRESS=192.168.2.1 ...` to include an IPv4 address + ## TPM There is a configuration option to configure OpenSSL for use with a TPM.
`cmake` ... `-DUSING_TPM2=ON`
diff --git a/include/evse_security/crypto/interface/crypto_types.hpp b/include/evse_security/crypto/interface/crypto_types.hpp index 17603e5..4d0ed14 100644 --- a/include/evse_security/crypto/interface/crypto_types.hpp +++ b/include/evse_security/crypto/interface/crypto_types.hpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include namespace evse_security { @@ -49,6 +51,11 @@ struct CertificateSigningRequestInfo { std::string organization; std::string commonName; + /// @brief incude a subjectAlternativeName DNSName + std::optional dns_name; + /// @brief incude a subjectAlternativeName IPAddress + std::optional ip_address; + KeyGenerationInfo key_info; }; class CertificateLoadException : public std::runtime_error { diff --git a/lib/evse_security/CMakeLists.txt b/lib/evse_security/CMakeLists.txt index 48fe206..817b347 100644 --- a/lib/evse_security/CMakeLists.txt +++ b/lib/evse_security/CMakeLists.txt @@ -80,4 +80,16 @@ if(USING_TPM2) ) endif() +if(CSR_DNS_NAME) + target_compile_definitions(evse_security PRIVATE + CSR_DNS_NAME="${CSR_DNS_NAME}" + ) +endif() + +if(CSR_IP_ADDRESS) + target_compile_definitions(evse_security PRIVATE + CSR_IP_ADDRESS="${CSR_IP_ADDRESS}" + ) +endif() + target_compile_features(evse_security PUBLIC cxx_std_17) diff --git a/lib/evse_security/crypto/openssl/openssl_supplier.cpp b/lib/evse_security/crypto/openssl/openssl_supplier.cpp index 515ec6d..7583138 100644 --- a/lib/evse_security/crypto/openssl/openssl_supplier.cpp +++ b/lib/evse_security/crypto/openssl/openssl_supplier.cpp @@ -7,8 +7,12 @@ #include +#include #include #include +#include +#include +#include #include #include @@ -724,9 +728,26 @@ bool OpenSSLSupplier::x509_generate_csr(const CertificateSigningRequestInfo& csr sk_X509_EXTENSION_push(extensions, ext_key_usage); sk_X509_EXTENSION_push(extensions, ext_basic_constraints); + std::vector names; + if (csr_info.dns_name.has_value()) { + names.push_back({std::string("DNS:") + csr_info.dns_name.value()}); + } + if (csr_info.ip_address.has_value()) { + names.push_back({std::string("IP:") + csr_info.ip_address.value()}); + } + + X509_EXTENSION* ext_san = nullptr; + if (!names.empty()) { + auto comma_fold = [](std::string a, const std::string& b) { return std::move(a) + ',' + b; }; + std::string value = std::accumulate(std::next(names.begin()), names.end(), std::string(names[0]), comma_fold); + ext_san = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, value.c_str()); + sk_X509_EXTENSION_push(extensions, ext_san); + } + const bool result = X509_REQ_add_extensions(x509_req_ptr.get(), extensions); X509_EXTENSION_free(ext_key_usage); X509_EXTENSION_free(ext_basic_constraints); + X509_EXTENSION_free(ext_san); sk_X509_EXTENSION_free(extensions); if (!result) { EVLOG_error << "Failed to add csr extensions!"; diff --git a/lib/evse_security/evse_security.cpp b/lib/evse_security/evse_security.cpp index ce235b2..c7581a1 100644 --- a/lib/evse_security/evse_security.cpp +++ b/lib/evse_security/evse_security.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -724,6 +725,16 @@ std::string EvseSecurity::generate_certificate_signing_request(LeafCertificateTy info.commonName = common; info.country = country; info.organization = organization; +#ifdef CSR_DNS_NAME + info.dns_name = CSR_DNS_NAME; +#else + info.dns_name = std::nullopt; +#endif +#ifdef CSR_IP_ADDRESS + info.ip_address = CSR_IP_ADDRESS; +#else + info.ip_address = std::nullopt; +#endif info.key_info.key_type = CryptoKeyType::EC_prime256v1; info.key_info.generate_on_tpm = use_tpm; diff --git a/tests/openssl_supplier_test.cpp b/tests/openssl_supplier_test.cpp index d292dde..89cb817 100644 --- a/tests/openssl_supplier_test.cpp +++ b/tests/openssl_supplier_test.cpp @@ -1,10 +1,11 @@ #include -#include #include #include -#include #include +#include + +// #define OUTPUT_CSR using namespace evse_security; @@ -107,10 +108,82 @@ TEST_F(OpenSSLSupplierTest, x509_generate_csr) { "UK", "Pionix", "0123456789", + .dns_name = std::nullopt, + .ip_address = std::nullopt, + {CryptoKeyType::EC_prime256v1, false, std::nullopt, "pki/csr_key.pem", std::nullopt}}; + auto res = OpenSSLSupplier::x509_generate_csr(csr_info, csr); + ASSERT_TRUE(res); + + std::ofstream out("csr.pem"); + out << csr; + out.close(); + + ASSERT_GT(csr.size(), 0); +} + +TEST_F(OpenSSLSupplierTest, x509_generate_csr_dns) { + std::string csr; + CertificateSigningRequestInfo csr_info = { + 0, + "UK", + "Pionix", + "0123456789", + .dns_name = "cs.pionix.de", + .ip_address = std::nullopt, + {CryptoKeyType::EC_prime256v1, false, std::nullopt, "pki/csr_key.pem", std::nullopt}}; + auto res = OpenSSLSupplier::x509_generate_csr(csr_info, csr); + ASSERT_TRUE(res); + +#ifdef OUTPUT_CSR + std::ofstream out("csr_dns.pem"); + out << csr; + out.close(); +#endif + + ASSERT_GT(csr.size(), 0); +} + +TEST_F(OpenSSLSupplierTest, x509_generate_csr_ip) { + std::string csr; + CertificateSigningRequestInfo csr_info = { + 0, + "UK", + "Pionix", + "0123456789", + .dns_name = std::nullopt, + .ip_address = "127.0.0.1", + {CryptoKeyType::EC_prime256v1, false, std::nullopt, "pki/csr_key.pem", std::nullopt}}; + auto res = OpenSSLSupplier::x509_generate_csr(csr_info, csr); + ASSERT_TRUE(res); + +#ifdef OUTPUT_CSR + std::ofstream out("csr_ip.pem"); + out << csr; + out.close(); +#endif + + ASSERT_GT(csr.size(), 0); +} + +TEST_F(OpenSSLSupplierTest, x509_generate_csr_dns_ip) { + std::string csr; + CertificateSigningRequestInfo csr_info = { + 0, + "UK", + "Pionix", + "0123456789", + .dns_name = "cs.pionix.de", + .ip_address = "127.0.0.1", {CryptoKeyType::EC_prime256v1, false, std::nullopt, "pki/csr_key.pem", std::nullopt}}; auto res = OpenSSLSupplier::x509_generate_csr(csr_info, csr); ASSERT_TRUE(res); +#ifdef OUTPUT_CSR + std::ofstream out("csr_dns_ip.pem"); + out << csr; + out.close(); +#endif + ASSERT_GT(csr.size(), 0); } diff --git a/tests/openssl_supplier_test_tpm.cpp b/tests/openssl_supplier_test_tpm.cpp index 0c318db..15583ea 100644 --- a/tests/openssl_supplier_test_tpm.cpp +++ b/tests/openssl_supplier_test_tpm.cpp @@ -109,6 +109,8 @@ TEST_F(OpenSSLSupplierTpmTest, x509_generate_csr) { "UK", "Pionix", "0123456789", + .dns_name = std::nullopt, + .ip_address = std::nullopt, {CryptoKeyType::EC_prime256v1, true, std::nullopt, "tpm_pki/csr_key.pem", std::nullopt}}; // std::cout << "tpm2 pre: " << OSSL_PROVIDER_available(nullptr, "tpm2") << std::endl; diff --git a/tests/tests.cpp b/tests/tests.cpp index 4ea384f..bb3d6fe 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -70,19 +71,22 @@ bool equal_certificate_strings(const std::string& cert1, const std::string& cert #if USING_OPENSSL_3 bool supports_tpm_usage() { bool supports_tpm = false; + auto libctx = OSSL_LIB_CTX_new(); - OSSL_PROVIDER* tpm2_provider = OSSL_PROVIDER_load(nullptr, evse_security::PROVIDER_TPM); + OSSL_PROVIDER* tpm2_provider = OSSL_PROVIDER_load(libctx, evse_security::PROVIDER_TPM); if (tpm2_provider != nullptr) { supports_tpm = - OSSL_PROVIDER_available(nullptr, evse_security::PROVIDER_TPM) && OSSL_PROVIDER_self_test(tpm2_provider); + OSSL_PROVIDER_available(libctx, evse_security::PROVIDER_TPM) && OSSL_PROVIDER_self_test(tpm2_provider); OSSL_PROVIDER_unload(tpm2_provider); } else { supports_tpm = false; } - // Load default again - OSSL_PROVIDER_load(nullptr, evse_security::PROVIDER_DEFAULT); + // Load default again (removed - not needed and causes a memory leak) + // OSSL_PROVIDER_load(nullptr, evse_security::PROVIDER_DEFAULT); + + OSSL_LIB_CTX_free(libctx); std::cout << "Supports TPM usage: " << supports_tpm << std::endl; return supports_tpm; From 1d564344e365d225c18bf145baa66d102cfd0af3 Mon Sep 17 00:00:00 2001 From: James Chapman Date: Mon, 19 Feb 2024 11:45:21 +0100 Subject: [PATCH 2/2] fix: QA issue Signed-off-by: James Chapman --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 14f4aec..514b750 100644 --- a/README.md +++ b/README.md @@ -37,11 +37,13 @@ make test ``` ## Certificate Signing Request + There are two configuration options that will add a DNS name and IP address to the -subject alternative name in the certificate signing request. +subject alternative name in the certificate signing request. By default they are not added. -- `cmake -DCSR_DNS_NAME=charger.pionix.de ...` to include a DNS name -- `cmake -DCSR_IP_ADDRESS=192.168.2.1 ...` to include an IPv4 address + +- `cmake -DCSR_DNS_NAME=charger.pionix.de ...` to include a DNS name +- `cmake -DCSR_IP_ADDRESS=192.168.2.1 ...` to include an IPv4 address ## TPM There is a configuration option to configure OpenSSL for use with a TPM.
@@ -52,6 +54,7 @@ The library will use the `UseTPM` flag and the PEM private key file to configure whether to use the `default` provider or the `tpm2` provider. Configuration is managed via propquery strings (see CMakeLists.txt) + - `PROPQUERY_DEFAULT` is the string to use when selecting the default provider - `PROPQUERY_TPM2` is the string to use when selecting the tpm2 provider @@ -63,6 +66,7 @@ propquery|action "?provider=tpm2,tpm2.digest!=yes"|prefer the tpm2 provider but not for message digests For more information see: + - [Provider for integration of TPM 2.0 to OpenSSL 3.x](https://github.com/tpm2-software/tpm2-openssl) - [OpenSSL property](https://www.openssl.org/docs/man3.0/man7/property.html) - [OpenSSL provider](https://www.openssl.org/docs/man3.0/man7/provider.html)