Skip to main content

Generate and Validate Signature

About

Signature is a security parameter that needs to be generated on merchant's Backend to verify the request authenticity.

Here is the case the Signature is used:

  1. When merchant hits DOKU endpoints
    1. Merchant generate Signature from Request Header : Merchant must generate the signature in request header and DOKU will verify the authenticity.
    2. Merchant validate Signature from Response Header : DOKU must generate the signature in response header and Merchant will verify the authenticity.
  2. When DOKU hits merchant endpoints (HTTP Notification / Inquiry Request).
    Merchant must also verify the Signature that DOKU sends on the request header when DOKU Notification hits merchant Notification URL, to verify the notification request is coming from DOKU. You can check the detail for signature in HERE

Signature components from Request Header

To generate a Signature in request header, merchant need to prepare these components:

Client-Id:value
Request-Id:value
Request-Timestamp:value
Request-Target:value
Digest:value

Component Explanation

NameDescription
Client-IdRetrieved from the Request Header
Request-IdRetrieved from the Request Header
Request-TimestampRetrieved from the Request Header
Request-TargetThe path of the endpoint that will be hitted e.g: /doku-virtual-account/v2/payment-code.

NOTE: For the HTTP Notification from DOKU to merchant server, this will be the path of merchant Notification URL. As for the Inquiry Request, this will be the path of merchant Inquiry URL
DigestEncoded (base64) value of hashed (SHA-256) JSON body. This component only applied for POST Method.

Preparation

Before generating Signature, merchant need to prepare all the component required.

Set Client-Id, Request-Id, Request-Timestamp.

Use the Client-Id, Request-Id, Request-Timestamp that is placed on the Request Header.

Set Request-Target

The Request-Target is depending on who is sending the request:

  1. When merchant hits DOKU endpoints: The Request-Target is the path of the DOKU API that merchant hits.
    For instance, if merchant wants to hit DOKU VA API: https://api.doku.com/doku-virtual-account/v2/payment-code. Therefore, the Request-Target value is /doku-virtual-account/v2/payment-code.
  2. When DOKU hits merchant endpoints (HTTP Notification / Inquiry Request): The Request-Target is the path of merchant Notification URL or the Inquiry URL.
    For instance, if merchant set the Notification URL: https://yourdomain.com/payments/notifications. Therefore, the Request-Target value is /payments/notifications.

Generate Digest

Digest is the hashed of the request body. To generate the Digest:

  1. Calculate SHA256 base64 hash from the JSON Body

Generating Signature

After all the Signature component has been set, merchant can now generate it:

  1. Arrange the signature components to one component and its value per line by adding \n escape character. Don't add \n at the end of the string. Sample of the raw format:
Client-Id:MCH-0001-10791114622547\nRequest-Id:cc682442-6c22-493e-8121-b9ef6b3fa728\nRequest-Timestamp:2020-08-11T08:45:42Z\nRequest-Target:/doku-virtual-account/v2/payment-code\nDigest:5WIYK2TJg6iiZ0d5v4IXSR0EkYEkYOezJIma3Ufli5s=

This is how merchant see it:

Client-Id:MCH-0001-10791114622547
Request-Id:cc682442-6c22-493e-8121-b9ef6b3fa728
Request-Timestamp:2020-08-11T08:45:42Z
Request-Target:/doku-virtual-account/v2/payment-code
Digest:5WIYK2TJg6iiZ0d5v4IXSR0EkYEkYOezJIma3Ufli5s=
  1. Calculate HMAC-SHA256 base64 from all the components above using the Secret Key from DOKU Back Office
  2. Put encoded value and prepend HMACSHA256= to the Signature. Sample:
Signature: HMACSHA256=OvIRJs/jH8BIcGsktr4d8nnYtxY6E0Uzdm9d1GVgv5s=

Signature Component from Response Header

To validate a signature in response header, merchant need to see and check these components.

Client-Id:value
Request-Id:value
Response-Timestamp:value
Request-Target:value
Digest:value

Component Explanation

NameDescription
Client-IdRetrieved from the Request Header
Request-IdRetrieved from the Request Header
Response-TimestampRetrieved from the Response Header
Request-TargetThe path of the endpoint that will be hitted e.g: /doku-virtual-account/v2/payment-code.

DigestEncoded (base64) value of hashed (SHA-256) JSON body. This component only applied for POST Method.

Preparation

Before validating Signature, merchant need to check all the component required.

Set Client-Id, Request-Id, Response-Timestamp.

Use the Client-Id, Request-Id, Response-Timestamp that is placed on the Response Header.

Set Request-Target

The Request-Target is depending on who is sending the request:

  1. When merchant hits DOKU endpoints: The Request-Target is the path of the DOKU API that merchant hits.

Validating Signature

After merchant send request to DOKU and generate signature in request header, DOKU will send response and generate signature in response header. Then merchant can verify this response is coming from DOKU by Signature.

  1. Arrange the signature components to one component and its value per line by adding \n escape character. Don't add \n at the end of the string. Sample of the raw format:
Client-Id:MCH-0001-10791114622547\nRequest-Id:cc682442-6c22-493e-8121-b9ef6b3fa728\Response-Timestamp:2020-08-11T08:45:42Z\nRequest-Target:/doku-virtual-account/v2/payment-code\nDigest:5WIYK2TJg6iiZ0d5v4IXSR0EkYEkYOezJIma3Ufli5s=

