diff --git a/README.md b/README.md
index 6c52ab6..514b750 100644
--- a/README.md
+++ b/README.md
@@ -36,6 +36,15 @@ 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`
@@ -45,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
@@ -56,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)
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;