Skip to main content

Message Signature

Introduction​

This documentation explains the RSA-256 message signature mechanism used for securing data transmitted via APIs. In this mechanism, digital signatures are created using RSA-256 keys to ensure the authenticity and integrity of the data being transmitted.

Prerequisites​

Before using the RSA-256 message signature mechanism, ensure you have the following prerequisites:

  • Knowledge of RSA-256 encryption and digital signatures.
  • Access to a public and private RSA-256 key pair.
  • Your API endpoint and data to be transmitted.
info

You can also find our message signature SDK or sample apps in the Resources section.

Generating RSA-256 Keys​

To use RSA-256 for message signatures, you need an RSA-256 key pair consisting of a private key (for signing) and a public key (for verification).

info

For more information on how to generate the keys and obtained our public keys, please refer to the Key Management section.

Signing a Message​

To sign a message using your private key, you need to append specific field values of the request message together and then sign the resulting string.

fieldA + fieldB + fieldC + fieldD

Verifying a Message Signature​

To verify a message signature using the recipient's public key, you need to recompute the data to be signed and then compare it to the received signature.

Using the API Headers​

To use the RSA-256 message signature mechanism for your API, follow these steps:

  • Sign the message
  • Include the signature in the "X-Signature" header of your API request.
  • Include the certificate serial number used to verify the signature in the "X-Signature-Key" header of your API request.
  • Example API request using cURL:
curl -X POST https://api.example.com/resource \
-H "Content-Type: application/json" \
-H "X-Signature: <signature>" \
-H "X-Signature-Key: <certificate_serial_number>" \
-d '{"field1": "value1", "field2": "value2", "field3": "value3"}'

Message Signature Fields​

DuitNow Online Banking/Wallets​

Retrieve Bank List​

Request Message​
DefinitionParameterField/Value
Originating Participant CodeHeaderclientId
Receiving Participant CodeHeader"RPPEMYKL"
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
End-to-End IdentificationBodymessageId
Merchant IdentificationBodyclientId
Response Message​
DefinitionParameterField/Value
Originating Participant CodeHeader"RPPEMYKL"
Receiving Participant CodeHeaderclientId
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
End-to-End IdentificationBodyendToEndId
Transaction StatusBodycode
Transaction Status ReasonBodyreason

Initiate Payment​

Request Message​
DefinitionParameterField/Value
Originating Participant CodeHeaderclientId
Receiving Participant CodeHeader"RPPEMYKL"
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
Debiting AgentBody"RPPEMYKL"
End-to-End IdentificationBodyendToEndId
Instructed AmountBodyamount
Crediting AgentBodyclientId
Response Message​
DefinitionParameterField/Value
Originating Participant CodeHeader"RPPEMYKL"
Receiving Participant CodeHeaderclientId
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
Original End-to-End IdentificationBodyendToEndId
Transaction StatusBodycode
Transaction Status ReasonBodyreason

Status Inquiry​

(also known as Transaction Enquiry)

Request Message​
DefinitionParameterField/Value
Originating Participant CodeHeaderclientId
Receiving Participant CodeHeader"RPPEMYKL"
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
Request IdentificationBodyendToEndId
Transaction IdentificationBodyendToEndId
Response Message​
DefinitionParameterField/Value
Originating Participant CodeHeader"RPPEMYKL"
Receiving Participant CodeHeaderclientId
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
Enquiry StatusBodycode
Enquiry Status ReasonBodyreason

Cancel Payment​

Request Message​
DefinitionParameterField/Value
Originating Participant CodeHeaderclientId
Receiving Participant CodeHeader"RPPEMYKL"
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
Original Transaction End-to-End IdentificationBodyendToEndId
New Transaction StatusBodystatus
Merchant IdentificationBodyclientId
Response Message​
DefinitionParameterField/Value
Originating Participant CodeHeader"RPPEMYKL"
Receiving Participant CodeHeaderclientId
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
Original Transaction End-to-End IdentificationBodyoriginalTransactionId
New Transaction StatusBodycode + reason
Merchant IdentificationBodyendToEndId

DuitNow AutoDebit​

DefinitionParameterField/Value
Originating Participant CodeHeaderclientId
Receiving Participant CodeHeader"RPPEMYKL"
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
Mandate Request IdentificationBodymandateId
Creditor NameBodymerchant.name
Debtor NameBodycustomer.name
Debiting AgentBody"RPPEMYKL"
Merchant IdentificationBodyclientId
DefinitionParameterField/Value
Originating Participant CodeHeader"RPPEMYKL"
Receiving Participiant CodeHeaderclientId
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
Original Mandate IdentificationBodymandateId
Status CodeBodycode
Status Reason CodeBodyreason
DefinitionParameterField/Value
Originating Participant CodeHeaderclientId
Receiving Participant CodeHeader"RPPEMYKL"
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
End-to-End IdentificationBodymessageId
DefinitionParameterField/Value
Originating Participant CodeHeader"RPPEMYKL"
Receiving Participiant CodeHeaderclientId
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
Original Mandate IdentificationBodyoriginalConsentId
Status CodeBodycode
Status Reason CodeBodyreason
DefinitionParameterField/Value
Originating Participant CodeHeaderclientId
Receiving Participant CodeHeader"RPPEMYKL"
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
Request IdentificationBodymandateId
Transaction IdentificationBodymandateId
DefinitionParameterField/Value
Originating Participant CodeHeader"RPPEMYKL"
Receiving Participiant CodeHeaderclientId
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
Original Message IdentificationBodyoriginalTransactionId
Enquiry StatusBodycode
Enquiry Status ReasonBodyreason
DefinitionParameterField/Value
Originating Participant CodeHeaderclientId
Receiving Participant CodeHeader"RPPEMYKL"
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
Original Transaction End-to-End IdentificationBodymandateId
New Transaction StatusBodystatus
Merchant IdentificationBodyclientId
DefinitionParameterField/Value
Originating Participant CodeHeader"RPPEMYKL"
Receiving Participiant CodeHeaderclientId
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
Original Message IdentificationBodyoriginalTransactionId
Status CodeBodycode
Status Reason CodeBodyreason
End-to-End Identification of Modified RequestBodymandateId
DefinitionParameterField/Value
Originating Participant CodeHeaderclientId
Receiving Participant CodeHeader"RPPEMYKL"
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
Cancellation ReasonBodyreason
Consent IdentificationBodyconsentId
DefinitionParameterField/Value
Originating Participant CodeHeader"RPPEMYKL"
Receiving Participiant CodeHeaderclientId
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
Original Mandate IdentificationBodyoriginalConsentId
Status CodeBodycode
Status Reason CodeBodyreason

