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. Please refer to PKI Management on how to generate them.
  • Your API endpoint and data to be transmitted. Please refer to the API Reference for available APIs for Duitnow QR.
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 to obtain our public keys, please refer to the Key Management section. Note for Certification Center (CC) stage, participants are required to use the private and public keys provided by Paynet.

Message Signature Fields

Signatures for Duitnow QR is generated using certain field values in the request/response body. Which fields is used is determined by the message type, which is defined in the table below.

Request TypeRequest Signature FieldsResponse Signature Fields
QR Enquiry / Paymentpacs.008.001.06.01pacs.002.001.08.01
Transaction Enquirycamt.005.001.08camt.006.001.08
Webhook QR Enquirypacs.008.001.06.01pacs.002.001.08.01

Please refer to the API Reference for further information regarding on the API requirements, and the Message Signature Fields Reference section for details on the signature fields.

Signing a Message

info

Participant are required to generate message signature when initiating an API request, and when responding to a webhook API call from Paynet.

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

For example, in QR Enquiry request , which uses pacs.008.001.06.01 message fields, The steps are as follows :

1. Append all required fields into a single string, without spaces.

Required fields for pacs.008.001.06.01 :

End to End ID + Interbank Settlement Amount + Crediting Agent + Creditor Account ID

Example values of the fields :

20240603BICCODE1520OQR96975706 + 1.00 + 111222 + 99999999999

Example of the final message string :

20240125BICCODE1520OQR276376851.0012233399999999999

2. Sign the message string using RSA-256 encryption algorithm and your private key. Please refer to the Sample Code section for examples of code implementation.

7948b612e519ea4d5837576ce681d37e83fcb726977e2e7434b65fac10feb6e556864b23acdd10e543b879aa6fa3a7aaf33efc4c0e0526a30c
8c5cd33dba25cfe51c7b3bd8ca9e755590aa54a0aba1b893301686d42bf2ba49be62096785365fd1d90bdc1f9a32714b211e1510dc0b9d69962
3223beeb3655083d6a496e2fdaa8aed2789dc77074f2cec0cf7615d1011b4c13f5e85b1c83a2b13c971d4b5e7dfca4a3e929903feae987557
8a08742504b132c22c48fbdaff38dc82eb71f45eada9d9d777c60f74925fcfbdf3e088af0c6db6159ba36c1af68dea7925327e0572d8ddb5a7
4fe3866adf5c38102c3e48adcbca40a301fa45493a7f5f46e07bdfb1

3. Encode the signed message in Base64 format.

eUi2EuUZ6k1YN1ds5oHTfoP8tyaXfi50NLZfrBD+tuVWhksjrN0Q5UO4eapvo6eq8z78TA4FJqMMjFzTPbolz+UcezvYyp51VZCqVKCrobiTMBaG1Cv
yukm+YglnhTZf0dkL3B+aMnFLIR4VENwLnWmWIyI77rNlUIPWpJbi/aqK7SeJ3HcHTyzsDPdhXRARtME/XoWxyDorE8lx1LXn38pKPpKZA/6umHVXigh
0JQSxMsIsSPva/zjcgutx9F6tqdnXd8YPdJJfz73z4IivDG22FZujbBr2jep5JTJ+BXLY3bWnT+OGat9cOBAsPkity8pAowH6RUk6f19G4HvfsQ==

4. Set the encoded message into the BusMsg.AppHdr.RPPSgntr.Signature field. You are required to also set your certificate serial number into the BusMsg.AppHdr.RPPSgntr.KeyNbr field.

{
"BusMsg": {
"AppHdr": {
...
"RPPSgntr": {
"KeyNbr": "3213fff52677126775b92079b9ebb8a0f5852c99",
"Signature": "eUi2EuUZ6k1YN1ds5oHTfoP8tyaXfi50NLZfrBD+tuVWhksjrN0Q5UO4eapvo6eq8z78TA4FJqMMjFzTPbolz+UcezvYyp51VZCqVKCrobiTMBaG1Cvyukm+YglnhTZf0dkL3B+aMnFLIR4VENwLnWmWIyI77rNlUIPWpJbi/aqK7SeJ3HcHTyzsDPdhXRARtME/XoWxyDorE8lx1LXn38pKPpKZA/6umHVXigh0JQSxMsIsSPva/zjcgutx9F6tqdnXd8YPdJJfz73z4IivDG22FZujbBr2jep5JTJ+BXLY3bWnT+OGat9cOBAsPkity8pAowH6RUk6f19G4HvfsQ=="
}
...
},
...
}
}

