Signing

We have deprecated the signing of the entire API request (the URL, headers and body). You now only need to sign the request body. Please switch to signing the body solely by April 28, 2020. Requests with full request signatures will stop being validated on that date.

We are legally required to protect our users and their data from malicious attacks and intrusions. That is why we beyond having a secure https connection, we use asymmetric cryptography for signing requests that create a session or payment. The use of signatures ensures the data is coming from the trusted party and was not modified after sending and before receiving.

Request body signing is only mandatory for the following operations:

  • open a session;

  • create a payment;

  • create a scheduled payment;

  • any other operation that executes a payment such as the following:

    • accept a draft payment;

    • accept a draft scheduled payment;

    • accept a payment request.

You will know that the API call must be encrypted if you get the 466 error code.

The signing mechanism is implemented in our SDKs so if you are using them you don't have to worry about the details described below.

The signatures are created using the SHA256 cryptographic hash function and are included (already encoded in Base64) in the X-Bunq-Client-Signature request header and the X-Bunq-Server-Signatureresponse header.

Here is the data you need to sign:

WHERE

DETAILS

Requests

  • body

Responses

  • the response code

  • headers, sorted alphabetically by key, with key and value separated by : (a colon followed by a space) and only including Cache-Control, User-Agent and headers starting with X-Bunq-. The headers should be separated from each other with a \n (linefeed) newline. For a full list of required call headers, see the headers page.

  • a \n (linefeed) newline separator

  • the response body

For signing requests, the client must use the private key corresponding to the public key that was sent to the server in the installation API call. That public key is what the server will use to verify the signature when it receives the request. In that same call the server will respond with a server side public key, which the client must use to verify the server's signatures. The generated RSA key pair must have key lengths of 2048 bits and adhere to the PKCS #8 standard.

Request signing example

Let's callPOST /v1/user/126/monetary-account/222/payment. Here is the request:

cache-control: no-cache
content-type: application/json
x-bunq-client-authentication: f15f1bbe1feba25efb00802fa127042b54101c8ec0a524c36464f5bb143d3b8b
user-agent: bunq-TestServer/1.00 sandbox/0.17b3
{
"amount": {
"value": "12.50",
"currency": "EUR"
},
"counterparty_alias": {
"type": "EMAIL",
"value": "bravo@bunq.com"
},
"description": "Payment for drinks."
}

Let's sign the request.

  • Create a $dataToSign variable with the body of the request.

  • On a new line follow that by a list of alphabetically-sorted headers only including Cache-Control, User-Agent and the headers starting with X-Bunq-. Convert to

    X-Header-Capitalization-Style from x-header-non-capitalization-style if needed.

  • Add an extra (so double) linefeed after the list of headers.

  • End with the body of the request.

So for our example above the request to sign will look like this:

{
"amount": {
"value": "12.50",
"currency": "EUR"
},
"counterparty_alias": {
"type": "EMAIL",
"value": "bravo@bunq.com"
},
"description": "Payment for drinks."
}
  • Create the signature to include in $dataToSign using the SHA256 algorithm and your private key.

Here is how to create a signature in PHP. The signature will be passed by reference into $signature.

openssl_sign($dataToSign, $signature, $privateKey, OPENSSL_ALGO_SHA256);

Encode the resulting $signature using Base64 and add the resulting value under the X-Bunq-Client-Signature header. It will look something like this: UINaaJELGHekiye4JExGx6TCs2lKMta74oVlZlwVNuVD6xPpH7RS6H58C21MmiQ75

You have signed your request! Send it!

Response verifying example

We sent the request and have received this response with code 200:

access-control-allow-origin: *
content-type: application/json
date: Thu, 07 Apr 2016 08:32:04 GMT
server: APACHE
strict-transport-security: max-age=31536000
transfer-encoding: chunked
x-bunq-client-response-id: 89dcaa5c-fa55-4068-9822-3f87985d2268
x-bunq-client-request-id: 57061b04b67ef
x-bunq-server-signature: ee9sDfzEhQ2L6Rquyh2XmJyNWdSBOBo6Z2eUYuM4bAOBCn9N5vjs6k6RROpagxXFXdGI9sT15tYCaLe5FS9aciIuJmrVW/SZCDWq/nOvSThi7+BwD9JFdG7zfR4afC8qfVABmjuMrtjaUFSrthyHS/5wEuDuax9qUZn6sVXcgZEq49hy4yHrV8257I4sSQIHRmgds4BXcGhPp266Z6pxjzAJbfyzt5JgJ8/suxgKvm/nYhnOfsgIIYCgcyh4DRrQltohiSon6x1ZsRIfQnCDlDDghaIxbryLfinT5Y4eU1eiCkFB4D69S4HbFXYyAxlqtX2W6Tvax6rIM2MMPNOh4Q==
x-frame-options: SAMEORIGIN
{
"Response": [
{
"Id": {
"id": 1561
}
}
]
}

The response will only contain X-Bunq-Client-Request-Id if you pass it with your request.

We need to verify that this response was sent by the bunq server and not from a man-in-the-middle.

  • Create a $dataToSign variable starting with the response code (200).

  • On a new line follow that by a list of alphabetically-sorted headers only including headers starting with X-Bunq-. Convert to

    X-Header-Capitalization-Style from x-header-non-capitalization-style if needed.

  • Add an extra (so double) linefeed after the list of headers.

  • End with the body of the request.

We are deprecating full response signature and will start only signing the response body on April 28, 2020.

So for our example above the response to sign will look like this:

200
X-Bunq-Client-Request-Id: 57061b04b67ef
X-Bunq-Server-Response-Id: 89dcaa5c-fa55-4068-9822-3f87985d2268
{
"Response": [
{
"Id": {
"id": 1561
}
}
]
}
  • Verify the signature of $dataToVerify using the SHA256 algorithm and the $publicKey of the server.

Here is how you can verify the signature in PHP:

openssl_sign($dataToVerify, $signature, $publicKey, OPENSSL_ALGO_SHA256);

Troubleshooting

If you get this error The request signature is invalid, please check the following:

  • There are no redundant characters (extra spaces, trailing line breaks, etc.) in the data to sign.

  • In your data to sign, you have used only the endpoint URL (POST /v1/user) instead of the full URL (POST https://sandbox.public.api.bunq.com/v1/user).

  • You only added the Cache-Control, User-Agent and X-Bunq- headers.

  • You have sorted the headers alphabetically by key in the ascending order.

  • There is a colon followed by a space :. It separates the header key and value in your data to sign.

  • There is an extra line break after the list of headers in the data to sign, regardless of whether there is a request body.

  • Make sure the body is appended to the data to sign exactly as you are adding it to the request.

  • You have not added the X-Bunq-Client-Signature header to the list of headers.

  • You have added the full body to the data to sign.

  • You are using the data to sign to create a SHA256 hash signature.

  • You have Base64 encoded the SHA256 hash signature before adding it to the request under the X-Bunq-Client-Signature header.