Signature Generation

This section will explain how merchants can generate a signature using SNAP specification.

B2B Access Token Specification

B2B Access Token API header consists of the following fields:

Field NameField TypeMandatoryField Description
Content-typeStringMMedia type of the resource, i.e. application/json
X-TIMESTAMPStringMClient’s current local time in ISO-8601 format
X-SIGNATUREStringMCreated using asymmetric signature SHA256withRSA algorithm
X-CLIENT-KEYStringMClient ID
Content-type: application/json
X-TIMESTAMP: 2023-01-01T00:00:00+07:00
X-SIGNATURE: da1fa417c72d6b91c257e01e54fac824
X-CLIENT-KEY: 962489e9-de5d-4eb7-92a4-b07d44d64bf4

Asymmetric Signature SHA256withRSA

Based on the specification above, merchant should use asymmetric signature SHA256withRSA for Access Token API.

Merchant should use this formula to create X-SIGNATURE for Access Token API:
Client ID + “|” + Timestamp encrypted with merchant’s private key by using SHA256withRSA algorithm

For a better understanding, please refer to this sample scenario for generating signature using the formula above on B2B Access Token API:

No.Steps
1Given merchant has private key

-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCVZu2WHd4z5tbe
fkAWPjbB10Lgvu1CpsuIIVtO/wrrvKeD2LArmYSf5QxeJGG8HYlVYVp8I8/EVqay
JUbcyOd4aMLQFiCambDipecCfI7ecJ0K4+2ZneWOEq9JM91ZnGtwuLVdr1X/4/Nv
yGuNvmLAyfpJkU14yZliAUqJJ656jpM+WfY+hOpcLOzUe2wzx5RV7wYCyMl7/d1i
X33afJc6kbbUaA27yjlPdY6m0+DoZ0NNaRNZS7jzfJFtVXLTg/PgvwMNlMO/5Zyw
BWIOfWsD+MHUC6P9QYrHai8ZwEf9r+GivlNsAFs4NTpw9MdPngrexJm29k1Xh4UM
HV//jB1/AgMBAAECggEADcXGyitns/4oOauGyeYjUxw+eIxxP88zfRGiIralMZUb
Fi7wEpzc2oaZbMZK0jYg1mOanU4J1a4tQMfp7+l/WRzDNL6Nc+MOKN6lXJfR7dSQ
zZO0cBBbvIyhZwymb5/ZUbNdWM0UjvnbE6d0rsTpwp77+TMxYpynDJ9U2S70ySxe
fGi7x4rK02/8xmj1fY/Ls+uoYhF4IbGJIkFvrlAdWyFvv6sCGBYxl5sPuitgQxPv
Yc7dS7HE+6nZ+wWgoIXjC4CfxHBa9u3dfu3XAnPY3xpnVsbxCAH9OOIrZPY0C/Pq
gj+EElCBChUzDiedV3XipKUweDA+64sNm5NjqlqU8QKBgQDFf5a9vHVf9r2CTOgm
z5wTrK7aTwKwCNC4zpZUxDR8k7AV2gTelabpPWqUMZyRMUPs2hyBvhEWVNKxx3qs
7BAVpmcttuS5/dLNVdhNPUC2IgZqf3jWH2aS3lkci8DATQCphrEXZ3jrNaIRtrSC
kflE+yY8YP8b7ObktgvHz39JMQKBgQDBqCsMmOsQAveIQk5FGAMxsFU9G7OIBFHo
xpwkKUCjoqGxM+YQqDvcVeXNd/HdLnmR05//eoONemuDyYy8XRoud4tpaRKU/FK7
06WdljztB5rn2+cdUbmcU0fZiRQk+WuXsopZn3GUNvA/fQOW9qs7B5Rm5W71FeGk
ZgsvlAUlrwKBgQCUgFFaLWCcXa01UpqkxCp5aLi5EfvVXWuD6mKDLlzA51PZums6
6o/shO+kqoEtczu91mrk64NxpSof3vxRFdcqUEr4xrLJXx+oocnYmhwUVxU38s1r
Q4UfHe0nV7YBYmUDE3IJRRZY1aUdaKHmI9iok6e2csCfwMwEYRYOkekFoQKBgCyy
Oa1gpfA+Hw+N7i64ShRv1FyURi2Agb8uB9+4vbiG0rbpeZIioh5KnQ19P4+DKH/l
zinTBwXiWWpDXH4lJuPOp5ierbFBQ38ibDkg8dLrTG9zK7ZypFpWRmEI6GNYReLv
TEs/J6HDxFOC8Q8ow4COUUwmbCOY90lQXAiRK1b1AoGAScK1db9X5Ct+JtnjYyzi
GYLwyPhHCQIVmEDfuvbMs74woQsrBm3dBB3b2vf9pqcSZB4WAbTQ3wCdxCJKg6MN
6eXBO1M1lcIpZjCDyiXXi7PozNmkasRDUyDFwxlnyDLdRHL3FD+dh6do32FwGLBQ
u256RIyMB13qIuqrc91Z0hM=
-----END PRIVATE KEY-----
2Given merchant wants to create a request with header:
Content-type: application/json
X-TIMESTAMP: 2023-07-31T07:10:00+07:00
X-CLIENT-KEY: G1234325-SNAP
3X-SIGNATURE value for encryption will be:
G1234325-SNAP|2023-07-31T07:10:00+07:00