Real-Time Debit​

Request Message​
DefinitionParameterField/Value
Originating Participant CodeHeaderclientId
Receiving Participant CodeHeader"RPPEMYKL"
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
End-to-End IdentificationBodymessageId
Interbank Settlement AmountBodyamount
Crediting AgentBodyclientId
Debtor NameBody"DD Debtor"
Debtor Account IdentificationBody"DD Debtor Account"
Debiting AgentBody"RPPEMYKL"
Response Message​
DefinitionParameterField/Value
Originating Participant CodeHeader"RPPEMYKL"
Receiving Participiant CodeHeaderclientId
Business Message IdentificationHeadermessageId
Message IdentificationBodytransactionId
Original End-to-End IdentificationBodyoriginalMessageId
Transaction StatusBodycode
Transaction Status ReasonBodyreason

Browser Redirection​

Request Message​
DefinitionParameterField/Value
Message Identification-MsgId

Status Notification​

Request Message​
DefinitionParameterField/Value
Message Identification-MsgId

End-to-End ID Signature Verification​

For redirect flows related to DuitNow Online Banking/Wallets and Duitnow AutoDebit, the End-to-End ID of the requests will be signed. This signature and the End-to-End ID of the original request will be passed from the merchant to banks. Banks can then verify the info is from a trusted source by verifying the signature

Sample Codes​

Message Signing​

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.stream.Collectors;

public class SampleCode {

private static final String KEY_ALGORITHM = "RSA";
private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";

public static void main(String[] args) throws Exception {
// path to private key and public certificate
String privateKeyPath = "path_to_private_key";
String publicKeyPath = "path_to_rpp_public_certificate";

// message to sign
String message = "message_to_sign";

// signature to verify
String responseSignature = "signature_to_verify";

// Request Signing (Use to construct Request Message X-Signature)
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(createPrivateKeyInstance(privateKeyPath));
signature.update(message.getBytes(StandardCharsets.UTF_8));
System.out.println(Base64.getEncoder().encodeToString(signature.sign()));


public static PrivateKey createPrivateKeyInstance(String pathToKey)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
try (
BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(pathToKey))
)
) {
String content = reader
.lines()
.filter(line -> !line.startsWith("-----"))
.collect(Collectors.joining());
KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(
Base64.getDecoder().decode(content)
);
return factory.generatePrivate(keySpec);
}
}

public static PublicKey createPublicKeyInstance(String pathToKey)
throws IOException, NoSuchAlgorithmException, CertificateException {
try (FileInputStream reader = new FileInputStream(pathToKey)) {
CertificateFactory f = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) f.generateCertificate(
reader
);
return certificate.getPublicKey();
}
}
}

Message Verification​

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.stream.Collectors;

public class SampleCode {

private static final String KEY_ALGORITHM = "RSA";
private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";

public static void main(String[] args) throws Exception {
// path to private key and public certificate
String privateKeyPath = "path_to_private_key";
String publicKeyPath = "path_to_rpp_public_certificate";

// message to sign
String message = "message_to_sign";

// signature to verify
String responseSignature = "signature_to_verify";

// Response Verification (Use to verify Response Message X-Signature)
PublicKey publicKey = createPublicKeyInstance(publicKeyPath);
Signature signatureResponse = Signature.getInstance(SIGNATURE_ALGORITHM);
signatureResponse.initVerify(publicKey);
signatureResponse.update(message.getBytes(StandardCharsets.UTF_8));
boolean flag = signatureResponse.verify(
Base64.getDecoder().decode(responseSignature)
);
if (flag) {
System.out.println("verified successfully");
}
}

public static PrivateKey createPrivateKeyInstance(String pathToKey)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
try (
BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(pathToKey))
)
) {
String content = reader
.lines()
.filter(line -> !line.startsWith("-----"))
.collect(Collectors.joining());
KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(
Base64.getDecoder().decode(content)
);
return factory.generatePrivate(keySpec);
}
}

public static PublicKey createPublicKeyInstance(String pathToKey)
throws IOException, NoSuchAlgorithmException, CertificateException {
try (FileInputStream reader = new FileInputStream(pathToKey)) {
CertificateFactory f = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) f.generateCertificate(
reader
);
return certificate.getPublicKey();
}
}
}