External Authorisation Private & Public keys

Usages

Usually these keys are being used for either encrypting/decrypting or for signing/verifying.

encrypting/decrypting: This process consists in encrypting the ciphertext with a public key to ensure that no one but the entity using the private key will be authorised to decrypt the content.

signing/verifying: This process consists in creating a signature with a private key and verifying its validity with the public key. This makes ensures that the sender is who really claims to be. In addition to message Integrity and Non-repudiation.

Signing / Verifying

The signing process consists in the following steps:

Using the payload (UTF-8 Bytes) of the request that is going to be send to our client. We will hash the payload using SHA-256 and then using the private key we will generate the signature.

We will request our client’s API containing the following information:

The payload

The Signature (encoded base 64) generated from the payload (send as a header)

The keyId of the key used to generate the signature (send as a header)

The flow described can is contemplated in the image below.

The verifying process consists in the following steps:

  1. Receive the request
  2. Fetch the public key using the keyId received in the request in the client’s side.
    1. If It is the first time using the new key, the key won’t be found in their system. They would require to fetch the right JWK in our Authentication API (/.well-known/jwks.json), using the keyId received. Then they would store in their system for future use.
    2. If the key Id is found in their system they would be able retrieve the public key from their system.
  3. Compare the hashed payload against the decoded signature and decrypted with the public key. If the values match it means the payload is valid and it hasn’t been altered.

We can see the described steps in the following image:

Example of usage (Signing / Verifying)

We will now demonstrate the described flow in an example. Given a client that uses External balance authorisation, we need to send them transaction information so they do validations in they side and either accept or decline the transaction. This is why it is important knowing that the information send to them is not altered.

Using the private key for the specific client that we are going to request, we will sign the signature and send such signature along with the keyId for the key.

After the client receives the request containing the following information:

Request Body

{ "holdId": "c18c2ec7-16cc-400a-98b5-e3d7f1a0d152", "accountId": "74eb0d63-696b-424d-9d44-56e0f73e7305", "cardId": "db1c8006-96ea-4070-a53a-16d896058dc9", "amount": { "amount": "-0.52", "currency": "AUD" } }

Signature

Shaype-Signature: ZvBX+KxWh210vm/PMaRLI+dQ+uQcZAwSpNmpxfu2OEUncji4kTVZEdAiqL/62neYYUOMhtUvNaG65Y/wvf+0ZLemPcFbjr9jMGg/DrHRLQxMI+3unjQwnaLXc6D3s7u5L0IIeGlRzyuITHK5c2JnzSGghLlicASimwvV1AOaXrfhUuEoTjLURnsTl2uUtwYFgFnAnBwwG2Cm85Wki5/VlsJ4Ff03HqkyF046bJJH2hEQ7L8YZofFbjiXibJ07BJD258krrqUbxd5zy9xlZ8t47kGGFbNxTXq4lIOFsF2kM9SVK1BOO5/ah//kYgEY3ozWAYNqrcdsZCAn0QhqtJGmg==

KeyId

Shaype-Key-Id: ffa38711-7164-441a-8164-dd32d7582ab1

The client then, will go fetch the public key that matches the keyId given.

Now using openssl library we can verify that the payload and the signature match using the public key obtained.

openssl dgst -sha256 -verify publickey.pem -signature signature.sha256 payload.txt

Where…

  • publickey.pem is the public key obtained in format PEM.
  • signature.sha256 is the decoded base 64 value of the signature. We can decode it using echo signatureValue | base64 -d > signature.sha256
  • payload.txt is the value of the payload(without spaces or new lines) in UTF-8 Bytes. We can obtain it using echo -n "{"holdId":"c18c2ec7-16cc-400a-98b5-e3d7f1a0d152","accountId":"74eb0d63-696b-424d-9d44-56e0f73e7305","cardId":"db1c8006-96ea-4070-a53a-16d896058dc9","amount":{"amount":"-0.52","currency":"AUD"}}" | iconv -t utf-8 > payload.txt

If the payload and the signature matches the result will be “Verified OK”.

We could also verify the signature using with the following Java method:

/** Verifies that the signature corresponds to the payload. */ public void verify(String signature, String publicKeyDer64, String payload) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException { byte[] decoded = Base64.getDecoder().decode(publicKeyDer64); // Create Public Key KeyFactory keyFactory = KeyFactory.getInstance("RSA"); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decoded); var publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec); // Verify signature Signature publicSignature = Signature.getInstance("SHA256withRSA"); publicSignature.initVerify(publicKey); publicSignature.update(payload.getBytes(UTF_8)); byte[] signatureBytes = Base64.getDecoder().decode(signature); boolean verify = publicSignature.verify(signatureBytes); assertThat(verify).isTrue(); }

Where…

  • signature is encoded base 64.
  • publicKeyDer64 is the value of the public key in DER base 64 value.
  • payload.txt is the value of the payload(without spaces or new lines). For example: "{"holdId":"c18c2ec7-16cc-400a-98b5-e3d7f1a0d152","accountId":"74eb0d63-696b-424d-9d44-56e0f73e7305","cardId":"db1c8006-96ea-4070-a53a-16d896058dc9","amount":{"amount":"-0.52","currency":"AUD"}}"