Friday 5 January 2018

Obtaining keys to on-board/register a TPP client application to an OpenBanking bank API server

[Note that this was written in January 2018; some of the specifics about how you interact with the OpenBanking Directory have changed after the move to support eIDAS in mid-2019 and I suggest referring to OpenBanking's documentation for that part.]

Introduction

I've been doing a lot of work with the OpenBanking APIs recently (which go live for end-users on 13th January 2018).

There's a lot already written elsewhere about the whole concept of OpenBanking. To quickly sum up, the Competitions & Markets Authority insisting the that largest 9 banks in the UK publish APIs to a common standard is a really good thing and we've already seen that it's encouraging innovation.

Some of the development of the standards was not as open as some might like, and there's a lot of hidden information and new concepts that makes it difficult to actually get going on the system for someone that's not been intimately involved. The OpenBanking system is fairly unique in it's heavy use of MATLS and a custom certificate authority.

One particular pain point I ran into was registering client applications into OpenBanking's directory and then onto a particular bank's authorisation server. There is a front end guide for the directory (hopefully you have this if you have access to the directory, as I'm not entirely sure where the official place to get it from is), and there is a spec for client registrations here:

https://bitbucket.org/openid/obuk/src/4630771db004da59992fb201641f5c4ff2c881f1/uk-openbanking-registration-profile.md?at=master&fileviewer=file-view-default

The spec covers what you need to do in great detail - unfortunately it contains very little detail on how you can actually achieve it using common tools!

Creating the client on the directory

Firstly you need to create your client within the OpenBanking directory. This should be fairly straightforward (but try to get everything right and in there first time - the directory does not allow you to edit a client, say to add/correct redirect URIs - you have to create a client from scratch each time), and watch that the pages are sometimes a little slow to populate so have patience.

Creating your private keys & getting them signed

Once you've created your client on the directory,  you need to create the two private keys that OpenBanking requires (I've used unix shell style variables here; if you're on Windows you'll need to manually replace ${org} and ${client} in the commands). For the MIT (multi-industry testing) environment use:

org="my-org-id-from-open-banking-directory"
client="id-for-my-client-from-open-banking-directory

openssl req -new -newkey rsa:2048 -nodes -sha256 \
  -out transport.csr -keyout transport.key \
  -subj "/C=GB/O=Open Banking Limited/OU=${org}/CN=${client}"

openssl req -new -newkey rsa:2048 -nodes -sha256 \
  -out signing.csr -keyout signing.key \
  -subj "/C=GB/O=Open Banking Limited/OU=${org}/CN=${client}" 

Or if you are on the production environment, the O= needs to be 'OpenBanking':

openssl req -new -newkey rsa:2048 -sha256 -nodes \
  -out transport.csr -keyout transport.key \
  -subj "/C=GB/O=OpenBanking/OU=${org}/CN=${client}"
openssl req -new -newkey rsa:2048 -sha256 -nodes \
  -out signing.csr -keyout signing.key \
  -subj "/C=GB/O=OpenBanking/OU=${org}/CN=${client}"

Do not put any other/extra values into the DN (the '-subj' parameter).

Note the importance of -sha256 (which may be missing in the documentation from OpenBanking) - older OpenSSL installs will default to sha1 which is not acceptable. You can check if you have a sha1 or sha256 csr by running:

openssl req -in transport.csr -text -noout | grep 'Signatu'

This should output:

    Signature Algorithm: sha256WithRSAEncryption

These csr files can then be uploaded to the OpenBanking directory, which will give you two .pem files (download these alongside your private keys, renaming them to transport.pem and signing.pem) and also a Software Statement Assertion.

You will also need to obtain Open Banking's root and issuing certificate authority as .cer files. (I'm currently not sure what is the official route for obtaining these.)

You can check the contents of the files using openssl, eg:

openssl req -in transport.csr -text -noout
openssl rsa -in transport.key -text -noout
openssl x509 -in transport.pem -text -noout

