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);
    }
}

Last updated