CCA over TLS with Access Worldpay

Getting started with client certificate authentication with TLS on Access Worldpay Try.

Technical

Written by Andy Brodie
05 January 2021


Public Key Infrastructure (PKI) and certificate management is a function often performed by a centralized security team. This means that in some organizations it can be hard for application developers get set up with client certificates. Often as a result of this, development teams aren't able to set this up and then do all their development and testing with many security controls disabled. This can result in operational incidents and unexpected behavior in live.

In this section, we explain how to get set up with TLS client certificates from scratch. This is so you can try out our mTLS functionality and work with it as you develop your integration with Access Worldpay.

Prerequisite

Before following this guide, ensure that you've read the Authenticating with SSL/TLS integration guide first, as this relies on some of the information presented there.

Warning

In this guide we're going to cut lots of corners, such as encrypting private keys and assigning certificate serial numbers, for the sake of brevity. DO NOT follow this guide for your live certificates or they will be insecure. Always consult your organization's security team before preparing certificates for processing live payments or connecting to our Try environment.

Second Warning

DO NOT use this guide to manage PKI for live services.

Environment

These instructions make use of the ubiquitous OpenSSL library and tools, available from openssl.org or via your distribution's package manager (rpm, apt, etc.). Most operating systems (Windows being a notable exception) already have OpenSSL installed.

  • If you're using Windows - either grab yourself a Windows binary or (easier) use the Windows Subsystem for Linux in Windows 10 and then you can use the native Linux binaries.

  • If you're using a Mac - you may need to install OpenSSL using brew, as macOS distributions supply LibreSSL, which does not support all the command line switches we use in this tutorial.

Once you've installed OpenSSL, check that the right version is on the path:

$ openssl version
OpenSSL 1.1.1h  22 Sep 2020

Once you've got that working, come back here and carry on.

Create the issuer certificate

The first thing you must do is create an issuer certificate, or certificate authority. This is going to be a certificate that you will use to sign client certificates. There's two steps to creating the issuer certificate:

  1. Create the public/private key pair.
  2. Create a self-signed issuer certificate that incorporates the public key generated in step 1.

Once you've done that, send us the issuer certificate (see above) and we'll associate it with your account in our Try environment.

Create the issuer public/private key pair

We use a 2048-bit RSA key for our certificates in this guide.

Access Worldpay supports:

AlgorithmKey Sizes
RSA2048 or 4096
DSA2048
ECDSAAny named curve >=256 bit
Note

For live we recommend using ECDSA, as this uses smaller keys which are faster for computation. This tutorial uses RSA as it is the most commonly supported algorithm. ECDSA, being newer, may not be available in all environments.

Start by creating a 2048-bit RSA private key and storing in a file named issuer.private.key.pem:

openssl genrsa -out issuer.private.key.pem 2048

Now generate the public key from the private key and store in issuer.public.key.pem:

openssl rsa -in issuer.private.key.pem -pubout -out issuer.public.key.pem

Create a Self-Signed Issuer Certificate

Normally, this is a two step process: creating a Certificate Signing Request (CSR) and then creating the certificate from that. However, as this certificate is self-signed, we can do it all in one step:

openssl req \
  -key issuer.private.key.pem \
  -new \
  -x509 \
  -days 365 \
  -sha256 \
  -out issuer.cert.pem \
  -subj "/CN=MyClientCertIssuer" \
  -addext "subjectKeyIdentifier = hash" \
  -addext "keyUsage = critical, cRLSign, keyCertSign" \
  -addext "basicConstraints = critical, CA:true"

There are a lot of parameters here, in fact, OpenSSL only recently started supporting all of this information being passed on the command line instead of being stored in a configuration file. Here’s how everything breaks down:

ParameterMeaning
reqCommand to create a CSR or a self-signed certificate.
newWe're creating a new request.
-keyThe private key to use.
-x509Output a certificate in X509 format rather than a CSR in PKCS #10 Certification Request Syntax Specification.
-days 365The number of days the certificate is valid for, from today. Example uses 1 year.
-sha256Sign the certificate by encrypting a SHA-2 256-bit hash of the certificate with the CA private key.
-outThe file to write the certificate in to.
-subjThe X.500 Distinguished Name for the certificate in the format /type1=value1/type2=value2/. RFC3280 defines the mandatory and optional components. In the case of an issuer it must only be non-empty. You must replace the value in the example with your own.
-addextSpecifies certificate extensions in the form of name/value pairs, as follows:

subjectKeyIdentifier - Creates an extension containing the hash of the subject name.

keyUsage - This describes what the public key inside the certificate can be used for and how important it is.

critical - A certificate-using system MUST reject the certificate if it encounters a critical extension it does not recognize or a critical extension that contains information that it cannot process. A non-critical extension MAY be ignored if it is not recognized, but MUST be processed if it is recognized.

cRLSign – the subject public key can be used to verify the digital signature of a Certificate Revocation List

keyCertSign - the subject public key can be used to verify signatures on public key certificates.

basicConstraints - In this case, asserts that this certificate may only be used as a certificate authority via critical and CA:true.

The output certificate, named issuer.cert.pem in this example, is your issuer certificate that you must send to us. We use this to associate it with your Try account.

Create the Client Certificate

Creating the client certificate follows a similar pattern to the creation of the issuer certificate, except that we have to split the second step in to two separate steps:

  1. Create a new public/private key pair, as before.
  2. Create a Certificate Signing Request (CSR).
  3. Create a certificate based on data in the CSR that is signed with the issuer's private key.

Create the Client Public/Private Key Pair

Using the same mechanism that we used for issuer key generation:

openssl genrsa -out client.private.key.pem 2048

Now generate the public key from the private key:

openssl rsa -in client.private.key.pem -pubout -out client.public.key.pem

Create the CSR

To create the client certificate, you first must create a Certificate Signing Request (CSR):

openssl req \
  -new \
  -sha256 \
  -key client.private.key.pem \
  -out client.csr.pem \
  -subj "/O=MyOrganisation/CN=MyAWClient" \
  -addext "keyUsage = critical, digitalSignature, keyEncipherment" \
  -addext "basicConstraints = critical, CA:false"

The parameters are very similar to those used to create the issuer certificate. The difference is the keyUsage extension specifies only the usages required for performing a TLS handshake. It also forbids the use of this certificate to be used as a certificate authority.

Create the Signed Client Certificate

This is the step that is typically performed by a security team or external Certificate Authority: taking your CSR and generating a signed certificate from it. Contrary to popular belief, issuers do not sign the CSR itself, CSRs and Certificates are different structures. Instead, the CA creates a certificate from scratch, copies in information from the CSR and finally adds a digital signature using the issuer's private key.

To create the client certificate:

openssl x509 \
  -req \
  -CA issuer.cert.pem \
  -CAkey issuer.private.key.pem \
  -in client.csr.pem \
  -out client.cert.pem \
  -days 365 \
  -CAcreateserial
ParameterMeaning
x509Command to manage X509 certificates.
-CAThe public certificate of the issuer that is signing the certificate. The name of this is embedded in the new certificate so we know who signed it.
-CAkeyThe name of the file containing issuer's private key that is used to sign the new certificate.
-inThe CSR from which to create a certificate.
-days 31The number of days the certificate is valid for (from today).
-outThe file to write the signed certificate in to.
-CAcreateserialAll certificates should contain a serial number as a short, unique, identifier for that certificate. By default OpenSSL expects one of these to exist already, this option tells OpenSSL to create it now.

You're now ready to go and use mTLS with Access Worldpay's Try environment.

Verifying Connectivity

First, ensure that your IP mapping is working:

echo | openssl s_client -connect try.access.worldpay.com:443 -state > /dev/null
Note

The echo command at the beginning causes the command to terminate properly and not leave your terminal hanging, connected to our service, while it waits for further input.

If you're connected to the correct service, you will see the following line in the output:

