Cryptography

This chapter contains information how to manage the crypto configuration of the Identity Server.

Configuring certificates

To add new certificates to the crypto configuration, you can have the admin node of the server have certificate files read from etc/init upon server startup. The accepted files are PEM- or DER-encoded X509-certificates, that are named with the appropriate file extension (i.e. .pem, .der, .cer or .crt). As certificates are always used with a particular purpose, an ordering is applied based on the directory that contains the particular certificate file. When a certificate is imported, the filename (without the extension) is used as its id.

The following directory structure is used:

directory certificate purpose
etc/init/crypto/ssl-server-truststore a trusted server certificate used when setting up SSL/TLS connections. Imported in /facilities/crypto/ssl/server-truststore
etc/init/crypto/signature-verification a certificate that can can be used to verify a digital signature. Imported in /facilities/crypto/signature-verification-keys
etc/init/crypto/signer-truststores an issuer certificate that can be used to verify a digital signature from an upstream authentication provider. Imported in /facilities/crypto/signer-truststores

Example:

The X509-certificate stored as etc/init/crypto/ssl-server-truststore/www.example.com.der is imported in /facilities/crypto/ssl/server-truststore with an id www.example.com.

Note

This mechanism only allows adding new or replacing existing certificates in the configuration. To delete a certificate from configuration, please see Server Configuration overview.

Important

The server needs to be started with the appropriate start-up arguments to consider reloading configuration files, please for more information please see Server Configuration overview.

Configuring private keystores

As opposed to adding new certificates to the configuration, adding a keystore with a private key involves other actions than adding a certificate with just a public key, as a private key is usually transported with some layer of protection. There are two approaches for adding a new keystore to the configuration. The first method interacts with the configuration service, that validates and adds a given keystore into a running configuration. The other method is for preparing a keystore to be used in an XML-snippet that can be loaded by the server when it is configuring.

Both methods rely on starting with a private key that is stored in a base64-encoded PKCS12 or JKS file format.

Note

You can use tools like openssl to convert between existing keystore formats, or to create new key pairs. If you need to create a new RSA key pair, for example, you can use the following commands to create a new PKCS12 keystore with a 2048 bits RSA key pair, that is base64-encoded:

$ openssl req -x509 -sha256 -newkey rsa:2048 -keyout curity.key -out curity.crt -days 365
$ openssl pkcs12 -export -name curity -in curity.crt -inkey curity.key -out curity.p12
$ openssl base64 -in curity.p12 -out curity.p12-b64

Using an action to add a keystore

A keystore can be added through the CLI.

Before getting into the CLI, prepare the base64-encoded file with the keystore to eliminate chunked lines and make it a one-liner. You can do that using the tr command line tool to do this:

$ tr -d '\n' < curity.p12-b64

The output is something you want to copy into your clipboard for use in the CLI.

Next, get in to the CLI . Once you have connected, enter the following command to add the new signing key:

request facilities crypto add-signing-key-keystore id new-signing-key-alias

Replace new-signing-key-alias with the desired id of the new keystore.

The CLI will ask for a value for keystore; here you should paste the contents of the base64-encoded keystore (e.g. the contents of the curity.p12-b64 file). Also enter the password that protects the PKCS12 keystore. The interaction should look something like this:

admin@my-host% request facilities crypto add-signing-key-keystore id new-signing-key-alias-from-cli
Value for 'keystore' (<string, min: 1 chars>): MIILAAIBAzCCCsYGCSqGSIb3DQEHAaC....qA7KxkOQQIsHfJOFKfsQECAggA
Value for 'password' (<string, min: 1 chars>): Password1
status true

(keystore contents abbreviated for readability).

For a list of all possible actions that can be used to manage (i.e. add/replace) keystores, type request facilities crypto in the CLI and then press tab for autocomplete. This will list actions with their descriptions.

admin@my-host% request facilities crypto
Possible completions:
  add-decryption-key-keystore    - This action adds a new Server SSL key to the configuration
  add-encryption-key-keystore    - This action adds a new Server SSL key to the configuration
  add-signature-verification-key - This action adds a new Signature verification key to the configuration
  add-signer-truststore          - This action adds a new signer truststore to the configuration
  add-signing-key-keystore       - This action adds a new signing key to the configuration
  add-ssl-client-keystore        - This action adds a new SSL client key to the configuration
  add-ssl-client-truststore      - This action adds a new Client SSL truststore to the configuration
  add-ssl-server-keystore        - This action adds a new Server SSL key to the configuration
  add-ssl-server-truststore      - This action adds a new Server SSL truststore to the configuration
  apply-csr-response             - This action sets a certificate chain into a key store
  generate-csr                   - This action generates a CSR (Certificate Signing Request) from a keystore with a private key and a certificate
  generate-signing-key-keystore  - This action creates and adds a new signing key to the configuration
  generate-ssl-server-keystore   - This action creates and adds a new Server SSL key to the configuration