This is how merchant see it:

Client-Id:MCH-0001-10791114622547
Request-Id:cc682442-6c22-493e-8121-b9ef6b3fa728
Response-Timestamp:2020-08-11T08:45:42Z
Request-Target:/doku-virtual-account/v2/payment-code
Digest:5WIYK2TJg6iiZ0d5v4IXSR0EkYEkYOezJIma3Ufli5s=
  1. Calculate HMAC-SHA256 base64 from all the components above using the Secret Key from DOKU Back Office
  2. Put encoded value and prepend HMACSHA256= to the Signature. Sample:
Signature: HMACSHA256=OvIRJs/jH8BIcGsktr4d8nnYtxY6E0Uzdm9d1GVgv5s=
info

To make sure every response API from DOKU, just verify in Signature that you get from Response Header!

API with GET Method

For API that uses GET method such as, Check Status API, merchant don't need to generate a Digest.

  1. Arrange the signature components to one component and its value per line by adding \n escape character. Don't add \n at the end of the string. Sample of the raw format:
Client-Id:MCH-0001-10791114622547\nRequest-Id:d895fb53-479c-4f77-a76a-ab81b40d77cb\nRequest-Timestamp:2020-08-11T08:45:42Z\nRequest-Target:/orders/v1/status/INV-123123-12313

This is how merchant see it:

Client-Id:MCH-0001-10791114622547
Request-Id:d895fb53-479c-4f77-a76a-ab81b40d77cb
Request-Timestamp:2020-08-11T08:45:42Z
Request-Target:/orders/v1/status/INV-123123-12313
  1. Calculate HMAC-SHA256 base64 from all the components above using the Secret Key from DOKU Back Office
  2. Put encoded value and prepend HMACSHA256= to the Signature. Sample:
Signature: HMACSHA256=B1cKBzk/aB1AXADCZkq135bnktxY1o02zmmdd2cVgf12=

Sample code

Here is the sample code to generate the Signature:

Sample Only

This is just a sample code to demonstrate how to generate the Signature on different programming language. Kindly adjust the code to suited your project's structure.


import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

public class Signature {

public static final String CLIENT_ID = "Client-Id";
public static final String REQUEST_ID = "Request-Id";
public static final String REQUEST_TIMESTAMP = "Request-Timestamp";
public static final String REQUEST_TARGET = "Request-Target";
public static final String DIGEST = "Digest";
public static final String COLON_SYMBOL = ":";
public static final String NEW_LINE = "\n";

// Generate Digest
public static String generateDigest(String requestBody) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(requestBody.getBytes(StandardCharsets.UTF_8));
byte[] digest = md.digest();
return Base64.getEncoder().encodeToString(digest);
}

private static String generateSignature(String clientId, String requestId, String requestTimestamp, String requestTarget, String digest, String secret) throws InvalidKeyException, NoSuchAlgorithmException {
// Prepare Signature Component
System.out.println("----- Component Signature -----");
StringBuilder component = new StringBuilder();
component.append(CLIENT_ID).append(COLON_SYMBOL).append(clientId);
component.append(NEW_LINE);
component.append(REQUEST_ID).append(COLON_SYMBOL).append(requestId);
component.append(NEW_LINE);
component.append(REQUEST_TIMESTAMP).append(COLON_SYMBOL).append(requestTimestamp);
component.append(NEW_LINE);
component.append(REQUEST_TARGET).append(COLON_SYMBOL).append(requestTarget);
// If body not send when access API with HTTP method GET/DELETE
if(digest != null && !digest.isEmpty()) {
component.append(NEW_LINE);
component.append(DIGEST).append(COLON_SYMBOL).append(digest);
}

System.out.println(component.toString());
System.out.println();

// Calculate HMAC-SHA256 base64 from all the components above
byte[] decodedKey = secret.getBytes();
SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "HmacSHA256");
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
hmacSha256.init(originalKey);
hmacSha256.update(component.toString().getBytes());
byte[] HmacSha256DigestBytes = hmacSha256.doFinal();
String signature = Base64.getEncoder().encodeToString(HmacSha256DigestBytes);
// Prepend encoded result with algorithm info HMACSHA256=
return "HMACSHA256="+signature;
}

// Sample of Usage
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
String jsonBody = new JSONObject()
.put("order", new JSONObject()
.put("invoice_number", "INV-20210124-0001")
.put("amount", 15000)
)
.put("virtual_account_info", new JSONObject()
.put("expired_time", 60)
.put("amount", 15000)
)
.toString();

// Generate Digest from JSON Body, For HTTP Method GET/DELETE don't need generate Digest
System.out.println("----- Digest -----");
String digest = generateDigest(jsonBody);
System.out.println(digest);
System.out.println();

// Generate Signature
String headerSignature = generateSignature(
"yourClientId",
"yourRequestId",
"2020-10-21T03:38:28Z",
"/request-target/goes-here", // For merchant request to DOKU, use DOKU path here. For HTTP Notification, use merchant path here
digest, // Set empty string for this argumentes if HTTP Method is GET/DELETE
"secret-key-from-DOKU-back-office");

System.out.println("----- Header Signature -----");
System.out.println(headerSignature);
}
}