Verifying a Message Signature

info

Participant should verify the response message from Paynet after making an API call to Paynet, and when receiving a webhook API call from Paynet.

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.

For example, in QR Enquiry response message, which uses pacs.002.001.08.01 message type, The steps are as follows :

1. Obtain the fields to be used to verify from the response message if you are an Issuer/OFI , or the request message if you are an Acquirer/RFI. For pacs.002.001.08.01 , the fields would be as follows :

Message ID + Original End-to-End ID + Transaction Status + Transaction Status Reason

2. Append the fields into a single string, without spaces.

Example values of the fields :

20240604BICCODE152045383744 + 20240604PICAMYK1520OQR45383744 + RJCT + U170

Final message string to be verified against the message signature :

20240604PICAMYK15204538374420240604PICAMYK1520OQR45383744RJCTU170

3. Obtain the signature from the BusMsg.AppHdr.RPPSgntr field of the request/response message.

L4PFU81GhOxjb023PAIUy0lTkF1sSpdSrrrENvQwhMwxbhJkabJIJgsFBpTGD7Y1MTa+JEWgnBYWhbRsqCgXqyZB+wssYTFwJJ6iiHD9g9R7k9oPwujuOFvXmbEBZ/OvRGpfg7bvgYGyyy3owJURS+N4cyeCXLnTVPVPrwyp4L9+i7QinyLfmwhhofjazuOtZiMBeAmYl9L668FgsjF2vV/z7JBaji10XoSKNYYzJzykzHuxl/VCRqyCqMUYGBDgI2x8vW0gV9EjNPcXWKK6CUaQlHZ8t4BOh6yXOAzOd4rr1ks0KcI8P4zVeOK3WgkEog4XKLzrS21QO+Ms4lOFJw==

4. Decode the signature from Base64 format.

2f83c553cd4684ec636f4db73c0214cb4953905d6c4a9752aebac436f43084cc316e126469b248260b050694c60fb6353136be2445a09c161685b46ca82817ab2641fb0b2c613170249ea28870fd83d47b93da0fc2e8ee385bd799b10167f3af446a5f83b6ef8181b2cb2de8c095114be3787327825cb9d354f54faf0ca9e0bf7e8bb4229f22df9b0861a1f8dacee3ad66230178099897d2faebc160b23176bd5ff3ec905a8e2d745e848a358633273ca4cc7bb197f54246ac82a8c5181810e0236c7cbd6d2057d12334f71758a2ba09469094767cb7804e87ac97380cce778aebd64b3429c23c3f8cd578e2b75a0904a20e1728bceb4b6d503be32ce2538527

5. Verify the signature against the appended string in step 2, using the provided RPP public key. Refer to the Sample Code section for examples of code implementation. Please use RSA-256 algorithm to verify the message.

6. You may also verify the serial number in BusMsg.AppHdr.RPPSgntr.KeyNbr field to be the same as the RPP public certificate serial number as provided by Paynet.

Message Signature Fields Reference ( DuitNow QR )

pacs.008.001.06 / pacs.008.001.06.01

JSON Field PathDescription
BusMsg/Document/FIToFICstmrCdtTrfInf/CdtTrfTxInf/PmtId/EndToEndIdEndtoEndId
BusMsg/Document/FIToFICstmrCdtTrfInf/CdtTrfTxInf/IntrBkSttlmAmtInterbank settlement amount
BusMsg/Document/FIToFICstmrCdtTrfInf/CdtTrfTxInf/CdtrAgt/FinInstnId/Othr/IdCrediting Agent
BusMsg/Document/FIToFICstmrCdtTrfInf/CdtTrfTxInf/CdtrAcct/Id/Othr/IdCreditor Account ID
XML Field PathDescription
/FIToFICstmrCdtTrfInf/CdtTrfTxInf/PmtId/EndToEndIdEndtoEndId
/FIToFICstmrCdtTrfInf/CdtTrfTxInf/IntrBkSttlmAmtInterbank settlement amount
/FIToFICstmrCdtTrfInf/CdtTrfTxInf/CdtrAgt/FinInstnId/Othr/IdCrediting Agent
/FIToFICstmrCdtTrfInf/CdtTrfTxInf/CdtrAcct/Id/Othr/IdCreditor Account ID

