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:

  1. Protected (Header).
  2. Payload.
  3. Signature

Step 1. Creating the Protected Header

The protected header must contain:

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: