Apache HTTP Server configuration for PKCS#11

In the presented usage scenario, you learn how to configure an Apache HTTP Server to protect its private keys within an HSM that is addressable via a selected PKCS#11 provider configured for an OpenSSL 3.0 library. Configuration instructions for both of the previously introduced providers are included.

The use case descried in this topic is very similar to the one described in OpenSSL 1.1.1: Apache HTTP Server setup with the PKCS#11 libp11 engine. However, because OpenSSL engines are deprecated starting with OpenSSL 3.0, a new OpenSSL provider-based solution is required.

Description

The private key of an Apache HTTP Server is one of the most sensitive components since it is used to authenticate the server with the client during TLS handshake. Using a non-exportable key that is protected in an HSM, increases the web server’s security, because the private key can not be stolen when safe-guarded by the HSM.

The Apache HTTP Server implements the TLS protocol with the help of the mod_ssl module which uses OpenSSL to implement the TLS protocol and its crypto operations. It does not implement PKCS#11, so a PKCS#11-based HSM interface can not directly be used by mod_ssl.

You can use a suitable PKCS#11 provider to bridge between OpenSSL and PKCS#11. To enable the use of such a provider, OpenSSL provider-specific code has been added to the Apache HTTP Server's mod_ssl module with upstream version 2.4.59.

The HSM-protected private key, and optionally also the certificate, are specified by their PKCS#11 URI (see RFC 7512) in the Apache HTTP Server's configuration file (httpd.conf) with keywords SSLCertificateKeyFile <uri> and SSLCertificateFile <uri>.

The certificate can optionally also be stored as PKCS#11 certificate object, or as PEM file in the file system.

For use with providers, the Apache HTTP Server's configuration file must not specify an engine, that is, keyword SSLCryptoDevice must not be specified, or must be specified as SSLCryptoDevice builtin.

The provider used with mod_ssl must be configured in the OpenSSL configuration file (for example, openssl.cnf). This file must define and configure the provider in its own section. The provider must support the PKCS#11 URI scheme with its STORE implementation to be usable with mod_ssl. The OpenSSL functions OSSL_STORE_open, OSSL_STORE_load, OSSL_STORE_close, and so on, are used with the PKCS#11 URI specified with SSLCertificateKeyFile <uri> and SSLCertificateFile <uri> to load the private key and certificate.

Note: Both providers described in sections OpenSSL 3.0: PKCS#11 provider for signing operations and OpenSSL 3.0: PKCS#11 general provider fulfill these requirements.

An HSM-protected key that was loaded through an URI from a provider is most likely non-exportable. Thus, it can not be exported from the PKCS#11 provider. This means that crypto operations with such a non-exportable key must be performed through the same provider it was loaded with.

OpenSSL would normally select the provider for performing crypto operations based on the supplied property query. However, if a provider is selected that is different from the one with which the non-exportable key was loaded, the key must be exported from the originating provider, and imported into the provider that was selected for the crypto operation. This will fail with a non-exportable key.

If OpenSSL encounters such an export failure, it tries to find the desired crypto operation in the provider that key was loaded with. If it finds the crypto operation there, then it is performed by the originating provider, thus fulfilling the conditions for non-exportable keys.

Note: As both provides described in the previous sections do provide all required crypto operations, both can be used even in such a case, when a provider with non-exportable keys is supplied by the property query.

Setting up the prerequisites

A PKCS#11 implementation is required, like for example, openCryptoki as in this use case.

Install and setup openCryptoki and configure the tokens to use. For HSM-protected private keys, use either the CCA token or the EP11 token to ensure secure key cryptography. You find helpful information in openCryptoki - An Open Source Implementation of PKCS #11.

Install and setup an applicable provider, for example, one of the previously mentioned ones. Select the provider to use depending on your ways of using the Apache HTTP Server.

OpenSSL configuration for using the pkcs11-sign-provider


openssl_conf = openssl_def 

[openssl_def] 
providers = provider_sect 

[provider_sect] 
default = default_sect 
pkcs11sign = pkcs11sign_sect 

[default_sect] 
activate = 1 

[pkcs11sign_sect] 
module = /path/to/pkcs11sign.so 
identity = pkcs11sign 
pkcs11sign-module-path = /path/to/libopencryptoki.so 
pkcs11sign-forward = provider=default 
activate = 1 

