OpenSSL 1.1.1: Apache HTTP Server setup with the PKCS#11 libp11 engine

The libp11 engine is a library designed to call from within OpenSSL certain cryptographic functions provided by a PKCS#11 API (for example, functions implemented by an HSM or a smart card).

Note: Do not use engines with OpenSSL versions later than 1.1.1. Therefore, only use the libp11 engine for OpenSSL versions up to 1.1.1.

One way to connect OpenSSL with PKCS#11 is via the libp11 engine provided by the OpenSC project. This engine makes the operations provided by a specific PKCS#11 token available to OpenSSL. It makes it easier to use PKCS#11 in applications without having to program against the PKCS#11 API called Cryptoki.

This use case describes how to create and use a secure RSA or EC signing key for the Apache HTTP Server. It solves the problem that the server’s private key is vulnerable when being stored in an operating system’s file system or computer memory. The setup uses mod_ssl and OpenSSL libp11 engine, which is part of the OpenSC project. The engine uses openCryptoki via its PKCS#11 interface. In openCryptoki, the CCA and the EP11 token both support the secure key functionality. Figure 1 shows the overall setup.

Figure 1. libp11 engine use case setup
A picture showing the libp11 engine use case setup

Hardware prerequisites

Hardware prerequisites are a cryptographic coprocessor either configured in CCA mode or in EP11 mode with a valid master key setup.

Software prerequisites

Note: With OpenSSL 3.0, the engine concept is deprecated.
Software prerequisites are the CCA or EP11 host libraries installed. In addition to these IBM host libraries, several distribution-specific software packages are required:
  • OpenSSL
  • the p11tool as part of the GnuTLS utilities
  • the libp11 engine (0.4.10 or later)
  • the Apache HTTP Server and mod_ssl provided by the OpenSC project.

Sample setup on Red Hat Enterprise Linux

The following describes the setup on Red Hat Enterprise Linux with OpenSSL 1.1.1. Install the CCA and EP11 host libraries:

# rpm -ivh ep11-host-3.0.1-1.s390x.rpm
# rpm -ivh ep11-host-devel-3.0.1-1.s390x.rpm
# rpm -ivh csulcca-6.0.13-10.s390x.rpm

You can obtain the host libraries from the following URL:


Linux on Z software
Installation:

Install the following packages:


# yum -y install "opencryptoki*" 
# yum -y install gnutls-utils 
# yum -y install "libp11*" 
# yum -y install "httpd*" mod_ssl

Configuration:

Perform the following configuration steps:

  1. Configure the p11tool to work with openCryptoki: The p11tool needs to know where the openCryptoki PKCS#11 module is located. This is configured by creating a new plain text file in /etc/pkcs11/modules. You can call this file opencryptoki.module, for example. The file contains one single line:
    module: /usr/lib64/opencryptoki/PKCS11_API.so

    After creating the file, you can display available openCryptoki tokens and token URLs. In PKCS#11, tokens and key objects are identified by Uniform Resource Identifiers (URIs) specified in RFC7512. The p11tool refers to URIs as URLs.

    
    # p11tool --list-tokens 
    # p11tool --list-token-urls
    pkcs11:model=IBM%20CCA%20Token;manufacturer=IBM%20Corp.;serial=123;token=ccatok
    pkcs11:model=EP11;manufacturer=IBM;serial=93AABEC895428184;token=ep11tok 
    ... 
    
  2. Configure the libp11 engine: Apply the following changes in the openssl.cnf file:
    
    [openssl_init] 
    engines = engine_section
    ssl_conf = ssl_module 
    ... 
    [engine_section] 
    pkcs11 = pkcs11_section 
    [pkcs11_section] 
    engine_id = pkcs11 
    dynamic_path = /usr/lib64/engines-3/libpkcs11.so 
    MODULE_PATH = /usr/lib64/opencryptoki/PKCS11_API.so 
    init = 0 
    
    Note: The statement init = 0 means that the engine is not globally active but will be activated by applications explicitly, in this case by the Apache HTTP Server (httpd) and not by OpenSSL.

    With these changes to the openssl.cnf file, you can check if the engine is active in OpenSSL:

    
    # openssl engine -c 
    (dynamic) Dynamic engine loading  support 
    (pkcs11) pkcs11 engine 
     [RSA, rsaEncryption, id-ecPublicKey]
    
  3. Generate a new secure RSA key: As the token URL is part of many subsequently used p11tool commands, put it into a variable:
    
    # url="pkcs11:model=EP11;manufacturer=IBM;serial=93AABEC895428184;token=ep11tok" 
    
    It can then be referenced as "$url":
    
    # p11tool --login --generate-rsa --bits 2048 --outfile /etc/ssl/ep11rsa.key --label ep11rsa "$url" 
    
    Note: In the output file ep11rsa.key, only the public key is stored, which, however, is not used furthermore.

    After generating the RSA key, you can list it. The output shows two key objects: the public key and the private key. In the following, you need the private key URL.

    
    # p11tool --login --list-all "$url"
    
     Token 'ep11tok' with URL
    'pkcs11:model=EP11;manufacturer=IBM;serial=93AABEC895428184;token=ep11tok' 
    requires user PIN 
    Enter PIN:
     
    Object 0: 
           URL:
    pkcs11:model=EP11;manufacturer=IBM;serial=93AABEC895428184;
    token=ep11tok;id=%35%80%33%7B%3A%20%1A%39%41%3F%60%B4%EA%42%FE%02%E6%F8%39%5F;
    object=ep11rsa;type=private
           Type: Private key (RSA-2048) 
           Label: ep11rsa 
           Flags: CKA_WRAP/UNWRAP; CKA_PRIVATE; CKA_EXTRACTABLE; CKA_SENSITIVE; 
           ID: 35:80:33:7b:3a:20:1a:39:41:3f:60:b4:ea:42:fe:02:e6:f8:39:5f 
    
    Object 1: 
           URL:
    pkcs11:model=EP11;manufacturer=IBM;serial=93AABEC895428184;
    token=ep11tok;id=%35%80%33%7B%3A%20%1A%39%41%3F%60%B4%EA%42%FE%02%E6%F8%39%5F;
    object=ep11rsa;type=public
           Type: Public key (RSA-2048) 
           Label: ep11rsa 
           Flags: CKA_WRAP/UNWRAP; 
           ID: 35:80:33:7b:3a:20:1a:39:41:3f:60:b4:ea:42:fe:02:e6:f8:39:5f
    
  4. Create the secure key certificate: This step creates a TLS certificate, signed by the secure RSA key.
    
    # key="pkcs11:model=EP11;manufacturer=IBM;serial=93AABEC895428184;
    token=ep11tok;id=%35%80%33%7B%3A%20%1A%39%41%3F%60%B4%EA%42%FE%02%E6%F8%39%5F;
    object=ep11rsa;type=private"      /* must be all in one line */
    
    # openssl req -x509 -new -engine pkcs11 -keyform engine -subj 
    "/C=DE/ST=BW/L=Boeblingen/O=IBM R&D/OU=Linux on Z/CN=ep11rsa cert" 
    -key "$key" -out /etc/ssl/ep11rsa.crt    /* must be all in one line */
     

    The certificate is now stored in /etc/ssl/ep11rsa.crt.

    Note: Place the certificate file at an appropriate location so that the Apache HTTP Server is allowed to access it.
  5. Configure the Apache web server: On Red Hat Enterprise Linux, the Apache configuration is located in /etc/httpd/. Here, change file /etc/httpd/conf.d/ssl.conf as follows:

    Specify your certificate file ep11rsa.crt for parameter SSLCertificateFile and specify the RSA key URL for parameter SSLCertificateKeyFile.

    Note: Enclose the key URL in double quotes:
    
    SSLCertificateFile /etc/ssl/ep11rsa.crt 
    SSLCertificateKeyFile
    "pkcs11:model=EP11;manufacturer=IBM;serial=93AABEC895428184;token=ep11tok;
    id=%35%80%33%7B%3A%20%1A%39%41%3F%60%B4%EA%42%FE%02%E6%F8%39%5F;
    object=ep11rsa;type=private" 
    