These actions can also be invoked using the RESTCONF API. See the Invoking YANG Actions Using RESTCONF section for more information on how to do that.

Preparing the keystore for embedding in an XML configuration document

In case you are preparing an XML configuration file, you can use the conversion tools to process a PKCS12 keystore into the data that can be used to configure the crypto facilities of the Curity Identity Server. See the section on Converting KeyStores (keystore-entry) into correct PKCS12 format.

The output of the conversion, a base64-encoded string, can be used in an XML-snippet:

Listing 26 Sample signing key configuration configuration
<config xmlns="http://tail-f.com/ns/config/1.0">
    <facilities xmlns="https://curity.se/ns/conf/base">
        <crypto>
            <signing-keys>
                <signing-key>
                    <id>default-signing-key</id>
                    <keystore>MIILAAIBAzCCCsYGCSqGSIb3DQEHAaC....qA7KxkOQQIsHfJOFKfsQECAggA</keystore>
                </signing-key>
            </signing-keys>
        </crypto>
    </facilities>
</config>

(keystore contents abbreviated for readability).

This XML can be added to the configuration in $IDSVR_HOME/etc/init and reloaded using the normal procedure.

Converting KeyStores (keystore-entry) into correct PKCS12 format

In Curity, private keys are stored in a pre-defined format in the configuration. To facilitate the conversion to this format, a convertks script is available in the bin directory of the Identity Server distribution.

The resulting key can be configured in the crypto-section as ssl/server-keystore, ssl/client-keystore, signing-keys, encryption-keys and decryption-keys.

Usage of the convertks script

The convertks script helps with processing a base64-encoded keystore, pick one Private Key entry out of that keystore, and write it as sole Entry in a new KeyStore. The output of the script is a base64-encoded version of this KeyStore.

There are two main ways of using the script, either by providing the input keystore as argument, or by providing it as a file.

The convertks script can show all its options when you invoke it with:

$ convertks --help

Provide KeyStore through command-line

Given a base64-encoded KeyStore (i.e. “MIInputKeystore”) that is protected with a KeyStore-password ‘KeyStorePassword’, containing an entry with an alias of ‘InputEntryAlias’ that is protected with password ‘InputEntryPassword’, the convertks script can be invoked with the following commandline:

$ convertks --in-password KeyStorePassword --in-alias InputEntryAlias --in-entry-password InputEntryPassword --in-keystore MIInputKeystore

Note: if entry-password is the same as password of the keystore, it can be omitted in the argument list.

This will output the KeyStore in the correct format to the console. If the output needs to be written to a file instead, add the --out-file parameter with the appropriate filename. The script will be silent.

Note that if any required argument is not provided, the script will prompt for it.

Using stdin

Instead of providing the keystore on the commandline itself, it can also be provided through stdin, i.e. with a command like:

$ cat "MIInputKeystore" | convertks --in-password KeyStorePassword --in-alias InputEntryAlias --in-entry-password InputEntryPassword

Provide KeyStore through file

If the base64-encoded KeyStore is stored in a file, this procedure is the same as above, but the --in-keystore parameter that provides the KeyStore must be replaced with the --in-file argument that contains the name of the file. Given that the file is “current-keystore-base64.txt”, the convertks script can be invoked with the following commandline:

$ convertks --in-password KeyStorePassword --in-alias InputEntryAlias --in-entry-password InputEntryPassword --in-file current-keystore-base64.txt --out-file new-keystore-base64.txt

Note: if entry-password is the same as password of the keystore, it can be omitted in the argument list.

This will output the KeyStore in the correct format to the file new-keystore-base64.txt

If there is no --out-file argument provided, the new keystore will be shown in the console.

Working with PKCS1 private keys

There are plenty of keystore formats that are used throughout, one of which is specified in the PKCS#1 specification in RFC 8017. A major drawback for PKCS#1 keystores is the fact that they can only contain one key, which makes it more difficult to manage, as the Curity Identity Server prefers to use keystores with private- and public keys (or certificates) combined.

