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.
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).
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.
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
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | clientId |
Receiving Participant Code | Header | "RPPEMYKL" |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
End-to-End Identification | Body | messageId |
Merchant Identification | Body | clientId |
Response Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | "RPPEMYKL" |
Receiving Participant Code | Header | clientId |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
End-to-End Identification | Body | endToEndId |
Transaction Status | Body | code |
Transaction Status Reason | Body | reason |
Initiate Payment
Request Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | clientId |
Receiving Participant Code | Header | "RPPEMYKL" |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
Debiting Agent | Body | "RPPEMYKL" |
End-to-End Identification | Body | endToEndId |
Instructed Amount | Body | amount |
Crediting Agent | Body | clientId |
Response Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | "RPPEMYKL" |
Receiving Participant Code | Header | clientId |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
Original End-to-End Identification | Body | endToEndId |
Transaction Status | Body | code |
Transaction Status Reason | Body | reason |
Status Inquiry
(also known as Transaction Enquiry)
Request Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | clientId |
Receiving Participant Code | Header | "RPPEMYKL" |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
Request Identification | Body | endToEndId |
Transaction Identification | Body | endToEndId |
Response Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | "RPPEMYKL" |
Receiving Participant Code | Header | clientId |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
Enquiry Status | Body | code |
Enquiry Status Reason | Body | reason |
Cancel Payment
Request Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | clientId |
Receiving Participant Code | Header | "RPPEMYKL" |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
Original Transaction End-to-End Identification | Body | endToEndId |
New Transaction Status | Body | status |
Merchant Identification | Body | clientId |
Response Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | "RPPEMYKL" |
Receiving Participant Code | Header | clientId |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
Original Transaction End-to-End Identification | Body | originalTransactionId |
New Transaction Status | Body | code + reason |
Merchant Identification | Body | endToEndId |
DuitNow AutoDebit
Initiate Consent
Request Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | clientId |
Receiving Participant Code | Header | "RPPEMYKL" |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
Mandate Request Identification | Body | mandateId |
Creditor Name | Body | merchant.name |
Debtor Name | Body | customer.name |
Debiting Agent | Body | "RPPEMYKL" |
Merchant Identification | Body | clientId |
Response Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | "RPPEMYKL" |
Receiving Participiant Code | Header | clientId |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
Original Mandate Identification | Body | mandateId |
Status Code | Body | code |
Status Reason Code | Body | reason |
Retrieve Consent Details
Request Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | clientId |
Receiving Participant Code | Header | "RPPEMYKL" |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
End-to-End Identification | Body | messageId |
Response Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | "RPPEMYKL" |
Receiving Participiant Code | Header | clientId |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
Original Mandate Identification | Body | originalConsentId |
Status Code | Body | code |
Status Reason Code | Body | reason |
Consent Status Inquiry
Request Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | clientId |
Receiving Participant Code | Header | "RPPEMYKL" |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
Request Identification | Body | mandateId |
Transaction Identification | Body | mandateId |
Response Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | "RPPEMYKL" |
Receiving Participiant Code | Header | clientId |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
Original Message Identification | Body | originalTransactionId |
Enquiry Status | Body | code |
Enquiry Status Reason | Body | reason |
Cancel Consent
Request Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | clientId |
Receiving Participant Code | Header | "RPPEMYKL" |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
Original Transaction End-to-End Identification | Body | mandateId |
New Transaction Status | Body | status |
Merchant Identification | Body | clientId |
Response Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | "RPPEMYKL" |
Receiving Participiant Code | Header | clientId |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
Original Message Identification | Body | originalTransactionId |
Status Code | Body | code |
Status Reason Code | Body | reason |
End-to-End Identification of Modified Request | Body | mandateId |
Terminate Consent
Request Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | clientId |
Receiving Participant Code | Header | "RPPEMYKL" |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
Cancellation Reason | Body | reason |
Consent Identification | Body | consentId |
Response Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | "RPPEMYKL" |
Receiving Participiant Code | Header | clientId |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
Original Mandate Identification | Body | originalConsentId |
Status Code | Body | code |
Status Reason Code | Body | reason |
Real-Time Debit
Request Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | clientId |
Receiving Participant Code | Header | "RPPEMYKL" |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
End-to-End Identification | Body | messageId |
Interbank Settlement Amount | Body | amount |
Crediting Agent | Body | clientId |
Debtor Name | Body | "DD Debtor" |
Debtor Account Identification | Body | "DD Debtor Account" |
Debiting Agent | Body | "RPPEMYKL" |
Response Message
Definition | Parameter | Field/Value |
---|
Originating Participant Code | Header | "RPPEMYKL" |
Receiving Participiant Code | Header | clientId |
Business Message Identification | Header | messageId |
Message Identification | Body | transactionId |
Original End-to-End Identification | Body | originalMessageId |
Transaction Status | Body | code |
Transaction Status Reason | Body | reason |
Browser Redirection
Request Message
Definition | Parameter | Field/Value |
---|
Message Identification | - | MsgId |
Status Notification
Request Message
Definition | Parameter | Field/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 {
String privateKeyPath = "path_to_private_key";
String publicKeyPath = "path_to_rpp_public_certificate";
String message = "message_to_sign";
String responseSignature = "signature_to_verify";
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();
}
}
}
from Crypto.PublicKey import RSA
from Crypto.Hash import SHA256
from Crypto.Signature import PKCS1_v1_5
from base64 import b64decode, b64encode
private_key = 'privateKey'
rpp_request_data = "requestData"
def sign():
key_bytes = bytes(private_key)
key_bytes = b64decode(key_bytes)
key = RSA.importKey(key_bytes)
hash_value = SHA256.new(bytes(rpp_request_data))
signer = PKCS1_v1_5.new(key)
signature = signer.sign(hash_value)
return b64encode(signature)
res = sign()
print (res)
$pvtKeyRes = openssl_pkey_get_private(file_get_contents('apigate.key'));
openssl_pkey_export($pvtKeyRes, $pvtKey);
echo $pvtKey . PHP_EOL;
$pubKeyRes = openssl_pkey_get_public(file_get_contents('apigate.pub'));
$dtlKey = openssl_pkey_get_details($pubKeyRes);
echo 'bits=[' . $dtlKey['bits'] . ']' . PHP_EOL;
$pubKey = $dtlKey['key'];
echo $pubKey . PHP_EOL;
$message = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
echo 'message := ' . $message . PHP_EOL;
$sigAlgo = 'sha256WithRSAEncryption';
$mdMethods = openssl_get_md_methods(true);
$found = false;
foreach ($mdMethods as $mdMethod) {
if ($sigAlgo === $mdMethod) {
$found = true;
break;
}
}
if ($found === false) {
throw new Exception("method=[$sigAlgo] not found");
}
$signatureBytes = base64_decode($signature);
$verify = openssl_verify($message, $signatureBytes, $pubKeyRes, $sigAlgo);
openssl_pkey_free($pubKeyRes);
if ($verify === -1) {
echo 'error while verifying' . PHP_EOL;
} elseif ($verify === 0) {
echo 'Wrong signature' . PHP_EOL;
} elseif ($verify === 1) {
echo 'Correct signature' . PHP_EOL;
} else {
throw new Exception('fail to verify message');
}
using System;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
namespace SignApp{
class Sign {
public static string SignData(byte[] data, string pkcs12File, string pkcs12Password)
{
X509Certificate2 signerCert = new X509Certificate2(pkcs12File, pkcs12Password, X509KeyStorageFlags.Exportable);
RSACryptoServiceProvider rsaCSP = new RSACryptoServiceProvider();
rsaCSP.FromXmlString(signerCert.PrivateKey.ToXmlString(true));
var SignedData = rsaCSP.SignData(data, CryptoConfig.MapNameToOID("SHA256"));
return Convert.ToBase64String(SignedData);
}
}
}
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 {
String privateKeyPath = "path_to_private_key";
String publicKeyPath = "path_to_rpp_public_certificate";
String message = "message_to_sign";
String responseSignature = "signature_to_verify";
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();
}
}
}
$pvtKeyRes = openssl_pkey_get_private(file_get_contents('apigate.key'));
openssl_pkey_export($pvtKeyRes, $pvtKey);
echo $pvtKey . PHP_EOL;
$pubKeyRes = openssl_pkey_get_public(file_get_contents('apigate.pub'));
$dtlKey = openssl_pkey_get_details($pubKeyRes);
echo 'bits=[' . $dtlKey['bits'] . ']' . PHP_EOL;
$pubKey = $dtlKey['key'];
echo $pubKey . PHP_EOL;
$message = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
echo 'message := ' . $message . PHP_EOL;
$sigAlgo = 'sha256WithRSAEncryption';
$mdMethods = openssl_get_md_methods(true);
$found = false;
foreach ($mdMethods as $mdMethod) {
if ($sigAlgo === $mdMethod) {
$found = true;
break;
}
}
if ($found === false) {
throw new Exception("method=[$sigAlgo] not found");
}
$signatureBytes = base64_decode($signature);
$verify = openssl_verify($message, $signatureBytes, $pubKeyRes, $sigAlgo);
openssl_pkey_free($pubKeyRes);
if ($verify === -1) {
echo 'error while verifying' . PHP_EOL;
} elseif ($verify === 0) {
echo 'Wrong signature' . PHP_EOL;
} elseif ($verify === 1) {
echo 'Correct signature' . PHP_EOL;
} else {
throw new Exception('fail to verify message');
}
using System;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
namespace SignApp{
class Sign {
public static bool VerifySignature(byte[] data, string signature, string publicCert)
{
X509Certificate2 partnerCert = new X509Certificate2(publicCert);
RSACryptoServiceProvider rsaCSP = (RSACryptoServiceProvider)partnerCert.PublicKey.Key;
return rsaCSP.VerifyData(data, CryptoConfig.MapNameToOID("SHA256"), Convert.FromBase64String(signature));
}
}
}