*same value as X-CLIENT-KEY + “|” + X-TIMESTAMP
4By using merchant’s private key to encrypt the value with SHA256withRSA algorithm, merchant will generate
iv5YorKVVFOFS59l0HChDvPe+HeoE/jY5CfVgCg5i16nj5/DVnKg49ilkv8PyeU7y38apHhgO+cUrvkfUs5BhDD69yLn7xp8hzN9RcR0UDy5+nCrQ3GGCVSzZJvlEXN+TB0j4Q6pMsjrq4+YRoyDa4mTlsjJTU9aGjLRFBYgY4MyMQ5x11JyLnoFwbS8TJ5e/q4mUozrp49VyHe7OQSFnJNwvMSFrDyAIxoOK0IZQIlY29PaDIFcWoR+RJAY42H2FryjJcPpNfeercbkj9jsBLV3wmEKiNoN6lgFFLk5QicDnAAQzL45s92EYeCqDNuTTJOydaJbUqEo1d/ZPVNBtQ==
5Final result of merchant's header will be:
Content-type: application/json
X-TIMESTAMP: 2023-07-31T07:10:00+07:00
X-CLIENT-KEY: G1234325-SNAP
X-SIGNATURE: <SIGNATURE SAMPLE RESULT>

Sample code

import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;

public class SignatureGenerator {