SSL_connect:SSLv3/TLS read server certificate request
Note

Depending on whether you're using TLS v1.2 or v1.3, this line appears in a different place in the output.

If you don't see this line in the output, you are most likely connecting to the original Try environment. This won't contain the output line because a certificate can only be sent if the server requests it, which won't happen on the non-mTLS enabled Try environment.

Using the Client Certificate

To use the client certificate for your connections, you need the client certificate (client.cert.pem) and the client private key (client.private.key.pem) to be configured in your client. This is so that when Access Worldpay requests a client certificate as part of the TLS handshake, it uses these two files.

Handily, OpenSSL permits trying out the certificates via the command line:

echo | openssl s_client -cert client.cert.pem -key client.private.key.pem -connect try.access.worldpay.com:443

If you want to see the handshake completed in the context of an HTTP request we recommend you use a tool like curl. Curl is pre-installed on Mac and Linux machines and is available for Windows from the authors at curl.se/windows.

To test things out with curl:

curl --cert client.cert.pem --key client.private.key.pem https://try.access.worldpay.com:443/

If everything is working, you'll receive output like this:

{
  "_links": {
    "payments:authorize": {
      "href": "https://try.access.worldpay.com/payments/authorizations"
    },
    "service:payments": {
      "href": "https://try.access.worldpay.com/payments"
    },
    "service:sessions": {
      "href": "https://try.access.worldpay.com/sessions"
    },
    "service:tokens": {
      "href": "https://try.access.worldpay.com/tokens"
    },
    "service:verifications/accounts": {
      "href": "https://try.access.worldpay.com/verifications/accounts"
    },
    "service:verifications/customers/3ds": {
      "href": "https://try.access.worldpay.com/verifications/customers/3ds"
    },
    "service:verifiedTokens": {
      "href": "https://try.access.worldpay.com/verifiedTokens"
    },
    "service:fraudsight": {
      "href": "https://try.access.worldpay.com/fraudsight"
    },
    "service:exemptions": {
      "href": "https://try.access.worldpay.com/exemptions"
    },
    "service:payouts": {
      "href": "https://try.access.worldpay.com/payouts"
    },
    "service:payments/global/direct": {
      "href": "https://try.access.worldpay.com/payments/global/direct"
    },
    "curies": [
      {
        "href": "https://try.access.worldpay.com/rels/payments/{rel}",
        "name": "payments",
        "templated": true
      }
    ]
  }
}

You're now good to go and continue integrating with Access Worldpay!

Troubleshooting

This section contains common errors and their causes.

Missing Certificate

If you receive the following response:

{"errorName": "missingClientTLS", "message": "Required client TLS certificate is missing"}

This means that you are connecting to the correct mTLS endpoint, but have not supplied a client certificate. Ensure that you are supplying your client with both the client certificate and corresponding private key.

400 Bad Request

If you receive the following response:

{"errorName": "failedClientTLS", "message": "Client TLS certificate is invalid"}

This is because the certificate you have provided has not been accepted. This can happen for a number of reasons, the two most common are:

  1. The certificate chain has not been matched against a known issuer certificate. If you are sure that you're using a client certificate signed by an authorized issuer certificate associated with your account then contact FIS support.
  2. The certificate that you have provided, or one of the certificates in the chain, has expired.
Note

In the case that you must provide an updated certificate to us, please remember that we NEVER ask for, and you must NEVER send us, a private key. We may, however, ask for copies of client or intermediate certificates (i.e. the ones you are supplying on the handshake), in addition to issuer certificates, to confirm that they are valid.

Unable to create a certificate due to RNG errors

When you try to create a certificate you may get the following error:

Can't load /home/myuser/.rnd into RNG
139961075569088:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/home/myuser/.rnd

The RNG is a Random Number Generator and is used by OpenSSL to seed the pseudo-random number generator. You can create this file by executing:

openssl rand 256 -out /home/myuser/.rnd

Make sure that you replace myuser with your own home directory (assuming that's where openssl is looking).