Considerations concerning the pkcs11-sign-provider: The pkcs11-sign-provider does currently not support certificates. A workaround is to supply the server’s certificate in a PEM file in the file system. The pkcs11-sign-provider does also not support to prompt for the PKCS#11 PIN during key loading. That is, the PIN must be specified via URI (directly or as file reference).

OpenSSL configuration for using the pkcs11-provider


openssl_conf = openssl_def 

[openssl_def] 
providers = provider_sect 

[provider_sect] 
default = default_sect 
pkcs11 = pkcs11_sect 

[pkcs11_sect] 
module = /path/to/pkcs11.so 
identity = pkcs11 
pkcs11-module-path = /path/to/libopencryptoki.so 
pkcs11-module-load-behavior = early 
activate = 1 

Considerations concerning the pkcs11-provider: The configuration option pkcs11-module-load-behavior = early is required in the pkcs11-provider configuration in the OpenSSL configuration file. Without this option, the provider does not properly report that it supports signatures. However, in contrast to the pkcs11-sign-provider, the pkcs11-provider does support to prompt for the PKCS#11 PIN when the key object is being loaded. This avoids that the PIN must be specified as part of the URI or in the OpenSSL configuration file. A pass phrase prompt like the following is shown when starting the Apache HTTP Server:

Apache/x.y.z-dev mod_ssl (Pass Phrase Dialog)
A pass phrase is required to access the private key.

Private key www.example.com:443
(pkcs11:manufacturer=IBM;model=EP11;serial=93AADFK711976872;token=ep11;
id=%00%11%22%33%44%55%66%77;object=httpd%20server%20key:prv;type=private)
Enter pass phrase for PKCS#11 Token (Slot 4 - Linux):

Checking your provider: To check whether the providers are active, use the command openssl list -providers. Also, use the command openssl list -signature-algorithms to show that the selected provider supports ECDSA and RSA.

If you do not want to define the provider in the global OpenSSL configuration file, you can use a specific OpenSSL configuration file just for the use with this scenario, and set the OPENSSL_CONF environment variable to point to the configuration file to use. Make sure that this environment variable is set or exported whenever running commands and using the Apache HTTP Server later on.

Generate the server’s private key and certificate

You must generate a private and public key pair for use with the Apache HTTP Server and have the private key protected in the HSM. This can either be an RSA key pair or an ECC key pair. To generate an RSA key pair with openCryptoki and the EP11 token, use a command similar to the following example, where the EP11 token is identified by the slot number 4 and the user PIN of this token is prompted:


# p11sak generate-key rsa 2048 --label "httpd server key" \ 
                               --id 0011223344556677 --slot 4 

Please enter user PIN: 
Successfully generated a RSA key pair with labels 
         "httpd server key:pub":"httpd server key:prv"

The ID (--id) is optional, but the pkcs11-provider requires that the private key and the corresponding public key have the same ID.

List the just generated keys to obtain the private key’s PKCS#11 URI:


# p11sak list-key all --long --slot 4 
Please enter user PIN: 
... 
Label: "httpd server key:prv" 
URI: pkcs11:manufacturer=IBM;model=EP11;serial=93AADFK711976872;token=ep11; 
     id=%00%11%22%33%44%55%66%77;object=httpd%20server%20key:prv;type=private 
Key: private RSA 2048 
... 

If you choose to supply the PKCS#11 PIN as part of the URI, add it to the URI as follows:

...;type=private?pin-source=/path/to/file-containing-the-pin 

Make sure that the path to the file-containing-the-pin is only readable by the user(s) under which the Apache HTTP Server runs. Alternatively, specify the PIN directly within the URI (which is, however, very insecure):

 ...;type=private?pin-value=<pin>

Optional: Export the public key:


# p11sak export-key rsa --file /path/to/httpd-public-key.pem \ 
                        --label "httpd server key:pub" --slot 4 
Please enter user PIN: 
Are you sure you want to export public RSA 2048 key object "httpd server key:pub" [y/n/a/c]? y 
Successfully exported public RSA 2048 key object "httpd server key:pub" to file ’/path/to/httpd-public-key.pem’. 
1 key object(s) exported.

Generate a server certificate. Figure 1 shows a simple example, how to create a self-signed certificate with the help of OpenSSL:

Figure 1. Server certificate generation

# openssl req -new -x509 -out /path/to/httpd-certificate.crt \ 
                   -key "<full-pkcs11-uri-including-pin-option>" 