pacs.002.001.08 / pacs.002.001.08.01

JSON Field PathDescription
BusMsg/Document/FIToFIPmtStsRptInf/GrpHdr/MsgIdMessage ID
BusMsg/Document/FIToFIPmtStsRptInf/TxInfAndSts/OrgnlEndToEndIdOriginal End-to-End ID
BusMsg/Document/FIToFIPmtStsRptInf/TxInfAndSts/TxStsTransaction Status
BusMsg/Document/FIToFIPmtStsRptInf/TxInfAndSts/StsRsnInf/Rsn/PrtryTransaction Status Reason
XML Field PathDescription
/FIToFIPmtStsRptInf/GrpHdr/MsgIdMessage ID
/FIToFIPmtStsRptInf/TxInfAndSts/OrgnlEndToEndIdOriginal End-to-End ID
/FIToFIPmtStsRptInf/TxInfAndSts/TxStsTransaction Status
/FIToFIPmtStsRptInf/TxInfAndSts/StsRsnInf/Rsn/PrtryTransaction Status Reason

camt.005.001.08

JSON Field PathDescription
BusMsg/AppHdr/Fr/FIId/FinInstnId/Othr/IdFrom ID
BusMsg/AppHdr/To/FIId/FinInstnId/Othr/IdTo ID
BusMsg/AppHdr/BizMsgIdrBizMsg ID
BusMsg/Document/GetTx/MsgHdr/MsgIdMessage ID
BusMsg/Document/GetTx/MsgHdr/ReqTp/Prtry/IdRequest ID
BusMsg/Document/GetTx/TxQryDef/TxCrit/NewCrit/SchCrit/PmtSch/PmtId/TxIdTransaction ID
XML Field PathDescription
/GetTx/MsgHdr/MsgIdMessage ID
/GetTx/MsgHdr/ReqTp/Prtry/IdRequest ID
/GetTx/TxQryDef/TxCrit/NewCrit/SchCrit/PmtSch/PmtId/TxIdTransaction ID

camt.006.001.08

JSON Field PathDescriptionNote
BusMsg/AppHdr/Fr/FIId/FinInstnId/Othr/IdFrom ID
BusMsg/AppHdr/To/FIId/FinInstnId/Othr/IdTo ID
BusMsg/AppHdr/BizMsgIdrBizMsg ID
BusMsg/Document/RtrTx/MsgHdr/MsgIdMessage ID
BusMsg/Document/RtrTx/MsgHdr/OrgnlBizQry/MsgIdOriginal Message IDOptional field
BusMsg/Document/RtrTx/RptOrErr/BizRpt/TxsSummry/EnqSts/Cd/PrtryEnquiry Status
BusMsg/Document/RtrTx/RptOrErr/BizRpt/TxsSummry/EnqSts/Rsn/PrtryEnquiry Status Reason
XML Field PathDescription
/RtrTx/MsgHdr/MsgIdMessage ID
/RtrTx/MsgHdr/OrgnlBizQry/MsgIdOriginal Message ID
/RtrTx/RptOrErr/BizRpt/TxsSummry/EnqSts/Cd/PrtryEnquiry Status
/RtrTx/RptOrErr/BizRpt/TxsSummry/EnqSts/Rsn/PrtryEnquiry Status Reason

Message Signature Fields Reference ( Others )

head.001.001.01

Field PathDescription
/Fr/FIId/FinInstnId/Othr/IdFrom ID
/To/FIId/FinInstnId/Othr/IdTo ID
/BizMsgIdrBizMsg ID

admi.002.001.01

Field PathDescription
RltdRef/RefReference of original message
Rsn/RjctgPtyRsnReject Reason
Rsn/ RjctnDtTmRejection Date Time

admn.001.001.01

Field XML PathDescription
GrpHdr/MsgIdMessage ID
AdmnTxInf/FnctnCdFunction Code
AdmnTxInf/InstgAgt/FinInstnId/Othr/IdInstructing Agent

admn.002.001.01

Field XML PathDescription
GrpHdr/MsgIdMessage Id
AdmnResponse/InstgAgt/FinInstnId/Othr/IdInstructing Agent
AdmnResponse/OrgnlInstrIdOriginal Instructing Agent
AdmnResponse/FnctnCdFunction Code
AdmnResponse/TxStsTransaction Status