    public static void main(String[] args) throws Exception {
        // Generate key pairs
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(2048); // Adjust the key size as needed

        // Get the private and public keys
        // Insert private key content here
        PrivateKey privateKey = generatePrivateKey("-----BEGIN PRIVATE KEY-----MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCVZu2WHd4z5tbefkAWPjbB10Lgvu1CpsuIIVtO/wrrvKeD2LArmYSf5QxeJGG8HYlVYVp8I8/EVqayJUbcyOd4aMLQFiCambDipecCfI7ecJ0K4+2ZneWOEq9JM91ZnGtwuLVdr1X/4/NvyGuNvmLAyfpJkU14yZliAUqJJ656jpM+WfY+hOpcLOzUe2wzx5RV7wYCyMl7/d1iX33afJc6kbbUaA27yjlPdY6m0+DoZ0NNaRNZS7jzfJFtVXLTg/PgvwMNlMO/5ZywBWIOfWsD+MHUC6P9QYrHai8ZwEf9r+GivlNsAFs4NTpw9MdPngrexJm29k1Xh4UMHV//jB1/AgMBAAECggEADcXGyitns/4oOauGyeYjUxw+eIxxP88zfRGiIralMZUbFi7wEpzc2oaZbMZK0jYg1mOanU4J1a4tQMfp7+l/WRzDNL6Nc+MOKN6lXJfR7dSQzZO0cBBbvIyhZwymb5/ZUbNdWM0UjvnbE6d0rsTpwp77+TMxYpynDJ9U2S70ySxefGi7x4rK02/8xmj1fY/Ls+uoYhF4IbGJIkFvrlAdWyFvv6sCGBYxl5sPuitgQxPvYc7dS7HE+6nZ+wWgoIXjC4CfxHBa9u3dfu3XAnPY3xpnVsbxCAH9OOIrZPY0C/Pqgj+EElCBChUzDiedV3XipKUweDA+64sNm5NjqlqU8QKBgQDFf5a9vHVf9r2CTOgmz5wTrK7aTwKwCNC4zpZUxDR8k7AV2gTelabpPWqUMZyRMUPs2hyBvhEWVNKxx3qs7BAVpmcttuS5/dLNVdhNPUC2IgZqf3jWH2aS3lkci8DATQCphrEXZ3jrNaIRtrSCkflE+yY8YP8b7ObktgvHz39JMQKBgQDBqCsMmOsQAveIQk5FGAMxsFU9G7OIBFHoxpwkKUCjoqGxM+YQqDvcVeXNd/HdLnmR05//eoONemuDyYy8XRoud4tpaRKU/FK706WdljztB5rn2+cdUbmcU0fZiRQk+WuXsopZn3GUNvA/fQOW9qs7B5Rm5W71FeGkZgsvlAUlrwKBgQCUgFFaLWCcXa01UpqkxCp5aLi5EfvVXWuD6mKDLlzA51PZums66o/shO+kqoEtczu91mrk64NxpSof3vxRFdcqUEr4xrLJXx+oocnYmhwUVxU38s1rQ4UfHe0nV7YBYmUDE3IJRRZY1aUdaKHmI9iok6e2csCfwMwEYRYOkekFoQKBgCyyOa1gpfA+Hw+N7i64ShRv1FyURi2Agb8uB9+4vbiG0rbpeZIioh5KnQ19P4+DKH/lzinTBwXiWWpDXH4lJuPOp5ierbFBQ38ibDkg8dLrTG9zK7ZypFpWRmEI6GNYReLvTEs/J6HDxFOC8Q8ow4COUUwmbCOY90lQXAiRK1b1AoGAScK1db9X5Ct+JtnjYyziGYLwyPhHCQIVmEDfuvbMs74woQsrBm3dBB3b2vf9pqcSZB4WAbTQ3wCdxCJKg6MN6eXBO1M1lcIpZjCDyiXXi7PozNmkasRDUyDFwxlnyDLdRHL3FD+dh6do32FwGLBQu256RIyMB13qIuqrc91Z0hM=-----END PRIVATE KEY-----");

        // Create a signature instance with SHA-256 with RSA algorithm
        Signature signature = Signature.getInstance("SHA256withRSA");

        // Initialize the signature instance with the private key
        signature.initSign(privateKey);

        // Data to be signed
        // Insert your payload here
        String data = "G1234325-SNAP|2023-07-31T07:10:00+07:00";

        // Update the data to be signed
        signature.update(data.getBytes());

        // Generate the signature
        byte[] digitalSignature = signature.sign();
        
        String finalSignature = Base64.getEncoder().encodeToString(digitalSignature);

        System.out.println("Original data: " + data);
        System.out.println("Digital signature: " + finalSignature);
    }
    
    private static PrivateKey generatePrivateKey(String privateKey) throws Exception {
        String privateKeyString = privateKey
                            .replace("-----BEGIN PRIVATE KEY-----", "")
                            .replace("-----END PRIVATE KEY-----", "")
                            .replaceAll("\\s", "");

        // Decode Base64 encoded private key
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyString);

        // Generate PrivateKey object from byte array
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        return keyFactory.generatePrivate(keySpec);
    }
}

Payouts API Specification

Payouts API header consists of the following fields:

Field NameField TypeMandatoryField Description
Content-typeStringMMedia type of the resource, i.e. application/json
X-TIMESTAMPStringMClient’s current local time in ISO-8601 format
X-SIGNATUREStringMCreated using symmetric signature HMAC_SHA512 algorithm
AuthorizationStringMRepresents access_token of a request, received from Access Token API response
X-PARTNER-IDStringMMerchant’s partner ID
X-EXTERNAL-IDStringMMerchant’s unique ID per transaction request
CHANNEL-IDStringMChannel id Device identification on which the API services are currently being accessed by the end user. Given by BI
Content-type:application/json
X-TIMESTAMP:2020-01-01T00:00:00+07:00
X-SIGNATURE: da1fa417c72d6b91c257e01e54fac824
Authorization: Bearer gp9HjjEj813Y9JGoqwOeOPWbnt4CupvIJbU1Mmu4a11MNDZ7Sg5u9a
X-PARTNER-ID: BMRI
X-EXTERNAL-ID:12345678901234567890
CHANNEL-ID:12345

Symmetric Signature HMAC_SHA512

Based on the specification above, merchant should use symmetric signature HMAC_SHA512 for Transactional API.