Note

When certificates are obtained from Let’s Encrypt using the getssl script, the resulting files are two PKCS#1 files.

As a consequence, to use PKCS#1 keys and certificates, they must be processed outside Curity so they can be configured using the Admin UI or other means. Converting the PKCS#1 key material into a Curity supported format can be done with tooling like for example openssl. This section will walk through the steps needed to perform the conversions to produce a singular keystore that can be used with Curity Identity Server.

Hint

When looking inside the contents of a PEM-encoded file, you can tell that it is a PKCS#1 formatted private key when the file starts with -----BEGIN RSA PRIVATE KEY-----. A PKCS#8 formatted file would start with -----BEGIN PRIVATE KEY-----.

Given there are two files, a private key in one file (e.g. curity.example.com.key) and a certificate in another file (e.g. curity.example.com.crt), you can use the following commands to convert these into one file in the PKCS#8 format, that can be used directly with the Curity Identity Server to import:

Listing 27 Use openssl to convert PKCS#1 files into a PKCS#8 keystore file
$ openssl pkcs8 -in "curity.example.com.key" -topk8 -out "curity.example.com.key.pkcs8" -nocrypt
$ cat curity.example.com.key.pkcs8 curity.example.com.crt > curity.example.com.pkcs8

The commands above produce a file curity.example.com.key.pkcs8 that can be used in the same way that a PKCS#12 file can be used to configure Curity Identity Server using the instructions from Configuring private keystores.

Hardware Security Module

This section describes the setup of a Hardware Security Module (HSM) in the Curity Identity Server. Curity interfaces with an HSM using PKCS#11. This means that some kind of shared library is needed which provides this PKCS#11 capability. This is typically provided by the manufacturer of the HSM. Once an HSM is configured, keys can be used from the HSM. It is not required that all keys, public or private, reside on the HSM. It is possible to configure Curity to use some keys from its own configuration and others from the HSM. This can be especially helpful during testing, but can be useful even in production when it is desirable to configure the public keys in Curity rather than fetch them from the HSM (which may only be accessible over a network connection).

SSL, signature verification keys (public keys), signing keys, trust anchors, and client keys can all be configured to be stored on the HSM. When configuring a keystore, the admin just needs to specify that the key is located on the HSM. The admin node need not have access to the HSM; only run-time nodes that will use the keys housed in the HSM need access to the unit. This may be a subset of the run-time nodes depending on your deployment. There is currently no way to test the connectivity of the HSM from the admin node because it is assumed that this node won’t have access to an HSM.

In order for a key to be accessible to Curity, the HSM must contain not only a public key but also a certificate; without this, the key will not be found at run-time by Curity. The certificate can be self-signed, but it may also be signed by a trusted authority. The only algorithm supported at this time is RSA; elliptic curve and DSA keys cannot be used.

Entering a PIN

In order for each Curity run-time node to access keys on an HSM, they will need to login. This is done using a PIN or pass phrase. There are four ways to provide this PIN to Curity:

  1. On the command line using the -i flag with the PIN as an argument (e.g., idsvr -i 11223344)
  2. On the command line using the -i flag with a file as an argument where that file contains the PIN (e.g., idsvr -i ~/.hsm-pin)
  3. On the command line using only the -i flag and entering the PIN when prompted
  4. Using the environment variable IDSVR_HSM_PIN.

If the -i flag is used with a PIN as an argument (as in the first or second option above), then the admin will not need to interactively provide one. When provided directly on the command line, the PIN will be included in the output of ps and other such tools; to avoid this, the second option of reading the PIN from a file can be used. With this option, the file can be a symlink or pipe as well. The third option of providing the PIN through an environment variable may also make it accessible to such tools. If the PIN is set interactively, it will only be resident in the node’s memory and not accessible from outside tools (like ps). This method requires interactive input from an admin on the console during startup, however.

Configuring the HSM

Before any keystore can be configured as one residing in the HSM, the configuration for the unit must be defined. This is done by setting at least the path to the shared library that provides PKCS#11 support. This can be done using any of the management interfaces. For detailed information on the HSM-related settings, refer to the configuration reference.

Configuring the HSM in the GUI

To configure an HSM using the admin UI, navigate to the System ‣ General page. On this page, there is a section labeled Hardware Security Module. By default, this is toggled off, as shown in Fig. 20:

