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]

Reply via email to