pacs.008.001.06.02

Field XML PathDescription
/FIToFICstmrCdtTrfCBFT/CdtTrfTxInf/PmtId/EndToEndIdEndtoEndId
/FIToFICstmrCdtTrfCBFT/CdtTrfTxInf/IntrBkSttlmAmtInterbank settlement amount
/FIToFICstmrCdtTrfCBFT/CdtTrfTxInf/CdtrAgt/FinInstnId/Othr/IdCrediting Agent
/FIToFICstmrCdtTrfCBFT/CdtTrfTxInf/CdtrAcct/Id/Othr/IdCreditor Account ID

pacs.002.001.08.02

Field XML PathDescription
/FIToFIPmtStsRptInf/GrpHdr/MsgIdMessage ID
/FIToFIPmtStsRptInf/TxInfAndSts/OrgnlEndToEndIdOriginal End-to-End ID
/FIToFIPmtStsRptInf/TxInfAndSts/TxStsTransaction Status
/FIToFIPmtStsRptInf/TxInfAndSts/StsRsnInf/Rsn/PrtryTransaction Status Reason

pacs.003.001.08.01

Field XML PathDescription
/FIToFICstmrDrctDbtInf/GrpHdr/MsgIdMessage ID
/FIToFICstmrDrctDbtInf/DrctDbtTxInf/PmtId/EndToEndIdOriginal End-to-End ID
/FIToFICstmrDrctDbtInf/DrctDbtTxInf/IntrBkSttlmAmtInterbankSettlement Amt
/FIToFICstmrDrctDbtInf/DrctDbtTxInf/CdtrAgt/FinInstnId/Othr/IdCrediting Agent
/FIToFICstmrDrctDbtInf/DrctDbtTxInf/Dbtr/NmDebtor Name
/FIToFICstmrDrctDbtInf/DrctDbtTxInf/DbtrAcct/Id/Othr/IdDebtor Account
/FIToFICstmrDrctDbtInf/DrctDbtTxInf/DbtrAgt/FinInstnId/Othr/IdDebiting Agent
/FIToFICstmrDrctDbtInf/DrctDbtTxInf/SplmtryData/Envlp/QRTxInfo/QRCdQR String

admi.004.001.02

Field XML PathDescription
/SysEvtNtfctn/EvtInf/EvtCdEvent Code
/SysEvtNtfctn/EvtInf/EvtTmEvent Time

admi.011.001.01

Field XML PathDescription
/SysEvtAck/MsgIdMessage ID
/SysEvtAck/AckDtls/EvtCdEvent Code

prxy.001.001.01

Field XML PathDescription
/PrxyRegn/GrpHdr/MsgIdMessage ID
/PrxyRegn/MsgSndr/Agt/FinInstnId/Othr/IdMessage Sender ID
/PrxyRegn/Regn/RegnTpRegistration Type
/PrxyRegn/Regn/Prxy/TpProxy Type
/PrxyRegn/Regn/Prxy/ValProxy Value
/PrxyRegn/Regn/PrxyRegn/Agt/FinInstnId/Othr/IdAgent Id
/PrxyRegn/Regn/PrxyRegn/Acct/Id/Othr/IdAccount ID
/PrxyRegn/Regn/PrxyRegn/Acct/Tp/PrtryAccount Type
/PrxyRegn/Regn/PrxyRegn/Acct/NmAccount Name

prxy.002.001.01

Field XML PathDescription
PrxyRegnRspn/GrpHdr/MsgIdMessage ID
PrxyRegnRspn/GrpHdr/MsgRcpt/Agt/FinInstnId/Other/IdMessage Recipient ID
PrxyRegnRspn/OrgnlGrpInf/OrgnlMsgIdOriginal Message ID
PrxyRegnRspn/RegnRspn/PrxRspnStsProxy Response Status
PrxyRegnRspn/RegnRspn/StsRsnInf/PrtryStatus Reason
PrxyRegnRspn/OrgnlRegnTpOriginal Registration Type
PrxyRegnRspn/PrxyRegn/RegnIdRegistration ID

prxy.003.001.01