Merchant should use this formula to create X-SIGNATURE for Transactional API:
HTTPMethod +”:“+ EndpointUrl +":"+ AccessToken +":“+ Lowercase(HexEncode(SHA-256(minify(RequestBody))))+ ":“ + TimeStamp encrypted with merchant’s client secret by using HMAC_SHA512 algorithm.

For a better understanding, please refer to this sample scenario for generating signature using the formula above on Transactional API:

No.Steps
1Given merchant has client secret fdppqbF5wq7vVegyvsV1CROMv646nJ7A
2Given merchant wants to create a request with header for POST https://api.midtrans.com/v1.0/registration-account-binding
Content-type:application/json
X-TIMESTAMP:2020-01-01T00:00:00+07:00
Authorization: Bearer gp9HjjEj813Y9JGoqwOeOPWbnt4CupvIJbU1Mmu4a11MNDZ7Sg5u9a
X-PARTNER-ID: BMRI
X-DEVICE-ID: 0987ADCASA
X-EXTERNAL-ID:12345678901234567890
CHANNEL-ID:12345
3X-SIGNATURE value for encryption will be:
POST:/v1.0/debit/payment-host-to-host:gp9HjjEj813Y9JGoqwOeOPWbnt4CupvIJbU1Mmu4a11MNDZ7Sg5u9a:56fa5f4999ad8014de49d7898c1d1d53472569db8999de3c1b752a0dd181e98c:2020-01-01T00:00:00+07:00

*same value as HTTPMethod +”:“+ EndpointUrl +":"+ AccessToken +":“+ Lowercase(HexEncode(SHA-256(minify(RequestBody))))+ ":“ + TimeStamp
4By using merchant’s client secret to encrypt the value with HMAC_SHA512 algorithm, merchant will generate

FSlidRHe4ow9qppNifGVQNcdv67lBjgCiP0BHylh+IKXo4fs2rHaGsFNUY0N8t0rPlZs4iAHAOCDklnCccwuJw==
5Final result of merchant's header will be:
Content-type:application/json
X-TIMESTAMP:2020-01-01T00:00:00+07:00
Authorization: Bearer gp9HjjEj813Y9JGoqwOeOPWbnt4CupvIJbU1Mmu4a11MNDZ7Sg5u9a
X-PARTNER-ID: BMRI
X-DEVICE-ID: 0987ADCASA
X-EXTERNAL-ID:12345678901234567890
CHANNEL-ID:12345
X-SIGNATURE: FSlidRHe4ow9qppNifGVQNcdv67lBjgCiP0BHylh+IKXo4fs2rHaGsFNUY0N8t0rPlZs4iAHAOCDklnCccwuJw==
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class HmacSHA512Example {

    public static void main(String[] args) {
        try {
            
            // Insert client secret given by midtrans here
            String clientSecret = "fdppqbF5wq7vVegyvsV1CROMv646nJ7A";
            
            // Generate a secret key for HMAC-SHA512
            SecretKey secretKey = generateSecretKeyFromString(clientSecret);

            // Insert you payload here
            String payLoad = "POST:/v1.0/debit/payment-host-to-host:gp9HjjEj813Y9JGoqwOeOPWbnt4CupvIJbU1Mmu4a11MNDZ7Sg5u9a:56fa5f4999ad8014de49d7898c1d1d53472569db8999de3c1b752a0dd181e98c:2020-01-01T00:00:00+07:00";

            // Compute the HMAC SHA-512 hash
            String hmac = computeHmacSHA512(secretKey, payLoad);

            // Print the resulting hash
            System.out.println("HMAC SHA-512 signature: " + hmac);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static SecretKey generateSecretKeyFromString(String keyString) throws Exception {
        // Create a byte array from the string
        byte[] keyBytes = keyString.getBytes(StandardCharsets.UTF_8);

        // Create a SecretKeySpec from the byte array
        SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "HmacSHA512");

        return secretKeySpec;
    }

    private static String computeHmacSHA512(SecretKey secretKey, String message) throws Exception {
        // Get an HMAC-SHA512 Mac instance and initialize with the secret key
        Mac mac = Mac.getInstance("HmacSHA512");
        mac.init(secretKey);

        // Compute the HMAC on the input message bytes
        byte[] hmacBytes = mac.doFinal(message.getBytes());

        // Convert the HMAC bytes to a base64 encoded string
        return Base64.getEncoder().encodeToString(hmacBytes);
    }
}