../../_images/hsm-disabled.png

Fig. 20 The HSM is toggled off (i.e., disabled) by default

Once it is toggled on, various settings can be configured. As shown in Fig. 21, the two that are required are some sort of identifier or name and the path (as it will be on any run-time node that uses the HSM) to the shared library that provides the PKCS#11 implementation.

../../_images/hsm-enabled.png

Fig. 21 The HSM can be toggled on and then the required settings and some optional ones can be configured

Either the slot list index or slot ID must be configured as well. By default, the list index is selected with a value of 0, but this can be changed as needed. The values will depend on the PKCS#11 provider used with the HSM. For example, to get this information with OpenSC, the pkcs11-tool command can be used as shown in Listing 28:

Listing 28 Getting information about the slots to use with OpenSC’s pkcs11-tool
$ pkcs11-tool -I
Cryptoki version 2.20
Manufacturer     OpenSC Project
Library          OpenSC smartcard framework (ver 0.17)
Using slot 0 with a present token (0x0)

To make the HSM as compatible with Curity as possible, the Include Compatibility Attributes option should be toggled on. If this poses a problem, select attributes can be enabled using the Mechanisms option. This option allows certain PKCS#11 mechanics to be enabled or disabled as needed. Valid mechanisms can be found in the PKCS#11 reference guide; any unrecognized value will cause the unit to be inaccessible and a warning to be logged.

Configuring the HSM in the CLI

The CLI can also be used to configure the HSM. For example, enter configuration mode and set it as shown in Listing 29:

Listing 29 Typical example of configuring an HSM using the CLI
admin@prod-curity1% set facilities crypto hardware-security-module library /usr/local/lib/opensc-pkcs11.so

To delete the HSM configuration, just issue the command delete facilities crypto hardware-security-module and commit the change.

Debugging the PKCS#11 Provider

To get more information from the underlying PKCS#11 provider, you can pass the --debug-hsm flag to the idsvr command. This will cause all PKCS#11 keystore-related and provider-based messages to be written to the server log. You can also specify -Djava.security.debug=sunpkcs11,pkcs11keystore,pkcs11 in the JVM options of a certain node. If this method is used, the extra provider-related debugging information will be output after the node is restarted. When using this tactic, it can be helpful to include provider in the comma-separated list of debug message sources together with sunpkcs11, pkcs11keystore, and pkcs11. To set these and provider via the command-line, do not use the the --debug-hsm parameter; instead, pass it via the JAVA_OPTS environment variable when starting the idsvr command (e.g., JAVA_OPTS=-Djava.security.debug=sunpkcs11,pkcs11keystore,pkcs11,provider idsvr).

Some HSM providers, like OpenSC, include a proxy library that logs all access to the PKCS#11 module. To use such an interceptor, configure the proxy library as the one that Curity should use. In the case of OpenSC, in particular, the library that is being proxied needs to be set via an environment variable. This can be done from the shell when starting the idsvr command (e.g., PKCS11SPY=/usr/local/lib/opensc-pkcs11.so idsvr). In the same way, other environment variables of OpenSC’s can be set before invoking idsvr; for instance, the environment variable, OPENSC_DEBUG, can be set to some debug level, like 9, which results in trace-level debug messages being printed to the server log.

EdDSA support

The EdDSA algorithm is based on Edwards Curves, and its use with JOSE is standardized by RFC 8037. The Curity Identity Server fully supports the Ed25519 and Ed448 signature algorithms for digital signature creation and validation.

Contrary to RSA and Elliptic Curve algorithms, EdDSA explicitly specifies the hashing algorithm to use when calculating signatures, hence the signature algorithm name for the two EdDSA algorithms (Ed25519 and Ed448) is the same for both, i.e. EdDSA. The actual signature algorithm that is used to create or verify signatures must be derived from the accompanying key.

Listing 30 Example of a JWT header that is signed with EdDSA
{
  alg: 'EdDSA',
  x5t: 'ZRNic4l1nEsFtyu0BDTL4sbF0eo'
}

In the above JWT header, the x5t field points to the certificate that contains an EdDSA key. If that key is using the Ed25519 key algorithm, it should be validated using the Ed25519 signature algorithm.

Note that RFC 8037 profiles EdDSA for the PureEdDSA variants of Ed25519 and Ed448, and therefore does not use the Ed25519ph, Ed25519ctx` or Ed448ph variants.