Field XML PathDescription
PrxyLookUp/GrpHdr/MsgIdMessage ID
PrxyLookUp/GrpHdr/MsgSndr/Agt/FinInstnId/Othr/IdMessage Sender ID
PrxyLookUp/LookUp/PrxyOnly/LkUpTpLook Up Type
PrxyLookUp/LookUp/PrxyOnly/IdLookup Value
PrxyLookUp/LookUp/PrxyOnly/PrxyRtrvl/TpProxy Retrieval Type
PrxyLookUp/LookUp/PrxyOnly/PrxyRtrvl/ValProxy Retrieval Value

prxy.003.002.01.01

Field XML PathDescription
/PrxyLookUpCBFT/GrpHdr/MsgIdMsg ID
/PrxyLookUpCBFT/LookUp/PrxyOnly/LkUpTpType of Look Up
/PrxyLookUpCBFT/LookUp/PrxyOnly/PrxyRtrvl/TpProxy Type
/PrxyLookUpCBFT/LookUp/PrxyOnly/PrxyRtrvl/ValProxy Value

prxy.004.001.01

Field XML PathDescription
PrxyLookUpRspn/GrpHdr/MsgIdMessage ID
PrxyLookUpRspn/GrpHdr/MsgRcpt/Agt/FinInstnId/Othr/IdMessage Sender ID
PrxyLookUpRspn/OrgnlGrpInf/OrgnlMsgIdOriginal Message ID
PrxyLookUpRspn/LookUpRspn/RegnRspn/PrxRspnStsResponse Status
PrxyLookUpRspn/LookUpRspn/RegnRspn/StsRsnInf/PrtryReason Code

prxy.004.002.01.01

Field XML PathDescription
/PrxyLookUpRspnCBFT/OrgnlGrpInf/OrgnlMsgIdOriginal Message ID
/PrxyLookUpRspnCBFT/LkUpRspn/OrgnlPrxyRtrvl/TpOriginal Proxy Type
/PrxyLookUpRspnCBFT/LkUpRspn/OrgnlPrxyRtrvl/ValOriginal Proxy Value
/PrxyLookUpRspnCBFT/LkUpRspn/RegnRspn/PrxRspnStsTransaction Status
/PrxyLookUpRspnCBFT/LkUpRspn/RegnRspn/StsRsnInf/PrtryTransaction Status Reason

prxy.005.001.01

Field XML PathDescription
PrxyNqryReq/GrpHdr/MsgIdMessage ID
PrxyNqryReq/GrpHdr/MsgSndr/Agt/FinInstnId/Other/IdMessage Sender ID
PrxyNqryReq/Nqry/ScndId/TpSecondary ID Type
PrxyNqryReq/Nqry/ScndId/ IdSecondary ID Value

prxy.006.001.01

Field XML PathDescription
PrxyNqryRspn/GrpHdr/MsgIdMessage ID
PrxyNqryRspn/GrpHdr/MsgRcpt/Agt/FinInstnId/Other/IdMessage Recipient ID
PrxyNqryRspn/NqryRspn/PrxRspnStsStatus Code
PrxyNqryRspn/NqryRspn/StsRsnInf /PrtryReason Code

prxy.901.001.01

Field XML PathDescription
PrxyNtfctn/GrpHdr/MsgIdMessage ID
PrxyNtfctn/GrpHdr/MsgRcpt/Agt/FinInstnId/Othr/IdMessage Recipient ID
PrxyNtfctn/Ntfctn/OrgnlIdOriginal ID
PrxyNtfctn/Ntfctn/OrgnlPrxy/TpOriginal Proxy Type
PrxyNtfctn/Ntfctn/OrgnlPrxy/ValOriginal Proxy Value
PrxyNtfctn/Ntfctn/OrgnlAcct/RegnIdOriginal Registration ID
PrxyNtfctn/Ntfctn/OrgnlAcct/DsplNmOriginal Display Name
PrxyNtfctn/Ntfctn/OrgnlAcct/Agt/FinInstnId/Othr/IdOriginal Debiting Agent
PrxyNtfctn/Ntfctn/OrgnlAcct/Acct/Id/Othr/IdOriginal Account ID
PrxyNtfctn/Ntfctn/OrgnlAcct/Acct/Id/Othr/Tp/PrtryOriginal Account Type
PrxyNtfctn/Ntfctn/NewAcct/RegnIdNew Registration ID
PrxyNtfctn/Ntfctn/NewAcct/DsplNmNew Display Name
PrxyNtfctn/Ntfctn/NewAcct/Agt/FinInstnId/Othr/IdNew Debiting Agent

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