Testing

You can now immediately test the setup using the OpenSSL utilities s_server and s_client. This test verifies the correct key and certificate setup, but does not involve the Apache web server.

Test with s_server / s_client:


# openssl s_server -engine pkcs11 -keyform engine \ 
                   -key "$key" -cert /etc/ssl/ep11rsa.crt \ 
                   -debug -msg \ 
                   -accept 443 -tls1_2 -www 

Engine "pkcs11" set. 
Enter PKCS#11 token PIN for ep11tok: 
Using default temp DH parameters 
ACCEPT ... 
>>> TLS 1.2, Handshake [length 0010], Finished 
    14 00 00 0c c9 29 9d 4b de 21 82 03 58 74 fa 26 
write to 0x2aa22939a50 [0x2aa22a0b7a0] (242 bytes => 242 (0xF2)) 

The s_server is now in a listening state and you can use the s_client to connect to the s_server:


# openssl s_client -debug -msg -connect <ip_address>:443 
...
SSL-Session: 
    Protocol  : TLSv1.2 
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384 
    Session-ID: C3699D42DE586D9209078A9115A3D2A1214CF14E5E035A33E8BC47FED66C2D24 
    Session-ID-ctx: 
    Master-Key: 9BE04...D7753FF1838FE6FE7128CFF82DA0F04EB4699 
...

A successful connection shows the protocol and used TLS cipher suite.

Test with Apache web server

You can use the httpd -X parameter to start Apache single-threaded. You are prompted for the user PIN of the openCryptoki EP11 token:


# httpd -X 
Apache/2.4.51 mod_ssl (Pass Phrase Dialog) 
A pass phrase is required to access the private key. 

Private key t3545012.lnxne.boe:443 (pkcs11:model=EP11;manufacturer=IBM;serial=93AABEC895428184;token=ep11tok;
id=%35%80%33%7B%3A%20%1A%39%41%3F%60%B4%EA%42%FE%02%E6%F8%39%5F;object=ep11rsa;type=private)
Enter PKCS#11 token PIN for ep11tok: 

After starting httpd, use s_client to connect to the web server.


# openssl s_client -debug -msg -connect t3545012:443 
... 
SSL-Session: 
    Protocol : TLSv1.3
    Cipher   : TLS_AES_256_GCM_SHA384 ... 

Again, a successful connection shows the protocol and TLS cipher suite.

Starting Apache multi-threaded, is usually done via the systemctl command:

# systemctl start httpd

If you use the systemctl command in this way, your are not prompted for the PIN of the openCryptoki EP11 token. Instead, you must provide the PIN within the PKCS11 URL. You can either specify the PIN directly using the pin-value option, or by using the pin-source option pointing to a file that contains the PIN. The first alternative is quite insecure, because the PIN is in clear text in the configuration file. For the second alternative, you can protect the file by access controls.


Example for pin-value
SSLCertificateKeyFile "pkcs11:model=EP11; ..... ?pin-value=11223344" 

Example for pin-source
SSLCertificateKeyFile "pkcs11:model=EP11; ..... ?pin-source=file:/<path_to_pin-file>"