Field Level Encryption (FLE), when enabled is a security feature that ensures sensitive data is protected when transmitted to Banked's APIs. This feature is particularly important for handling personal and financial information, providing an additional layer of security beyond standard TLS encryption.
FLE provides end-to-end encryption for sensitive data, ensuring it remains protected even when passing through third-party services.
Overview
The process involves:
- Fetch Banked's public keys from the
https://api.banked.com/.well-known/jwks.json
JWKS endpoint - Prefix the sensitive field names with
encrypted_
- Encrypt sensitive data field values before sending requests
Implementation Steps
Step 1. Get Banked's Public Keys
Using your API credentials in the Authrozation header field, fetch the public keys for encryption via a JWKS (JSON Web Key Set) endpoint:
GET https://api.banked.com/.well-known/jwks.json
It's mandatory to use include your API authorisation in the Authorization
header field otherwise you won't get optimised keys for your regional location.
Response Format
{ "keys": [ { "kty": "RSA", "use": "enc", "alg": "RSA-OAEP-256", "kid": "4aeb1209-f09d-4d0d-90d0-488ac948fecc.1", "n": "n758mvJETwhB2v28yRsmXOGsjM9z1nbEcm8eyCtdR68laYuQB3XfbaGaQZN-MvV1RMx04tEvGJxux3Jm296G3XsWkWD2d4tvhN4EmSDcEIswv7h1YWScAvhCARmiH1LCXZFECpSluLiIF-xPom4bWi5koLApzT7Eh5b7NB-8m6uctrTBlLfWRtyX5MrAc8LhzBWLOC7zhw8k7dtbp2YYmYPDvco8NE5CKjT0ym6sSA2hW364HoxN_hbsI5BnrlzFjyUjb-XGDQbM10R1ItnEzSpiRLijdwR2Xzqs7AeSwK3s5H_jgLaVa3l9b-c7U470lZZoM7gNJVrqNptz-8FRj4nW3WfPfm8X8Q_ew_0ijkB69zwPdWX9cgAjIw60MjMM9M-lbbJvtghdfwbFYKiVzNmYDmOb3Jgp7VMmiIhIA9kLdsa0qRvwF6Zh5ur2uS_AU1Qnu7bkl__pDbQr2DCKGceIknj_xreo9m32qGghd5sfJRDHWLnOZKlE1_JI54tp", "e": "AQAB", "bnkd.iat": 1738665189, "bnkd.exp": 1773052389, "bnkd.test": true, "bnkd.region": "au" } ] }
The response will contain the following properties:
- kty - Key Type
- use - “enc” indicates that a key can be used for encryption
- alg - indicates the type of encryption algorithm to be used
- kid - This is the unique Key ID which will be a RFC 4122 version 4 UUID plus the version number of the key with a . separating each component, for example: 04ff9caf-9696-4d9f-adf5-d678ff408755.1 where 42 04ff9caf-9696-4d9f-adf5-d678ff408755 is the V4 UUID and 1 is the version of the key. This is used by Banked to validate and decrypt the content encrypted with this key.
- n - Public Key Modulus - https://www.rfc-editor.org/rfc/rfc7518.html#section-6.3.1.1
- e - Public Key Exponent - https://www.rfc-editor.org/rfc/rfc7518.html#section-6.3.1.2
- bknd.iat - The date/time the key was issued at. This will be a RFC3339 Unix timestamp.
- bknd.exp - The expected expiry time of the key. This will be a RFC3339 Unix timestamp.
Step 2. Encrypt Sensitive Data
- Identify fields requiring encryption (see Fields Requiring Encryption)
- Encrypt each sensitive field.
- Prefix encrypted field names with
encrypted_
. For example thesource
bank account JSON field name becomesencrypted_source
Encrypted fields follow JOSE specifications with RSA-OEAP-256 (3072 bit RSA key, OEAP Padding - SHA256 Digest) and AES 256 GCM. The KeyID (kid, returned in the /.well-known/jwks.jso API response) must also be used as a JWE-protected header so Banked know which Private key to use for decryption. The kid can be retrieved from the JWKS.
The encrypted field value must be a JWE in compact format. The structure being five parts which are separated by dots (.): BASE64URL(JWE Header)
. BASE64URL(Encrypted Key)
. BASE64URL(Initialization Vector)
. BASE64URL(Ciphertext)
. BASE64URL(Authentication Tag)
Example Request
// Original { "source": { "account_type" : "plain_bank_account", "account_identifier": { "identifier_type": "BSBAccountNumber", "bsb": "111114", "account_number": "010111" }, "account_owner_name": "John Smith" } } // Encrypted { "encrypted_source": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2..." }
Key Rotation
To ensure continuous operation, your system must support key rotation. This covers both our periodic scheduled rotations for keys which are about to expire and any urgent, unforeseen rotations.
For Scheduled Rotations:
Our key expiry dates are published in the bnkd.iat
field on our well-known/jwks endpoint. Please implement a mechanism to retrieve the new public keys before the current keys expire. There are two methods to do this:
- Look at the expiry date and retrieve the new keys at least one day before the old keys expire or
- Simply retrieve the keys every day and cache them. This way you will get the most recent keys as soon as they are published. This option might easier to implement and test as it means you don't need to worry about adding functionality based on the expiry date.
For Unforeseen Rotations:
In extremely rare cases, Banked may need to rotate a key before its scheduled expiration. To make your code resilient to these events, we will signal this by returning an HTTP 422
error with a source
field of encryption key
and a code
field of invalid
.
Upon receiving this specific error, your system should automatically reload the Banked public keys and then retry the API call that failed.
Notes:
- The new keys are always located at index position zero in the
keys
JSON array from our well-known/jwks endpoint. - Remember to include your API credentials in the
Authorization
header when retrieving keys to make sure you retrieve the correct key.
API Reference
Request Fields Which Support Encryption
Payment Sessions API
Endpoint | Field | Sensitivity |
---|---|---|
POST /v2/payment_sessions | payee | high |
POST /v2/payment_sessions | payer | high |
POST /v2/payment_sessions | mandate | high |
Mandates API
Endpoint | Field | Sensitivity |
---|---|---|
POST /v2/mandates | source | high |
POST /v2/mandates | destination | high |
POST /v2/mandates | actions.#.source | high |
Error Responses
Invalid Encryption Key
HTTP/1.1 422 Unprocessable Entity Content-Type: application/json { "errors": [ { "code": "invalid", "source": "encryption key", "title": "invalid encryption key" } ] }
When receiving this error, fetch a new key from the JWKS endpoint using API credentials in the Authorization
header field, re-encrypt your payload, and submit a new request.
Code Examples
Please see the example Javascript code that can be used to generate the JWE for adding to encrypted field/s. Please note that this code is provided as an example and is not designed for production use.
import pkg from 'node-jose'; const {JWE} = pkg; const payload = `{ "account_type" : "plain_bank_account", "account_identifier": { "identifier_type": "BSBAccountNumber", "account_number": "123456789", "bsb": "123456" }, "account_owner_name": "John Smith" }`; var jwksUrl = 'https://api.banked.com/.well-known/jwks.json'; var jwks = await fetch(jwksUrl, { headers: { 'Authorization': `<Basic or Bearer> <Auth Token>` } }).then(response => response.json()); var key = jwks.keys[0]; let encrypted = await JWE.createEncrypt({format: 'compact', fields: {alg: 'RSA-OAEP-256', enc: 'A256GCM'}}, key).update(payload).final(); console.log(encrypted);
Note: The new keys are always located at position index zero in the keys JSON array from our well-known/jwks endpoint.
Best Practices
- Generate new CEK for each sensitive data object
- Do not reuse CEKs between requests
- Cache public keys and refresh before expiry
- Handle key rotation gracefully
- Implement proper error handling for encryption/decryption failures