diff --git a/README.md b/README.md
index 514b750..2106346 100644
--- a/README.md
+++ b/README.md
@@ -36,6 +36,17 @@ make -j$(nproc) install
make test
```
+## Certificate Structure
+
+We allow any certificate structure with the following recommendations:
+
+- Root CA certificate directories/bundles should not overlap leaf certificates
+- It is not recommended to store any SUBCAs in the root certificate bundle (if using files)
+
+**Important:** when requesting leaf certificates with [get_key_pair](https://github.com/EVerest/libevse-security/blob/5cd5f8284229ffd28ae1dfed2137ef194c39e732/lib/evse_security/evse_security.cpp#L820) care should be taken if you require the full certificate chain.
+
+If a full chain is **Leaf->SubCA2->SubCA1->Root**, it is recommended to have the root certificate in a single file, **V2G_ROOT_CA.pem** for example. The **Leaf->SubCA2->SubCA1** should be placed in a file e.g. **SECC_CERT_CHAIN.pem**.
+
## Certificate Signing Request
There are two configuration options that will add a DNS name and IP address to the
@@ -45,6 +56,8 @@ 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
+When receiving back a signed CSR, the library will take care to create two files, one containing the **Leaf->SubCA2->SubCA1** chain and another containing the single **Leaf**. When they both exist, the return of [get_key_pair](https://github.com/EVerest/libevse-security/blob/5cd5f8284229ffd28ae1dfed2137ef194c39e732/include/evse_security/evse_types.hpp#L126) will contain a path to both the single file and the chain file.
+
## TPM
There is a configuration option to configure OpenSSL for use with a TPM.
`cmake` ... `-DUSING_TPM2=ON`
diff --git a/include/evse_security/evse_security.hpp b/include/evse_security/evse_security.hpp
index dfa2c96..1c99fad 100644
--- a/include/evse_security/evse_security.hpp
+++ b/include/evse_security/evse_security.hpp
@@ -24,10 +24,10 @@ struct LinkPaths {
};
struct DirectoryPaths {
- fs::path csms_leaf_cert_directory;
- fs::path csms_leaf_key_directory;
- fs::path secc_leaf_cert_directory;
- fs::path secc_leaf_key_directory;
+ fs::path csms_leaf_cert_directory; /**< csms leaf certificate for OCPP shall be located in this directory */
+ fs::path csms_leaf_key_directory; /**< csms leaf key shall be located in this directory */
+ fs::path secc_leaf_cert_directory; /**< secc leaf certificate for ISO15118 shall be located in this directory */
+ fs::path secc_leaf_key_directory; /**< secc leaf key shall be located in this directory */
};
struct FilePaths {
// bundle paths
@@ -58,10 +58,18 @@ class EvseSecurity {
public:
/// @brief Constructor initializes the certificate and key storage using the given \p file_paths for the different
- /// PKIs. For CA certificates CA bundle files must be specified. For the SECC and CSMS leaf certificates,
- /// directories are specified.
+ /// PKIs. For CA certificates either CA bundle files or directories containing the certificates can be specified.
+ /// For the SECC and CSMS leaf certificates, directories must be specified.
/// @param file_paths specifies the certificate and key storage locations on the filesystem
/// @param private_key_password optional password for encrypted private keys
+ /// @param max_fs_usage_bytes optional maximum filesystem usage for certificates. Defaults to
+ /// 'DEFAULT_MAX_FILESYSTEM_SIZE'
+ /// @param max_fs_certificate_store_entries optional maximum certificate entries. Defaults to
+ /// 'DEFAULT_MAX_CERTIFICATE_ENTRIES'
+ /// @param csr_expiry optional expiry time for a CSR entry. If the time is exceeded it will be deleted. Defaults to
+ /// 'DEFAULT_CSR_EXPIRY'
+ /// @param garbage_collect_time optional garbage collect time. How often we will delete expired CSRs and
+ /// certificates. Defaults to 'DEFAULT_GARBAGE_COLLECT_TIME'
EvseSecurity(const FilePaths& file_paths, const std::optional& private_key_password = std::nullopt,
const std::optional& max_fs_usage_bytes = std::nullopt,
const std::optional& max_fs_certificate_store_entries = std::nullopt,
@@ -71,15 +79,17 @@ class EvseSecurity {
/// @brief Destructor
~EvseSecurity();
- /// @brief Installs the given \p certificate within the specified CA bundle file
+ /// @brief Installs the given \p certificate within the specified CA bundle file or directory if directories are
+ /// used. If the certificate already exists it will only be updated
/// @param certificate PEM formatted CA certificate
/// @param certificate_type specifies the CA certificate type
/// @return result of the operation
InstallCertificateResult install_ca_certificate(const std::string& certificate, CaCertificateType certificate_type);
/// @brief Deletes the certificate specified by \p certificate_hash_data . If a CA certificate is specified, the
- /// certificate is removed from the bundle. If a leaf certificate is specified, the file will be removed from the
- /// filesystem
+ /// certificate is removed from the bundle or directory. If a leaf certificate is specified, the file will be
+ /// removed from the filesystem. It will also delete all certificates issued by this certificate, so that no invalid
+ /// hierarchies persisted on the filesystem
/// @param certificate_hash_data specifies the certificate to be deleted
/// @return result of the operation
DeleteCertificateResult delete_certificate(const CertificateHashData& certificate_hash_data);
@@ -94,7 +104,10 @@ class EvseSecurity {
/// @brief Verifies the given \p certificate_chain for the given \p certificate_type against the respective CA
/// certificates for the leaf and if valid installs the certificate on the filesystem. Before installing on the
- /// filesystem, this function checks if a private key is present for the given certificate on the filesystem.
+ /// filesystem, this function checks if a private key is present for the given certificate on the filesystem. Two
+ /// files are installed, one containing the single leaf (presuming it is the first in the chain) and also the full
+ /// certificate chain. The \ref get_key_pair function will return a path to both files if they exist, the one
+ /// containing the single leaf, and the file containing the leaf including the SUBCAs if present
/// @param certificate_chain PEM formatted certificate or certificate chain
/// @param certificate_type type of the leaf certificate
/// @return result of the operation
@@ -130,6 +143,7 @@ class EvseSecurity {
void update_ocsp_cache(const CertificateHashData& certificate_hash_data, const std::string& ocsp_response);
/// @brief Indicates if a CA certificate for the given \p certificate_type is installed on the filesystem
+ /// Supports both CA certificate bundles and directories
/// @param certificate_type
/// @return true if CA certificate is present, else false
bool is_ca_certificate_installed(CaCertificateType certificate_type);
@@ -162,7 +176,9 @@ class EvseSecurity {
/// @brief Searches the filesystem on the specified directories for the given \p certificate_type and retrieves the
/// most recent certificate that is already valid and the respective key. If no certificate is present or no key is
- /// matching the certificate, this function returns std::nullopt
+ /// matching the certificate, this function returns a GetKeyPairStatus other than "Accepted". The function \ref
+ /// update_leaf_certificate will install two files for each leaf, one containing the single leaf and one containing
+ /// the leaf including any possible SUBCAs
/// @param certificate_type type of the leaf certificate
/// @param encoding specifies PEM or DER format
/// @return contains response result
@@ -172,7 +188,9 @@ class EvseSecurity {
/// @return true if one of the links was updated
bool update_certificate_links(LeafCertificateType certificate_type);
- /// @brief Retrieves the PEM formatted CA bundle file for the given \p certificate_type
+ /// @brief Retrieves the PEM formatted CA bundle file for the given \p certificate_type It is not recommended to
+ /// add the SUBCAs to any root certificate bundle, but to leave them in the leaf file. See \ref
+ /// update_leaf_certificate
/// @param certificate_type
/// @return CA certificate file
std::string get_verify_file(CaCertificateType certificate_type);
@@ -182,8 +200,10 @@ class EvseSecurity {
/// @return day count until the leaf certificate expires
int get_leaf_expiry_days_count(LeafCertificateType certificate_type);
- /// @brief Collects and deletes unfulfilled CSR private keys. If also deleting the expired
- /// certificates, make sure the system clock is properly set for detecting expired certificates
+ /// @brief Collects and deletes unfulfilled CSR private keys. It also deletes the expired
+ /// certificates. The caller must be sure the system clock is properly set for detecting expired
+ /// certificates. A minimum of 'DEFAULT_MINIMUM_CERTIFICATE_ENTRIES' certificates to
+ /// have a safeguard against a poorly set system clock
void garbage_collect();
/// @brief Verifies the file at the given \p path using the provided \p signing_certificate and \p signature