This is an automated email from the ASF dual-hosted git repository. cliffjansen pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/qpid-proton.git
commit 96cbea1052a2196d4659ae039992f4393a0005a9 Author: Ahmad Fatoum <[email protected]> AuthorDate: Thu May 23 19:23:46 2024 +0200 PROTON-2594: Add OpenSSL PKCS#11 PROVIDER support to enable HSM use Hardware Security Modules (HSMs) are physical computing devices that safeguard secrets and allow performing cryptographic operations like encryption and signing without necessarily divulging the private key material. PKCS#11 is a platform-independent API for cryptographic tokens like HSMs. It defines a scheme for pkcs11: URIs that describe objects on the token as well as an API to interact with them. This commit adds support for transparent use of PKCS#11 URIs: Whenever a certificate or private key path is prefixed with pkcs11: it will be interpreted as PKCS#11 URI instead of a file path and OpenSSL will do all necessary communication with the HSM behind the scenes. For programs like proton that use OpenSSL, this could have been realized in two ways: - OpenSSL ENGINE: Introduced in OpenSSL 0.9.6 and deprecated in 3.0 - OpenSSL PROVIDER: Introduced in OpenSSL 3.0 to replace ENGINE While both are supported in recent OpenSSL versions, it's more future proof to use the PROVIDER API, even if this comes at the cost of having to add an #ifdef to the code. This has been tested on Linux/x86_64 with softhsm and Linux/arm32 with OP-TEE, both with v0.5 of https://github.com/latchset/pkcs11-provider. As everything is loaded dynamically, we do not link against any PKCS#11-related shared libraries. It's expected that PKCS#11 provider will already be loaded via OPENSSL_CONF if it's to be used. --- c/src/ssl/openssl.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 113 insertions(+), 8 deletions(-) diff --git a/c/src/ssl/openssl.c b/c/src/ssl/openssl.c index 7455cb618..62d4b40a1 100644 --- a/c/src/ssl/openssl.c +++ b/c/src/ssl/openssl.c @@ -50,6 +50,13 @@ #include <openssl/dh.h> #include <openssl/err.h> #include <openssl/x509v3.h> + +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) +#include <openssl/provider.h> +#include <openssl/store.h> +#include <openssl/ui.h> +#endif + #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> @@ -611,27 +618,125 @@ void pn_ssl_domain_free( pn_ssl_domain_t *domain ) } } +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) +static int ui_read_string(UI *ui, UI_STRING *uis) +{ + return UI_set_result(ui, uis, UI_get0_user_data(ui)); +} + +static OSSL_STORE_INFO *pkcs11_provider_get_info( const char *uri, const char *pin, int type ) +{ + UI_METHOD *ui_method = NULL; + + if (pin) { + ui_method = UI_create_method("Pin Reader"); + if (!ui_method) { + ssl_log_error("Failed to set up UI_METHOD"); + return NULL; + } + UI_method_set_reader(ui_method, ui_read_string); + } + + OSSL_STORE_CTX *store = OSSL_STORE_open_ex(uri, NULL, "provider=pkcs11", + ui_method, (void *)pin, NULL, NULL, NULL); + if (!store) { + ssl_log_error("Failed to open store for provider=pkcs11\n"); + UI_destroy_method(ui_method); + return NULL; + } + + for (OSSL_STORE_INFO *info = OSSL_STORE_load(store); info; + info = OSSL_STORE_load(store)) { + if (type == OSSL_STORE_INFO_get_type(info)) { + OSSL_STORE_close(store); + UI_destroy_method(ui_method); + return info; + } + OSSL_STORE_INFO_free(info); + } + + OSSL_STORE_close(store); + UI_destroy_method(ui_method); + return NULL; +} + +static X509 *read_certificate_pkcs11( const char *uri, const char *key_pass ) +{ + OSSL_STORE_INFO *info = pkcs11_provider_get_info(uri, key_pass, OSSL_STORE_INFO_CERT); + if (!info) return NULL; + + X509 *cert = OSSL_STORE_INFO_get1_CERT(info); + OSSL_STORE_INFO_free(info); + return cert; +} + +static EVP_PKEY *read_private_key_pkcs11( const char *uri, const char *key_pass ) +{ + OSSL_STORE_INFO *info = pkcs11_provider_get_info(uri, key_pass, OSSL_STORE_INFO_PKEY); + if (!info) return NULL; + + EVP_PKEY *key = OSSL_STORE_INFO_get1_PKEY(info); + OSSL_STORE_INFO_free(info); + return key; +} +#else /* < OpenSSL 3.0 */ +static X509 *read_certificate_pkcs11( const char *uri, const char *key_pass ) { + ssl_log_error("pkcs11: URI support requires >= OpenSSL 3.0\n"); + return NULL; +} +static EVP_PKEY *read_private_key_pkcs11( const char *uri, const char *key_pass ) { + ssl_log_error("pkcs11: URI support requires >= OpenSSL 3.0\n"); + return NULL; +} +#endif + +static bool is_pkcs11_uri( const char *file_path ) +{ + return strncmp(file_path, "pkcs11:", sizeof("pkcs11:") - 1) == 0; +} int pn_ssl_domain_set_credentials( pn_ssl_domain_t *domain, const char *certificate_file, const char *private_key_file, const char *password) { + int rc; + if (!domain || !domain->ctx) return -1; - if (SSL_CTX_use_certificate_chain_file(domain->ctx, certificate_file) != 1) { - ssl_log_error("SSL_CTX_use_certificate_chain_file( %s ) failed", certificate_file); + if (is_pkcs11_uri(certificate_file)) { + X509 *cert = read_certificate_pkcs11(certificate_file, password); + if (cert) + rc = SSL_CTX_use_certificate(domain->ctx, cert); + else + rc = -1; + } else { + rc = SSL_CTX_use_certificate_chain_file(domain->ctx, certificate_file); + } + + if (rc != 1) { + ssl_log_error("Failed to load certificate %s", certificate_file); return -3; } - if (password) { - domain->keyfile_pw = pn_strdup(password); // @todo: obfuscate me!!! - SSL_CTX_set_default_passwd_cb(domain->ctx, keyfile_pw_cb); - SSL_CTX_set_default_passwd_cb_userdata(domain->ctx, domain->keyfile_pw); + if (is_pkcs11_uri(private_key_file)) { + EVP_PKEY *pkey = read_private_key_pkcs11(private_key_file, password); + if (pkey) + rc = SSL_CTX_use_PrivateKey(domain->ctx, pkey); + else + rc = -1; + } else { + if (password) { + domain->keyfile_pw = pn_strdup(password); // @todo: obfuscate me!!! + SSL_CTX_set_default_passwd_cb(domain->ctx, keyfile_pw_cb); + SSL_CTX_set_default_passwd_cb_userdata(domain->ctx, domain->keyfile_pw); + } + + rc = SSL_CTX_use_PrivateKey_file(domain->ctx, private_key_file, SSL_FILETYPE_PEM); } - if (SSL_CTX_use_PrivateKey_file(domain->ctx, private_key_file, SSL_FILETYPE_PEM) != 1) { - ssl_log_error("SSL_CTX_use_PrivateKey_file( %s ) failed", private_key_file); + if (rc != 1) { + ssl_log_error("Failed to load private key %s", private_key_file); return -4; } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
