JWS Request for PSD2 Enrollment
To access Rabobank’s PSD2 services, such as Account Information and Payment Initiation, in the Production environment, you must use the Third party Providers API. This is part of the PSD2 Enrollment Service, which is used to request production access for certified PSD2 Account Information Services Providers (AISPs) and Payment Initiation Service Providers (PISPs).
When you send a request to the API, that request must be represented in the Flattened JWS JSON Serialization format.
In this document
In this document, we explain how to create the request object in the correct format.
- Overview of the components
- Step 1: Creating the Protected Header
- Step 2: Creating the payload
- Step 3: Creating the signature
- Step 4: Creating the API request body
- More information on the Flattened JWS JSON Serialization format
Overview of the components
A request object consists of the following components:
- Protected (Header).
- Payload.
- Signature
Step 1. Creating the Protected Header
The protected header must contain:
- The algorithm
- A registered X.509 client certificate.
Use your (PSD2) eIDAS QSEAL certificate issued by the Qualified Trust Service Provider of your choice
Currently, RS256 is the only supported algorithm.
In the following example, for purposes of illustration only, we use a regular X.509 certificate:
PROTECTED = { "alg": "RS256", "x5c": [BASE64(DER_CONTENT)] }
About the x5c parameter
The "x5c" parameter in the API Header request contains your X.509 public key certificate.
The x5c parameter must not contain more than one valid QSeal eIDAS certificate.
The string must be a Base64 encoded DER PKIX certificate value.
It must not be a Base64url (URL-safe Base64) encoded DER PKIX certificate value.
Converting X.509 certificates from PEM to DER format
If your X.509 client certificate is in PEM format, you can convert it to DER format with openssl:
openssl x509 -outform der -in certificatename.pem -out certificatename.der
Example:
{ "alg": "RS256", "x5c": ["MIIFeDCCA2ACCQCPCM/Z+y911TANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJOTDERMA8GA1UECgwIUmFib2JhbmsxGDAWBgNVBAsMD1BTRDIgRW5yb2xsbWVudDEeMBwGA1UEAwwVZGV2ZWxvcGVyLnJhYm9iYW5rLm5sMSIwIAYJKoZIhvcNAQkBFhNleGFtcGxlQHJhYm9iYW5rLm5sMB4XDTE5MDQwNTE1NDA0OFoXDTIwMDQwNDE1NDA0OFowfjELMAkGA1UEBhMCTkwxETAPBgNVBAoMCFJhYm9iYW5rMRgwFgYDVQQLDA9QU0QyIEVucm9sbG1lbnQxHjAcBgNVBAMMFWRldmVsb3Blci5yYWJvYmFuay5ubDEiMCAGCSqGSIb3DQEJARYTZXhhbXBsZUByYWJvYmFuay5ubDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOAJ9HwtfLbqysVnWbfbSevR8Y4Qubj2JJEzanv3jIi0E2cdtcDa7UgMZiFYzKx3aigsljh4c5+5MKwd9zdjwCR6Jz3s1DaaHluvQZjW3XuL2sg1RbnK4qAkqLie85Nqg4jcV0SIxfbHpbZXqyOlepHVt2qIkniWBfpOylMu2GcTKeBDbF0uWF+Bh6dJ6sUlKf5AgE1w4XWNBuxEE1Oa4Mb7wcAybRdjJpqbGBoZixVzOXgg002pSW7qkQ6tmm/36Z5+7YjZum300gNUXB1bg4NtK/wHAUBqG2CQI7yVWQgXfA+Oxk8qxQLsqvULDpcO53JhFNoYljIqmwlAmmB5aERcG37MBTcE5+akCgPglcc2npq1WGNDkEfMImCsEjl8aSKJlQRFiUJ+rrVPwlXVep189xi70qbm/A972enz8obAL1VlLY8bTtAS9Fv/aligujmQGU8C/LZcQEsGryQyYVgxvEBjPjRCIutYYeQm7B7xZq63YsablJ79I0Fs2fH52sx/OVay4Z3VXOyKOz/sACwznBxmtgwNq1SlMJ0VaWr8fGoQOCU/4RDl360QfsZ2ffCKI6NlVLBvUnoiqdu6necds5zJT0ubvST24K5B7WB/TvKlMuKA3OvbN301tMFCyOOZpMLpRwPcA7Jze75AFTDkWpLSyg7LdOr5yXZMUYqVAgMBAAEwDQYJKoZIhvcNAQELBQADggIBACIkcdAcFGidksywvS+arIPIbbRjwBrA/Mn7dRTKlAeNKz215jig1CBByeWjySU0zghruCw4g6k/1iXOvdrkJ7jY9niDJP9+477tEnwtmFVDi6J52gNmvbBHYLd2UlXU72mEr9LGp1XtgGtm19Nbmdpd60gnRXe4M8XL3SQqCe1p5dtPhDCcdG2jmSHJvlrHPWJr4sROG4iEcLi7Ht7Zk1YMtFs0M7Lbl7X+8xm5QfsrGFARsQQDwBsRy2+P3/q9eJbUIhos4qT8fgt97RYGNea8AVAOUqY+7fs1450VeDKVcGfGHHZS/tgM1G3NwjuizAk9LdhRkNMrCmj4ndAuZngU74H6osGsV34L/zxHZfBI2Gxy5VrH5lhTGX/YqGqHzYoC0FctZ9oIVTuNEhRNFVAVc58N+z11GEfsdOcj+HnCq9HCNP3KIwq60RsRMCvYOf0rmydzaGzz/oKdTtWvcvKt9AY7jgHukms+zYPAeOMOwz92OpK38uj2hRpIfywCEORT51CMm17Nqc4xtOHyvFAHK4ubi5TyMlV4blg30HtOOkWNkCmC51ouLkEtsRsmFFsU+bv/111U6fhAp8Ud7cep+oh06MEFxI1KnZDUF9meS2Jn/uv6Zpa8lySbN78jrjlPni2BEGeiDew1p3pc2U1IygseZZLDeJhoDXJpF3g7"] }
Step 2. Creating the Payload
The payload must contain:
- Your organization's primary technical contact's email address (
ptc_email
) - An expiration time (timestamp) in seconds (
exp
).
This indicates until when the request is allowed to be handled.
PAYLOAD = { "ptc_email": "YOUR_EMAIL", "exp": TIMESTAMP_INTEGER }
Example:
The (URL-safe) Base64 encoded example below results in a valid JWS payload:
{ "ptc_email": "example@rabobank.nl", "exp": 1554080659 }
Step 3. Creating the signature
A signature must contain three elements:
- A signing string
- The public certificate
- The private key
Step 3.1 Signing string
To create the signing string, you must include the protected header and the payload.
Both elements need to be:
- URL-safe Base64 encoded (RFC 4648) and then
- Concatenated with a period (
.
) in between.
SIGNING_STRING = BASE64URL(PROTECTED) + "." + BASE64URL(PAYLOAD)
Example
eyAiYWxnIjogIlJTMjU2IiwgIng1YyI6IFsiTUlJRmVEQ0NBMkFDQ1FDUENNL1oreTkxMVRBTkJna3Foa2lHOXcwQkFRc0ZBREIrTVFzd0NRWURWUVFHRXdKT1RERVJNQThHQTFVRUNnd0lVbUZpYjJKaGJtc3hHREFXQmdOVkJBc01EMUJUUkRJZ1JXNXliMnhzYldWdWRERWVNQndHQTFVRUF3d1ZaR1YyWld4dmNHVnlMbkpoWW05aVlXNXJMbTVzTVNJd0lBWUpLb1pJaHZjTkFRa0JGaE5sZUdGdGNHeGxRSEpoWW05aVlXNXJMbTVzTUI0WERURTVNRFF3TlRFMU5EQTBPRm9YRFRJd01EUXdOREUxTkRBME9Gb3dmakVMTUFrR0ExVUVCaE1DVGt3eEVUQVBCZ05WQkFvTUNGSmhZbTlpWVc1ck1SZ3dGZ1lEVlFRTERBOVFVMFF5SUVWdWNtOXNiRzFsYm5ReEhqQWNCZ05WQkFNTUZXUmxkbVZzYjNCbGNpNXlZV0p2WW1GdWF5NXViREVpTUNBR0NTcUdTSWIzRFFFSkFSWVRaWGhoYlhCc1pVQnlZV0p2WW1GdWF5NXViRENDQWlJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFPQUo5SHd0ZkxicXlzVm5XYmZiU2V2UjhZNFF1YmoySkpFemFudjNqSWkwRTJjZHRjRGE3VWdNWmlGWXpLeDNhaWdzbGpoNGM1KzVNS3dkOXpkandDUjZKejNzMURhYUhsdXZRWmpXM1h1TDJzZzFSYm5LNHFBa3FMaWU4NU5xZzRqY1YwU0l4ZmJIcGJaWHF5T2xlcEhWdDJxSWtuaVdCZnBPeWxNdTJHY1RLZUJEYkYwdVdGK0JoNmRKNnNVbEtmNUFnRTF3NFhXTkJ1eEVFMU9hNE1iN3djQXliUmRqSnBxYkdCb1ppeFZ6T1hnZzAwMnBTVzdxa1E2dG1tLzM2WjUrN1lqWnVtMzAwZ05VWEIxYmc0TnRLL3dIQVVCcUcyQ1FJN3lWV1FnWGZBK094azhxeFFMc3F2VUxEcGNPNTNKaEZOb1lsaklxbXdsQW1tQjVhRVJjRzM3TUJUY0U1K2FrQ2dQZ2xjYzJucHExV0dORGtFZk1JbUNzRWpsOGFTS0psUVJGaVVKK3JyVlB3bFhWZXAxODl4aTcwcWJtL0E5NzJlbno4b2JBTDFWbExZOGJUdEFTOUZ2L2FsaWd1am1RR1U4Qy9MWmNRRXNHcnlReVlWZ3h2RUJqUGpSQ0l1dFlZZVFtN0I3eFpxNjNZc2FibEo3OUkwRnMyZkg1MnN4L09WYXk0WjNWWE95S096L3NBQ3d6bkJ4bXRnd05xMVNsTUowVmFXcjhmR29RT0NVLzRSRGwzNjBRZnNaMmZmQ0tJNk5sVkxCdlVub2lxZHU2bmVjZHM1ekpUMHVidlNUMjRLNUI3V0IvVHZLbE11S0EzT3ZiTjMwMXRNRkN5T09acE1McFJ3UGNBN0p6ZTc1QUZURGtXcExTeWc3TGRPcjV5WFpNVVlxVkFnTUJBQUV3RFFZSktvWklodmNOQVFFTEJRQURnZ0lCQUNJa2NkQWNGR2lka3N5d3ZTK2FySVBJYmJSandCckEvTW43ZFJUS2xBZU5LejIxNWppZzFDQkJ5ZVdqeVNVMHpnaHJ1Q3c0ZzZrLzFpWE92ZHJrSjdqWTluaURKUDkrNDc3dEVud3RtRlZEaTZKNTJnTm12YkJIWUxkMlVsWFU3Mm1FcjlMR3AxWHRnR3RtMTlOYm1kcGQ2MGduUlhlNE04WEwzU1FxQ2UxcDVkdFBoRENjZEcyam1TSEp2bHJIUFdKcjRzUk9HNGlFY0xpN0h0N1prMVlNdEZzME03TGJsN1grOHhtNVFmc3JHRkFSc1FRRHdCc1J5MitQMy9xOWVKYlVJaG9zNHFUOGZndDk3UllHTmVhOEFWQU9VcVkrN2ZzMTQ1MFZlREtWY0dmR0hIWlMvdGdNMUczTndqdWl6QWs5TGRoUmtOTXJDbWo0bmRBdVpuZ1U3NEg2b3NHc1YzNEwvenhIWmZCSTJHeHk1VnJINWxoVEdYL1lxR3FIellvQzBGY3RaOW9JVlR1TkVoUk5GVkFWYzU4Tit6MTFHRWZzZE9jaitIbkNxOUhDTlAzS0l3cTYwUnNSTUN2WU9mMHJteWR6YUd6ei9vS2RUdFd2Y3ZLdDlBWTdqZ0h1a21zK3pZUEFlT01Pd3o5Mk9wSzM4dWoyaFJwSWZ5d0NFT1JUNTFDTW0xN05xYzR4dE9IeXZGQUhLNHViaTVUeU1sVjRibGczMEh0T09rV05rQ21DNTFvdUxrRXRzUnNtRkZzVStidi8xMTFVNmZoQXA4VWQ3Y2VwK29oMDZNRUZ4STFLblpEVUY5bWVTMkpuL3V2NlpwYThseVNiTjc4anJqbFBuaTJCRUdlaURldzFwM3BjMlUxSXlnc2VaWkxEZUpob0RYSnBGM2c3Il0gfQ.eyAicHRjX2VtYWlsIjogImV4YW1wbGVAcmFib2JhbmsubmwiLCAiZXhwIjogMTU0MDgwNjU5IH0
Step 3.2: Signing algorithm
To sign the string (with RSA SHA256), you must use:
- The X.509 QSeal eIDAS certificate.
- Its corresponding private key.
SIGNATURE = RSA_SHA256(
SIGNING_STRING,
PUBLIC_CERTIFICATE,
PRIVATE_KEY
)
Step 4. Creating the API request body
When you create the body of the API request, you must encode the text to URL-safe Base64 (see RFC 4648.) You must encode each part of the JWS:
{
"protected": BASE64URL(PROTECTED),
"payload": BASE64URL(PAYLOAD),
"signature": BASE64URL(SIGNATURE)
}
Example:
{
"protected": "eyAiYWxnIjogIlJTMjU2IiwgIng1YyI6IFsiTUlJRmVEQ0NBMkFDQ1FDUENNL1oreTkxMVRBTkJna3Foa2lHOXcwQkFRc0ZBREIrTVFzd0NRWURWUVFHRXdKT1RERVJNQThHQTFVRUNnd0lVbUZpYjJKaGJtc3hHREFXQmdOVkJBc01EMUJUUkRJZ1JXNXliMnhzYldWdWRERWVNQndHQTFVRUF3d1ZaR1YyWld4dmNHVnlMbkpoWW05aVlXNXJMbTVzTVNJd0lBWUpLb1pJaHZjTkFRa0JGaE5sZUdGdGNHeGxRSEpoWW05aVlXNXJMbTVzTUI0WERURTVNRFF3TlRFMU5EQTBPRm9YRFRJd01EUXdOREUxTkRBME9Gb3dmakVMTUFrR0ExVUVCaE1DVGt3eEVUQVBCZ05WQkFvTUNGSmhZbTlpWVc1ck1SZ3dGZ1lEVlFRTERBOVFVMFF5SUVWdWNtOXNiRzFsYm5ReEhqQWNCZ05WQkFNTUZXUmxkbVZzYjNCbGNpNXlZV0p2WW1GdWF5NXViREVpTUNBR0NTcUdTSWIzRFFFSkFSWVRaWGhoYlhCc1pVQnlZV0p2WW1GdWF5NXViRENDQWlJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFPQUo5SHd0ZkxicXlzVm5XYmZiU2V2UjhZNFF1YmoySkpFemFudjNqSWkwRTJjZHRjRGE3VWdNWmlGWXpLeDNhaWdzbGpoNGM1KzVNS3dkOXpkandDUjZKejNzMURhYUhsdXZRWmpXM1h1TDJzZzFSYm5LNHFBa3FMaWU4NU5xZzRqY1YwU0l4ZmJIcGJaWHF5T2xlcEhWdDJxSWtuaVdCZnBPeWxNdTJHY1RLZUJEYkYwdVdGK0JoNmRKNnNVbEtmNUFnRTF3NFhXTkJ1eEVFMU9hNE1iN3djQXliUmRqSnBxYkdCb1ppeFZ6T1hnZzAwMnBTVzdxa1E2dG1tLzM2WjUrN1lqWnVtMzAwZ05VWEIxYmc0TnRLL3dIQVVCcUcyQ1FJN3lWV1FnWGZBK094azhxeFFMc3F2VUxEcGNPNTNKaEZOb1lsaklxbXdsQW1tQjVhRVJjRzM3TUJUY0U1K2FrQ2dQZ2xjYzJucHExV0dORGtFZk1JbUNzRWpsOGFTS0psUVJGaVVKK3JyVlB3bFhWZXAxODl4aTcwcWJtL0E5NzJlbno4b2JBTDFWbExZOGJUdEFTOUZ2L2FsaWd1am1RR1U4Qy9MWmNRRXNHcnlReVlWZ3h2RUJqUGpSQ0l1dFlZZVFtN0I3eFpxNjNZc2FibEo3OUkwRnMyZkg1MnN4L09WYXk0WjNWWE95S096L3NBQ3d6bkJ4bXRnd05xMVNsTUowVmFXcjhmR29RT0NVLzRSRGwzNjBRZnNaMmZmQ0tJNk5sVkxCdlVub2lxZHU2bmVjZHM1ekpUMHVidlNUMjRLNUI3V0IvVHZLbE11S0EzT3ZiTjMwMXRNRkN5T09acE1McFJ3UGNBN0p6ZTc1QUZURGtXcExTeWc3TGRPcjV5WFpNVVlxVkFnTUJBQUV3RFFZSktvWklodmNOQVFFTEJRQURnZ0lCQUNJa2NkQWNGR2lka3N5d3ZTK2FySVBJYmJSandCckEvTW43ZFJUS2xBZU5LejIxNWppZzFDQkJ5ZVdqeVNVMHpnaHJ1Q3c0ZzZrLzFpWE92ZHJrSjdqWTluaURKUDkrNDc3dEVud3RtRlZEaTZKNTJnTm12YkJIWUxkMlVsWFU3Mm1FcjlMR3AxWHRnR3RtMTlOYm1kcGQ2MGduUlhlNE04WEwzU1FxQ2UxcDVkdFBoRENjZEcyam1TSEp2bHJIUFdKcjRzUk9HNGlFY0xpN0h0N1prMVlNdEZzME03TGJsN1grOHhtNVFmc3JHRkFSc1FRRHdCc1J5MitQMy9xOWVKYlVJaG9zNHFUOGZndDk3UllHTmVhOEFWQU9VcVkrN2ZzMTQ1MFZlREtWY0dmR0hIWlMvdGdNMUczTndqdWl6QWs5TGRoUmtOTXJDbWo0bmRBdVpuZ1U3NEg2b3NHc1YzNEwvenhIWmZCSTJHeHk1VnJINWxoVEdYL1lxR3FIellvQzBGY3RaOW9JVlR1TkVoUk5GVkFWYzU4Tit6MTFHRWZzZE9jaitIbkNxOUhDTlAzS0l3cTYwUnNSTUN2WU9mMHJteWR6YUd6ei9vS2RUdFd2Y3ZLdDlBWTdqZ0h1a21zK3pZUEFlT01Pd3o5Mk9wSzM4dWoyaFJwSWZ5d0NFT1JUNTFDTW0xN05xYzR4dE9IeXZGQUhLNHViaTVUeU1sVjRibGczMEh0T09rV05rQ21DNTFvdUxrRXRzUnNtRkZzVStidi8xMTFVNmZoQXA4VWQ3Y2VwK29oMDZNRUZ4STFLblpEVUY5bWVTMkpuL3V2NlpwYThseVNiTjc4anJqbFBuaTJCRUdlaURldzFwM3BjMlUxSXlnc2VaWkxEZUpob0RYSnBGM2c3Il0gfQ",
"payload": "eyAicHRjX2VtYWlsIjogImV4YW1wbGVAcmFib2JhbmsubmwiLCAiZXhwIjogMTU0MDgwNjU5IH0",
"signature": "wHAILEDbUN1RnGVnNGV_flHm35SC_AojfUUpep_IgOO1l6SkE2Zo3bk7VMnW64R74ksSl5Sp2dFa52iGOKgtZK5JUF3s5tuy5fuRMoTgW5qWH9WFFa2HSjpAEzJolU38vJtrQsdxxIR-AbrGVZoGR0f07e_lzxqqMCzBgTLT3p-ADuzJQ7jJoCvbVEVsc0HPtcT9UdLU87p3qb-j21QjYrN3R-pL7I5tuPBcP9G_xcmF9HZY92aWVzME-KfVWI1OKUeacvvGeKq7d_k6yPiFyUatJltEoHXjRkO1ViQBTIhbNjq_tWGkI2Q6El0kghDEQ6OE5ro7FmMlXBXOCGFAYlhA7xIPA4FgEb8CJ2tbRHk7Q4sOsR04GFx9EUKZBhEejF_DkZ-w0VdiAN_yOZmLZQvzUnSab7jw6e7ptVvsP7OEtXArydKKXIa5mUccsFTL4yAw6MjPttw41bnwcTLfqHYci3Pz1ROSAWAhMWr_ce0kwOc3752qA8k3GHnZPt8voBQG0BrH-c10ruhREtdskhcm7FSQU-vi9AjcJ_gyS7EK5qAyaWrzLmvd8aqH605HHmOBb3EkYJERDCm_NG3LCpd-ZmzT6th2kMWWsCVG_GNO_1pZh9rr5-_jMuGpOlrXwqRb2dT_Sb_m3hFuLJSDV6VCUk-aird8eELa-GtweMI"
}
More information on the Flattened JWS JSON Serialization format
The JSON Web Signature (JWS) JSON Serialization is a format for representing content secured with digital signature as a JSON object. The Flattened JWS JSON Serialization format is optimized for a single digital signature.
For more information on the Flattened JWS JSON Serialization format, see:
- Section 7.2.2 of RFC 7515.
- The x5c parameter (RFC 7515 section 4.1.6).
- The Base16, Base32, and Base64 Data Encodings (RFC 4648).