Folding the OpenBanking PEM files

Some software (including openssl when generating the pkcs12 necessary for firefox) considers the pem files provided by the OpenBanking directory to be invalid as they have very long lines, you can fix this with:

fold -w64 transport.pem > transport-fixed.pem

Using APIs in Firefox

If you want to do any testing in firefox, in particular using it's handy HTTP requester add-on (note that it currently only works in pre-quantum builds of firefox so you may need to download an pre-Quantum version), then you'll need to convert your transport key to a pfx file:

openssl pkcs12 -export -out transport.pfx -inkey transport.key -in transport-fixed.pem

This pfx file and the OpenBanking .cer files can then be imported into Firefox in Preferences -> Advanced -> Certificates -> "Your Certificates" and "Authorities" respectively.

Using APIs in curl

curl is my preferred tool for trying out APIs requests before coding them up. To use curl with a bank API server, you'll need to combine the two openbanking keys together:

cat obrootca.cer obissuingca.cer  >> obchain.cer

and also combining your private transport key with the signed certificate you got back from the OpenBanking directory:

cat transport.key transport.pem >> transport-combined.pem 

These certifcate/keys can then be used with curl using:

curl --cacert obchain.cer \
  --cert-type pem --cert transport-combined.pem

Note that if you make an error in supplying the client certificate or issuing chain, you may well discover that the bank's server just resets the TCP connection instead of returning a useful error - variants of this error message haunted me for longer than I would have liked:

curl: (56) SSL read: error:00000000:lib(0):func(0):reason(0), errno 104
(at least on linux, errno 104 is ECONNRESET or 'Connection reset by peer')

Just double check you are supplying a valid client certificate and have supplied the correct OpenBanking .cer files and you should get past this.

Creating a JWT for a bank's dynamic client registration endpoint

You will also need to produce a signed registration request (if the bank you're onboarding with allows or only has a dynamic client registration endpoint). To actually produce the signed request you can use:

https://jwt.io

You can add your signing private key in there, after selecting 'RS256' (the private key never leaves your browser, but if this is real production key you probably want to use a completely offline tool instead).

This site is invaluable for verifying if your JWT is signed correctly:

https://jwt.davetonge.co.uk

for the 'jwks endpoint' field you should enter your jwks URL found on your OpenBanking Directory page (it can also be found in your SSA if you use jwt.io to decode it).

The main gotcha here is to make sure your SSA is current (some banks require it to be less than 3 days old) and that the iss/exp fields are current - there's a handy online converter for the time stamps.

Generating a JWKS for the signing key

Lastly, you may find you need to convert your signing key into JSON Web Key Set format (JWKS for short). The npm pem-jwk module should be able to do this conversion, install it using:

  sudo npm install -g pem-jwk

An extra issue is that currently the npm RSA key reading module only copes with the older PKCS1 (traditional OpenSSL) key format, so you may well see a 'Could not parse PEM' or 'Could not read file' error - whereas current versions of SSL generate keys in the PKCS8 format - you can convert your key using:

openssl rsa -in signing.key -out signing-PKCS1.key

(thanks to this stack overflow post for a detailed explanation of the key formats!)

Then you can do the conversion to jwks:

 pem-jwk ~/path-to-my-keys/signing-PKCS1.key > signing.jwks

which will display the jwks on the console. You'll need to manually add the "kid": "<keyidfrom OB directory>" and "alg": "RS256" lines.

TLS Keys as JSON strings

You may also need the TLS transport keys as JSON strings; these are simply the openssl versions with the header/footer and all newlines removed, like so:

perl -pe '$_="" if /----/; s/\n//' transport.key > transport-key.json

perl -pe '$_="" if /----/; s/\n//' transport.pem > transport-cert.json

Wrapping up

Hopefully that should give you a good start. If you need additional help in this area, my company does consulting work - drop me an email at joseph@emobix.co.uk.