Signature Verification
All messages sent via the webhook mechanism will include a signature in the message header in accordance with the HTTP Message Signatures standard. The merchant’s POS system should verify that the signature is correct before accepting the message.
The signature is computed across one or more HTTP fields. For clarity, an HTTP field is defined as:
A "Structured Header" or "Structured Trailer" (if the field can be either, it is a "Structured Field") uses the types defined in this specification to define its syntax and basic handling rules, thereby simplifying both its definition by specification writers and handling by implementations.
To verify the signature the following process should be followed:
- Parse the signature information
- Parse the signature input as a dictionary structured field value
- Parse the signature as a dictionary structured field value
- Check each signature has a corresponding signature input entry
- Verify the signature (sig 1)
- If the required signature does not exist, Fail.
- Build the signature base
- Start with an empty string as the output
- Iterate through each signature input field
- Append the identifier surrounded in double quotes (“) to the output
- Append a colon (:) followed by a single space to the output
- If the parameters associated with the field contain “req”, “bs”, or “sf”, Fail.
- Obtain the string representation of the value
- If the identifier starts with a @ symbol, the field is a derived field and comes from the message data (note: DNA Payments are not currently utilising derived fields).
@method
- refers to the HTTP method of a request message@target-uri
- refers to the target URI of a request message@authority
- refers to the authority component of the target URI of the HTTP request message@scheme
- refers to the scheme of the target URL of the HTTP request message@request-target
- refers to the full request target of the HTTP request message@path
- refers to the target path of the HTTP request message@query
- refers to the query component of the HTTP request message@query-param
- allows addressing of individual query parameters
- If the field has the tr parameter and the identifier does not start with a @ symbol, the field is a trailer field (note: DNA Payments are not currently utilising trailer fields)
- If the identifier does not start with a @ symbol and is not a trailer field, the field is a header field and should be formatted as a serialized structured field.
- If the identifier is not supported, Fail.
- If the identifier starts with a @ symbol, the field is a derived field and comes from the message data (note: DNA Payments are not currently utilising derived fields).
- If the identifier is the
content-digest
, verify the content digest- Compute the hash of the message data
- Obtain the value of the
content-digest
- Compare the computed hash to the obtained value
- Append the string representation of the value to the output
- Append a newline (\n) value to the output
- Append the signature params identifier
@signature-params
- Append a colon (:) followed by a single space to the output
- Append the signature input value as a structured dictionary serialized field
- Load the public key
- If the key does not exist, Fail.
- Verify the signature
- Obtain the ASCII byte representation of the signature base
- Obtain the signature bytes
- Perform an RSA verify data operation using SHA512 with PKCS#1 padding
Useful documents
Describes this mechanism for creating, encoding, and verifying digital signatures or message authentication codes over components of an HTTP message.
RFC 8941 - Structured Field Values for HTTP
Describes the set of data types and associated algorithms that are intended to make it easier and safer to define and handle HTTP header and trailer fields
Example Response
Response
Content-Type
application/json; charset=utf-8
Content-digest
sha-256=:q4FNNYngaW3UfqdavrJkfA9Y1Mv8O0uzV8bMxpvPToI=:
Content-Length
915
Signature-Input
sig1=("content-type" "content-digest" "content-length");created=1671551150;keyid="AxeptConnectCloudTerminal-RequestSigningKey-Dev"
Signature
sig1=:ch0U8ixdMQfsrk7K1q+0nFLmovS9vpe
XQRYvZii+YjHNZRlWeCXod2pwu11CAdqrKGDN
oHOW6OY12vkukBay2sFfZCxLm4LHeB4VwGzR6
TwdPedsZzTt3CafxstLgQTqRlXUV4wmmwfMcQ
7W3rGs/7PaeAOO1iuoNCL1hNHQWn/lGWHHL9W
gk9Y3v7BY/oxAordDbPUyiqoXIpRFZ+khSNgV
dEkjd0xTaQXwaR9/6pWcZP8Jb1D9FYuS1ON5c
6ajHJxvqGNxqQpQWNCMPuvTPzk7jDBcdRx6+v
aax/myAEDiMdEVSkgZyFYgNgumuyrkPwXMcow
CTHjULi6tYuSSNQ==:
Note: Remove any CR/LF characters
Payload
{
"uti": "314f78a2-c533-4663-8be6-ab039d8ecd79",
"transType": "SALE",
"transApproved": <strong>true</strong>,
"transPartiallyApproved": <strong>false</strong>,
"transCancelled": <strong>false</strong>,
"amountTrans": 100,
"amountGratuity": 0,
"amountCashback": 0,
"CvmSignatureRequired": <strong>false</strong>,
"CvmPinVerified": <strong>false</strong>,
"transCurrencyCode": "826",
"TerminalId": "08983456",
"SoftwareVersion": "0.2.1",
"ReceiptNumber": "1234",
"paymentId": "6248/42/14122022121630253",
"ResponseCode": "00",
"stan": "543210",
"authorisationCode": "123ABC",
"MerchantTokenId": "098585392049583043",
"cardPan": "***********2314214",
"CardExpiryDate": "2412",
"CardStartDate": "2104",
"CardScheme": "Visa",
"CardPanSequenceNumber": "2",
"CardType": "EMV",
"Reference": "test2",
"transDateTime": "2022-11-11T14:11:56.03Z",
"CreatedDateTime": "2022-12-14T12:44:22.3555647",
"MerchantStoreId": 10221
}
Signature Base
The above details produce the following signature base:
"content-type": application/json; charset=utf-8
"content-digest": sha-256=:q4FNNYngaW3UfqdavrJkfA9Y1Mv8O0uzV8bMxpvPToI=:
"content-length": 915
"@signature-params": ("content-type" "content-digest" "content-length");created=1671551150;keyid="AxeptConnectCloudTerminal-RequestSigningKey-Dev"
Public Key
The public key used in this example was:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2PLPnH1/spdapJYJUxqD
bMeIpIrA6f1uJCjDMEJQMwDDmEViClTKLEEO62Cc7KmIamyilo6wfBtFKgxD1PCi
yuwMt7Nd/kXf6DS4OEv9XSEqAgvF11FAJ4fZ313OlKY0sFzMm/N6yE22BCD9HK13
g334BgBbSFcHRZM8tWu1tLq5+EP3OPko6jIQgy4I51tUn1fXpn+Xavx97fVp49tT
xlDzvbGxfDWpQdLk1BpJcLG5O2F+vvZpssld0tLlsONaV4FR6XYF10raGExcuNk0
/jpujewybxUaXAYLPiZ3+VveJ63k5phyNDC90StdhIYAVmH+7QtYMwQ/SKZnyfi5
SwIDAQAB
-----END PUBLIC KEY-----
The above key is used in our Pre-Production Environment. You will be provided a Live Public RSA Key as part of your initial order.