You are about to be asked to enter information that will be incorporated 
into your certificate request. 
What you are about to enter is what is called a Distinguished Name or a DN. 
There are quite a few fields but you can leave some blank. 
For some fields there will be a default value. 
If you enter ’.’, the field will be left blank. 
----- 
Country Name (2 letter code) [XX]:DE 
State or Province Name (full name) []:Germany 
Locality Name (eg, city) [Default City]:Boeblingen 
Organization Name (eg, company) [Default Company Ltd]:IBM 
Organizational Unit Name (eg, section) []: 
Common Name (eg, your name or your server’s hostname) []:www.example.com 
Email Address []:username@de.company.com 

As shown in the previous example, you are asked to enter the certificate attributes. If you want to generate a CA-signed certificate instead, omit the -x509 option. Thus, the command generates a certificate signing request instead, that you pass to the CA. With this request you let the CA sign the certificate and issue a CA signed certificate for you.

These commands use the HSM-protected private key via the configured PKCS#11 provider. Thus, the signature that is part of the certificate (signing request) is performed inside the HSM with the HSM-protected private key. It is important that you have already configured the used PKCS#11 provider properly when you run these commands.

Optional: Import the just generated certificate into openCryptoki:


# p11sak import-cert x509 --file /path/to/httpd-certificate.crt \ 
                          --label "https certificate" --id 0011223344556677 --slot 4 
Please enter user PIN: 
Successfully imported a X.509 certificate with label "https certificate"

The ID (--id) is optional, but it is useful to let the certificate and the corresponding private key have the same ID.

Optional: List the just imported certificate to obtain the certificate’s PKCS#11 URI:


# p11sak list-cert x509 --long --slot 4 
Please enter user PIN: 
Label: "https certificate" 
URI: pkcs11:manufacturer=IBM;model=EP11;serial=93AADFK711976872;token=ep11; 
            id=%00%11%22%33%44%55%66%77;object=https%20certificate;type=cert 
Certificate: X.509 
...

Configure the Apache HTTP Server to use the HSM-protected private key

The Apache HTTP Server configuration is supplied in a configuration file typically named httpd.conf. Here are the relevant settings for enabling the TLS protocol (that is, HTTPS) with an HSM-protected private key.


Listen 443 
SSLCryptoDevice builtin # or ommit this setting 
<VirtualHost *:443> 
    ServerName www.example.com 
    SSLEngine on 
    SSLCertificateFile "/path/to/httpd-certificate.crt" 
    SSLCertificateKeyFile "<full-pkcs11-uri-including-pin-option>" 
</VirtualHost> 

Instead of specifying the certificate file, you can also specify its PKCS#11 URI with option SSLCertificateFile "<full-pkcs11-cert-uri-including-pin-option>". This requires that you have imported the certificate into openCryptoki as previously shown and this is only supported with the pkcs11-provider.

Running the Apache HTTP Server

You can run the Apache HTTP Server in debug mode (with option -X, as shown in the following screen shot), or in normal mode.

To start the server in debug mode, enter:

httpd -X

The server then runs in foreground and does not fork. This is, in addition, also beneficial for debugging. However, do not run the server in debug mode on a production system.

The user under which the Apache HTTP Server runs must be allowed to use the PKCS#11 implementation. For use with openCryptoki, the user must be part of the group that owns the token directory, which by default is the pkcs11 group. Other PKCS#11 implementations may have other requirements.

To test if the web server is accepting requests, use the wget or curl commands:


wget --no-check-certificate https://localhost 
curl --insecure https://localhost --verbose

Use the --no-check-certificate option with wget or the --insecure option with curl, if the server’s certificate is a self-signed certificate that would not pass certificate validation otherwise.

You can monitor the usage of PKCS#11 mechanisms by the Apache HTTP Server using the pkcsstats tool of openCryptoki. With an RSA private key, each TLS handshake increases the used mechanism's counter, for example, for the CKM_SHA256_RSA_PKCS_PSS mechanism. The applied mechanism of course depends on the used TLS cipher suite and on the type of the web server’s private key (RSA or EC), as well as the digest algorithm used in the certificate.


Slot: 4 (label: ’ep11’ model: ’EP11’) 
-------------------------------+----------------------------------.... 
mechanism                      | strength 0      strength 112     .... 
                               | or no key                        .... 
-------------------------------+----------------------------------.... 
CKM_SHA256_RSA_PKCS_PSS        |          0                 1     .... 
-------------------------------+----------------------------------....