feat(cert-create): add pkcs11 engine support

Add pkcs11 engine support which allows using keys that are securely
stored on a HSM or TPM. To use this feature the user has to supply
an RFC 7512 compliant PKCS11 URI to a key instead of a file as an
argument to one of the key options. This change is fully backwards
compatible.

This change makes use of the openssl engine API which is deprecated
since openssl 3.0 and will most likely be removed in version 4. So
pkcs11 support will have to be updated to the openssl provider API
in the near future.

Signed-off-by: Robin van der Gracht <robin@protonic.nl>
Change-Id: If96725988ca62c5613ec59123943bf15922f5d1f
diff --git a/tools/cert_create/src/key.c b/tools/cert_create/src/key.c
index dc953d7..32229d1 100644
--- a/tools/cert_create/src/key.c
+++ b/tools/cert_create/src/key.c
@@ -9,7 +9,11 @@
 #include <stdlib.h>
 #include <string.h>
 
+/* Suppress OpenSSL engine deprecation warnings */
+#define OPENSSL_SUPPRESS_DEPRECATED
+
 #include <openssl/conf.h>
+#include <openssl/engine.h>
 #include <openssl/evp.h>
 #include <openssl/pem.h>
 
@@ -189,29 +193,69 @@
 	return 0;
 }
 
+static EVP_PKEY *key_load_pkcs11(const char *uri)
+{
+	char *key_pass;
+	EVP_PKEY *pkey;
+	ENGINE *e;
+
+	ENGINE_load_builtin_engines();
+	e = ENGINE_by_id("pkcs11");
+	if (!e) {
+		fprintf(stderr, "Cannot Load PKCS#11 ENGINE\n");
+		return NULL;
+	}
+
+	if (!ENGINE_init(e)) {
+		fprintf(stderr, "Cannot ENGINE_init\n");
+		goto err;
+	}
+
+	key_pass = getenv("PKCS11_PIN");
+	if (key_pass) {
+		if (!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0)) {
+			fprintf(stderr, "Cannot Set PKCS#11 PIN\n");
+			goto err;
+		}
+	}
+
+	pkey = ENGINE_load_private_key(e, uri, NULL, NULL);
+	if (pkey)
+		return pkey;
+err:
+	ENGINE_free(e);
+	return NULL;
+
+}
+
 int key_load(key_t *key, unsigned int *err_code)
 {
 	FILE *fp;
 
 	if (key->fn) {
-		/* Load key from file */
-		fp = fopen(key->fn, "r");
-		if (fp) {
-			key->key = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
-			fclose(fp);
-			if (key->key) {
-				*err_code = KEY_ERR_NONE;
-				return 1;
+		if (!strncmp(key->fn, "pkcs11:", 7)) {
+			/* Load key through pkcs11 */
+			key->key = key_load_pkcs11(key->fn);
+		} else {
+			/* Load key from file */
+			fp = fopen(key->fn, "r");
+			if (fp) {
+				key->key = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
+				fclose(fp);
 			} else {
-				ERROR("Cannot load key from %s\n", key->fn);
-				*err_code = KEY_ERR_LOAD;
+				WARN("Cannot open file %s\n", key->fn);
+				*err_code = KEY_ERR_OPEN;
 			}
+		}
+		if (key->key) {
+			*err_code = KEY_ERR_NONE;
+			return 1;
 		} else {
-			WARN("Cannot open file %s\n", key->fn);
-			*err_code = KEY_ERR_OPEN;
+			ERROR("Cannot load key from %s\n", key->fn);
+			*err_code = KEY_ERR_LOAD;
 		}
 	} else {
-		VERBOSE("Key filename not specified\n");
+		VERBOSE("Key not specified\n");
 		*err_code = KEY_ERR_FILENAME;
 	}
 
@@ -223,6 +267,10 @@
 	FILE *fp;
 
 	if (key->fn) {
+		if (!strncmp(key->fn, "pkcs11:", 7)) {
+			ERROR("PKCS11 URI provided instead of a file");
+			return 0;
+		}
 		fp = fopen(key->fn, "w");
 		if (fp) {
 			PEM_write_PrivateKey(fp, key->key,