Generating the Signature
Generating the RSA Key Pair
Merchant must securely generate 2048 bit RSA Public Private Key pair on their servers inside a crypto vault. Merchant must share the Public Key (KeyFormat-PEM) with Juspay during onboarding
Private key must be securely kept in a crypto vault on the merchant servers. Private key should never flow to the client.
Note: To simplify integration on sandbox, we have already shared a set of auto-generated keys which need to be configured. Please make sure a new set of keys is generated for production prior to go-live
The below command would generate a private key file private-key.pem
$ openssl genrsa -out private-key.pem 2048
The below command would generate a public key file public-key.pem for the private key file generated via above command
$ openssl rsa -in private-key.pem -pubout -out public-key.pem
Signing the Payload
Algorithm | RSA-SHA256 |
Format | HEX; base 64 encoded |
JSON payload needs to be signed after converting it to String using the Private key stored on the merchant server. The signature shall be in Base 64 encoded format.
Note: Private key must be securely kept in a crypto vault on the merchant servers. Private key should never flow to the client.
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.json.JSONObject;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.HashMap;
import javax.xml.bind.DatatypeConverter;
public class SignatureUtil {
public static void main(String ...args) {
JSONObject data = new JSONObject("{'order_id': 'venkatesh12','first_name': 'Test','last_name': 'Customer','customer_phone': '9876543210','customer_email': '[email protected]','merchant_id': 'udit_juspay','amount': '1.00','customer_id': '9876543210','return_url': 'https://sandbox.juspay.in/end','currency': 'INR','mandate.start_date': '1638535683287','mandate.end_date': '2134731745451','timestamp': '1576227696'}");
String filePath = "/<absolute-path-to-folder-containing-pem-file>/private-key.pem";
HashMap<String,String> response = createSignature(data, filePath);
}
public static HashMap<String,String> createSignature(JSONObject payload, String filePath) {
try {
PrivateKey privateKey = readPrivateKeyFromFile(filePath);
Signature privateSignature = Signature.getInstance("SHA256withRSA");
String[] requiredFields = {"order_id", "merchant_id", "amount", "timestamp", "customer_id"};
for (String key : requiredFields)
if(!payload.has(key))
throw new Exception(key + " not found in payload");
String signaturePayload = payload.toString();
privateSignature.initSign(privateKey);
privateSignature.update(signaturePayload.getBytes(StandardCharsets.UTF_8));
byte[] signature = privateSignature.sign();
String encodedSignature = DatatypeConverter.printBase64Binary(signature);
HashMap<String,String> response = new HashMap<String,String>();
response.put("signature",encodedSignature);
response.put("signaturePayload", signaturePayload);
return response;
} catch (Exception e) {
e.printStackTrace();
}
return new HashMap<String, String>();
}
private static PrivateKey readPrivateKeyFromFile(String filePath) throws IOException {
Security.addProvider(new BouncyCastleProvider());
PEMParser pemParser = new PEMParser(new FileReader(filePath));
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
PEMKeyPair pemKeyPair = (PEMKeyPair) pemParser.readObject();
KeyPair keyPair = converter.getKeyPair(pemKeyPair);
return keyPair.getPrivate();
}
}
import sun.security.util.DerInputStream;
import sun.security.util.DerValue;
import javax.xml.bind.DatatypeConverter;
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.util.HashMap;
import org.json.JSONObject;
public class SignatureUtil {
//The method that signs the data using the private key that is stored in keyFile path
public static HashMap<String, String> createSignature (JSONObject payload, String keyContentOrFile) throws Exception {
Signature rsa = Signature.getInstance("SHA256withRSA");
PrivateKey privateKey = null;
if (keyContentOrFile.startsWith("--")) {
privateKey = readPrivateKey(keyContentOrFile);
} else {
privateKey = readPrivateKeyFromFile(keyContentOrFile);
}
String[] requiredFields = {"order_id", "merchant_id", "amount", "timestamp", "customer_id"};
for (String key : requiredFields)
if(!payload.has(key))
throw new Exception(key + " not found in payload");
String signaturePayload = payload.toString();
rsa.initSign(privateKey);
rsa.update(signaturePayload.getBytes());
byte[] sign = rsa.sign();
String encodedSignature = DatatypeConverter.printBase64Binary(sign);
HashMap<String,String> response = new HashMap<String,String>();
response.put("signature",encodedSignature);
response.put("signaturePayload", signaturePayload);
return response;
}
//Method to retrieve the Private Key from a file
public static PrivateKey readPrivateKeyFromFile(String filename) throws Exception {
File keyFile = new File(filename);
BufferedReader reader = new BufferedReader(new FileReader(keyFile));
String line;
StringBuffer fileContent = new StringBuffer();
Boolean isPkcs1Content = false;
while ((line = reader.readLine()) != null) {
if (!line.startsWith("--")) {
fileContent.append(line).append("\n");
} else if (!isPkcs1Content && line.startsWith("-----BEGIN RSA PRIVATE KEY-----")){
isPkcs1Content = true;
}
}
byte[] keyBytes = DatatypeConverter.parseBase64Binary(fileContent.toString());
return generatePrivateKey(keyBytes, isPkcs1Content);
}
//Method to retrieve the Private Key from a variable
public static PrivateKey readPrivateKey(String content) throws Exception {
Boolean isPkcs1Content = false;
if (content.startsWith("-----BEGIN RSA PRIVATE KEY-----")){
isPkcs1Content = true;
}
content = content.replaceAll("-----BEGIN RSA PRIVATE KEY-----","");
content = content.replaceAll("-----BEGIN PRIVATE KEY-----","");
content = content.replaceAll("-----END RSA PRIVATE KEY-----","");
content = content.replaceAll("-----END PRIVATE KEY-----","");
byte[] keyBytes = DatatypeConverter.parseBase64Binary(content);
return generatePrivateKey(keyBytes, isPkcs1Content);
}
private static PrivateKey generatePrivateKey(byte[] keyBytes, Boolean isPkcs1Content) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
PrivateKey privateKey = null;
if (isPkcs1Content) {
DerInputStream derReader = new DerInputStream(keyBytes);
DerValue[] seq = derReader.getSequence(0);
// skip version seq[0];
BigInteger modulus = seq[1].getBigInteger();
BigInteger publicExp = seq[2].getBigInteger();
BigInteger privateExp = seq[3].getBigInteger();
BigInteger prime1 = seq[4].getBigInteger();
BigInteger prime2 = seq[5].getBigInteger();
BigInteger exp1 = seq[6].getBigInteger();
BigInteger exp2 = seq[7].getBigInteger();
BigInteger crtCoef = seq[8].getBigInteger();
RSAPrivateCrtKeySpec keySpec =
new RSAPrivateCrtKeySpec(modulus, publicExp, privateExp, prime1, prime2, exp1, exp2, crtCoef);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
privateKey = keyFactory.generatePrivate(keySpec);
} else {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
privateKey = kf.generatePrivate(spec);
}
return privateKey;
}
public static void main(String[] args) throws Exception{
JSONObject data = new JSONObject("{'order_id': 'venkatesh12','first_name': 'Test','last_name': 'Customer','customer_phone': '9876543210','customer_email': '[email protected]','merchant_id': 'udit_juspay','amount': '1.00','customer_id': '9876543210','return_url': 'https://sandbox.juspay.in/end','currency': 'INR','mandate.start_date': '1638535683287','mandate.end_date': '2134731745451','timestamp': '312342'}");
//This is a sample snippet. Always store and fetch the private key from crypto vault
String fileContent = "-----BEGIN RSA PRIVATE KEY-----\r\n" +
"MIIEpAIBAAKCAQEAzEHPYKNYRcZoWtgTvYPEmTeiSlMxJwNPf8bRJ5U6cXiup2j/\r\n" +
"a/l1oAiuVNQiDRzvSflS80FoRjQjcGRyqTYcYYYUTYOTh6j1WY1SQuPqrsvGNR3L\r\n" +
"/6CfbI2iD4bPRx2XdPbxr6UoHNvh6y1hAscZy5oxSCT6WW5jCpW1Bg==\r\n" +
"-----END RSA PRIVATE KEY-----\r\n";
// Sign with content in code
HashMap<String,String> p1 = createSignature(data, fileContent);
// Sign with content in file on given absolute path
HashMap<String,String> p2 = createSignature(data, "/<absolute-path-to-folder-containing-pem-file>/private-key.pem");
}
}
#!/bin/python
# pip install pycryptodome
import json
from Crypto.PublicKey import RSA # ignore this
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from base64 import b64encode
privateKeyString = "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAA70vuNEyFLz2FzpPYzwN5...aX1rYhPBmS89Yt6pu6McT7jlnw\n-----END RSA PRIVATE KEY-----"
# This is a sample snippet. Always store and fetch the private key from crypto vault
privateKey = RSA.import_key(privateKeyString)
def createSignature (payload, privateKey):
requiredFields = ["order_id", "merchant_id", "amount", "timestamp", "customer_id"]
signaturePayload = json.dumps(payload)
for key in requiredFields:
if key not in payload:
raise ValueError (key , " not found in payload")
signer = PKCS1_v1_5.new(privateKey)
digest = SHA256.new()
digest.update(signaturePayload.encode('utf-8'))
signature = signer.sign(digest)
encodedSignature = b64encode(signature)
return encodedSignature, signaturePayload
const privateKeyString = "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAA70vuNEyFLz2FzpPYzwN5aX1rYhPBmS89Yt6pu6McT7jlnw\n-----END RSA PRIVATE KEY-----"
// This is a sample snippet. Always store and fetch the private key from crypto vault
privateKey = new NodeRSA(privateKeyString);
function createSignature (payload, privateKey) {
const requiredFields = ["order_id", "merchant_id", "amount", "timestamp", "customer_id"];
var objKeys = Object.keys(payload);
if (requiredFields.every(key => objKeys.includes(key))){
signaturePayload = JSON.stringify(payload);
signature = privateKey.sign(signaturePayload, "base64", "utf8");
return {signature, signaturePayload}
}
throw Error ("Not a valid JSON payload");
}
<?php
$privateKey = "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAA70vuNEyFLz2FzpPYzwN5aX1rYhPBmS89Yt6pu6McT7jlnw\n-----END RSA PRIVATE KEY-----";
// This is a sample snippet. Always store and fetch the private key from crypto vault
function createSignature($payload, $privateKey) {
$response = array();
$requiredFields = ["order_id", "merchant_id", "amount", "timestamp", "customer_id"];
foreach ($requiredFields as $key){
if(!array_key_exists($key, $payload)){
echo $key . " is not present ";
return;
}
}
$signaturePayload = json_encode($payload);
$signature = "";
openssl_sign($signaturePayload, $signature, $privateKey, "sha256WithRSAEncryption");
array_push($response, base64_encode($signature));
array_push($response,$signaturePayload);
return $response;
}
?>
require 'openssl'
require 'base64'
require 'json'
privateKeyString = "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAA70vuN....EyFLz2FzpPYzwN5aX1rYhPBmS89Yt6pu6McT7jlnw\n-----END RSA PRIVATE KEY-----"
privateKey = OpenSSL::PKey::RSA.new(privateKeyString)
# This is a sample snippet. Always store and fetch the private key from crypto vault
def createSignature (payload, privateKey)
requiredFields = ["order_id", "merchant_id", "amount", "timestamp", "customer_id"]
signaturePayload = JSON.generate(payload)
jsonPayload = JSON.parse(signaturePayload)
for key in requiredFields do
if !jsonPayload.has_key?(key)
raise (key + " not found in payload \n")
end
end
signature = privateKey.sign(OpenSSL::Digest::SHA256.new, signaturePayload)
encodedSignature = Base64.encode64(signature).delete!("\n")
return encodedSignature, signaturePayload
end
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.OpenSsl;
using Newtonsoft.Json.Linq;
class SignatureUtil {
// string filePath = "/<absolute-path-to-folder-containing-pem-file>/private-key.pem";
public static Dictionary<string,string> createSignature (JObject payload, string filePath){
string[] requiredFields = {"order_id", "merchant_id", "amount", "timestamp", "customer_id"};
foreach (string key in requiredFields)
if(!payload.ContainsKey(key))
throw new Exception (key + " not found in payload");
string signaturePayload = payload.ToString();
byte[] byteArrayPayload = ASCIIEncoding.ASCII.GetBytes(signaturePayload);
StreamReader sr = new StreamReader(filePath);
PemReader pr = new PemReader(sr);
AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)pr.ReadObject();
RsaKeyParameters privateKey = (RsaKeyParameters) keyPair.Private;
ISigner sign = SignerUtilities.GetSigner(PkcsObjectIdentifiers.Sha256WithRsaEncryption.Id);
sign.Init(true, privateKey);
sign.BlockUpdate(byteArrayPayload, 0, byteArrayPayload.Length);
byte[] signature = sign.GenerateSignature();
string encodeSignature = Convert.ToBase64String(signature);
Dictionary<string, string> response = new Dictionary<string, string>();
response.Add("signature", encodeSignature);
response.Add("signaturePayload", signaturePayload);
Console.Write(encodeSignature);
return response;
}
}
Updated 4 months ago