Generate Signature

About

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

Signature components

To generate a Signature. You need to prepare these components:

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

Component Explanation

NameDescription
Client-IdClient ID you retrieved from Jokul Back Office. e.g: MCH-0001-10791114622547
Request-IdUnique random string (max 128 characters) generated by merchant to identify your request and protect you from duplicate request. e.g: 8quQyK39l4aM5cCml0Yy
Request-TimestampISO 8601 timestamp format. e.g: 2020-08-11T08:45:42Z
Request-TargetJokul resource-path minus host domain. e.g: /doku-virtual-account/v2/payment-code
DigestEncoded (base64) value of hashed (SHA-256) JSON body. e.g: 5WIYK2TJg6iiZ0d5v4IXSR0EkYEkYOezJIma3Ufli5s=

Preparation

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

Generate Digest

Hash of your JSON body request and as one of required component to generate Signature. To generate the digest:

  1. Calculate SHA256 base64 hash from your JSON Body

Set Request-Target

Value of field Request-Target is Jokul resource-path minus host domain. For instance, resource-path access by merchant: https://api.doku.com/doku-virtual-account/v2/payment-code. Therefore, the Request-Target value is /doku-virtual-account/v2/payment-code.

Request-Target for Notification

Jokul also generate Signature when sending HTTP Notification for certain payment channels. You need to verify the authenticity to ensure the Notification Request coming from Jokul or not. As for the Request-Target, Jokul will use your resource-path of your Notification URL.

For instance, you set the Notification URL https://yourwebsite.com/payments/notifications, therefore the Request-Target is /payments/notifications.

How to generate 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:8quQyK39l4aM5cCml0Yy\nRequest-Timestamp:2020-08-11T08:45:42Z\nRequest-Target:/doku-virtual-account/v2/payment-code\nDigest:5WIYK2TJg6iiZ0d5v4IXSR0EkYEkYOezJIma3Ufli5s=

This is how you see it:

Client-Id:MCH-0001-10791114622547
Request-Id:f9475948-c551-4fdb-996d-2ee20513913c
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 Jokul Back Office
  2. Put encoded value and prepend HMACSHA256= to the Signature. Sample:
Signature: HMACSHA256=OvIRJs/jH8BIcGsktr4d8nnYtxY6E0Uzdm9d1GVgv5s=

For API with GET and DELETE Method

The sample above is for API with POST method. For API that uses GET method, you 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:8quQyK39l4aM5cCml0Yy\nRequest-Timestamp:2020-08-11T08:45:42Z\nRequest-Target:/orders/v1/status/INV-123123-12313

This is how you 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 Jokul 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 make it easier for you to generate the Signature:

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 DOKUSignature {
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 myBodyJson) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(myBodyJson.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 = "{\"name\": \"john doe\"}";
// 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",
digest, // Set empty string for this argumentes if HTTP Method is GET/DELETE
"secret-key-from-jokul-back-office");
System.out.println("----- Header Signature -----");
System.out.println(headerSignature);
}
}

That's it! You can now put the Signature into your request header.