Hey! These docs are for version 3.2, which is no longer officially supported. Click here for the latest version, 1.0!

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. 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.

Sample Code Snippet
package com.newjuspay;

import sun.security.util.DerInputStream;
import sun.security.util.DerValue;

import javax.xml.bind.DatatypeConverter;
import java.io.*;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.net.URLDecoder;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;

/**
 * Created by azhar on 11/12/19.
 */
public class JuspayUtil {

    //The method that signs the data using the private key that is stored in keyFile path
    public static String sign(String data, String keyContentOrFile,Boolean urlEncodeSignature) throws Exception {
        Signature rsa = Signature.getInstance("SHA256withRSA");
        PrivateKey privateKey = null;
        data = URLDecoder.decode(data);

        if (keyContentOrFile.startsWith("--")) {
            privateKey = readPrivateKey(keyContentOrFile);
        } else {
            privateKey = readPrivateKeyFromFile(keyContentOrFile);
        }
        rsa.initSign(privateKey);
        rsa.update(data.getBytes());
        byte[] sign =  rsa.sign();
        if (urlEncodeSignature) {
            return URLEncoder.encode(DatatypeConverter.printBase64Binary(sign), "UTF-8");
        } else {
            return DatatypeConverter.printBase64Binary(sign);
        }
    }

    //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.toString());
        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{
        String data = "{\"merchant_id\":\"mid\",%20\"customer_id\":%20\"8600143648\",\"mobile_number\":%20\"8600143648\",\"email_address\":%20\"[email protected]\",\"time_stamp\":\"1576227696\"}";
        String fileContent = "-----BEGIN RSA PRIVATE KEY-----\r\n" + 
                "MIIEpAIBAAKCAQEAzEHPYKNYRcZoWtgTvYPEmTeiSlMxJwNPf8bRJ5U6cXiup2j/\r\n" + 
                "a/l1oAiuVNQiDRzvSflS80FoRjQjcGRyqTYcYYYUTYOTh6j1WY1SQuPqrsvGNR3L\r\n" + 
                "NG0owiXTheDd4S6NlK9zBXI1Gtf6Q/mGowfOje/W1i6AwDl1stPmplA7YleiHOUU\r\n" + 
                "X5XW3jKFChnLFcmD1hpvNrDbCYhy0xVHv2Pc0GhmFlavCTc2yPHYWwTyFYzY7PsX\r\n" + 
                "UVn3gSuL6w+aW7h7/1OkSYQzr6siLSqfGUfDIXqF2Oke4+PdhmbyqJ6vqNhH7C3e\r\n" + 
                "PuK8jGyaOsRWDgg1Sxc7fLFyA3CU7B00KIfcKwIDAQABAoIBAQCP8kkdh8Ar3Dmq\r\n" + 
                "2+B9jKE+gVCkJKDdJ54dJJY92RMI6M3dOUfYZkOY9sU1DxK4Pw38CfOFbzD3WMMo\r\n" + 
                "8AFGctXpfL5OKk4MKuxNoiS48zpu2TjkMg0E83Dn8hRxoxl7Gn24rTTYP7ZhJpg+\r\n" + 
                "01kpB9VvffjflIIz9cqWrnM7/gr7sBZjfNFFXmq2D9mvUrJVeyEWdJc5//2OuJKS\r\n" + 
                "tlDJmW6vrGM9K28Q8btyJcQOWl2IrIhICGMoSVdIJeRssd9mm1x4dVPaM315tQHe\r\n" + 
                "/CIFbENWI5jmutGfwb0lmiZJXayxEB5+4CPsYd+vxac+DSsbpZ3KJGWdrm+SFBsK\r\n" + 
                "WoM1Wq6hAoGBAP7MiOV/7Z9zJVVcwW3/R7BgW6T/fszqKfFdOcOCHq+IMH9gU/K/\r\n" + 
                "qwf5LTCOfeK4yQLg+uY/27D2IhIbRapp60BJ0PB2AT6lp5WnZHjO0llYknJsGVmW\r\n" + 
                "axMUi3+h+h1+UXfkb8yosY3PQcIdVp/zlJWT4qRQ5DWCPJLj6ah15V7JAoGBAM04\r\n" + 
                "SVsD+axQMvTRLs46B0V9JCLlr/fNol1Y8PhRNka3qsmUmE0eYwrAMoBsFJczHbDX\r\n" + 
                "JuViFTg5LKLemPi7lr3SY8LC2oCF0LZv8klyHxMD9ysw1wp5QKBHW+BxkFQMoqPT\r\n" + 
                "jxgzr78hh2/KN7qk1yR9RWAemTRjHlzU6/q7ZJlTAoGAf8lMju0N234AJ0ORrvzs\r\n" + 
                "k0SAMcwBZ/u4dVhv8+F6SkZfLEK/V2tQ93q8czHDaMWL6mmy627zW1jV1Ybf9YuR\r\n" + 
                "dHiQ1UgZb7Xcym1dMklnrW+CYuhb/lY19SfEnoo5yjjj2uEyQM4J3jknnZbhLu8W\r\n" + 
                "gb3dWNNI4J0ki/bJ5LbcBiECgYBcZAfaewqvwhd+0qJQ5B0b8sAPGftEBAciIYWz\r\n" + 
                "NdTKt6ujP0vmBFUwpODXolEO1Ut7rxFq2eKVwl/PH4odCU9PPGX/a/w1OomBaRra\r\n" + 
                "aA+HXxSrFDzsETTANAAwJtCEln+uY/ObQMHRucWg1ZiLZeUaH2/ZW008IZes0YtA\r\n" + 
                "AZfD0wKBgQCyVj0GyMwnHdF4p8M9TFYWb6BbRIeE7iUXqPvX6bzksylBPngpBF+J\r\n" + 
                "uEV0+VQiAjXwwzb8peuCNWZiyfpAn3v15GLPqOPURsL+HvIDwEBYerIh9p3Z2G38\r\n" + 
                "/6CfbI2iD4bPRx2XdPbxr6UoHNvh6y1hAscZy5oxSCT6WW5jCpW1Bg==\r\n" + 
                "-----END RSA PRIVATE KEY-----\r\n";

        // Sign with content in code
        String p1 = sign(data, fileContent, true);

        // Sign with content in file on given absolute path
        String p2 = sign(data, "D:\\eclipse-workspaces\\general-projects\\Juspay\\lib\\private-key.pem",true);

        System.out.println(p1.equals(p2));
        System.out.println(p1);
        System.out.println(p2);
    }
}
#!/bin/python
# pip install pycryptodome 

from Crypto.PublicKey import RSA # ignore this
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from Crypto import Random # ignore this
from base64 import b64encode

def sign(message, priv_key):
   signer = PKCS1_v1_5.new(priv_key)
   digest = SHA256.new()
   digest.update(message.encode('utf-8'))
   return signer.sign(digest)

def newkeys(keysize):         # This is a sample snippet. Always store and fetch the private key from crypto vault
   random_generator = Random.new().read
   key = RSA.generate(keysize, random_generator)
   private, public = key, key.publickey()
   return public, private

msg1 = "thisIsThePayloadToBeSigned"                       # Payload for signature
keysize = 2048
(public_key, private_key) = newkeys(keysize)

# Or use this to read the private key from a pem file
# private_key = RSA.importKey(open('pem file location', "rb").read(), passphrase="yourpasswordhere or None")
signature = sign(msg1, private_key)
encoded_signature = b64encode(signature)

print(public_key.exportKey('PEM'))
print(encoded_signature)
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");
        signaturePayload = "b64_" + Buffer.from(signaturePayload,'utf8').toString('base64');
        return {signature, signaturePayload}
    }
    throw Error ("Not a valid JSON payload");
}
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
​
import javax.xml.bind.DatatypeConverter;
import java.io.FileReader;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.*;
​
public class SignatureUtil {
   public static void main(String ...args) {
       String data = "{\"merchant_id\":\"mid\",%20\"customer_id\":%20\"8600143648\",\"mobile_number\":%20\"8600143648\",\"email_address\":%20\"[email protected]\",\"timestamp\":\"1576227696\"}";
       String filePath = "/<absolute-path-to-folder-containing-pem-file>/private-key.pem";
       String signature = sign(data, filePath, true);
       System.out.println(signature);
   }
​
   public static String sign(String payload, String filePath, boolean urlEncodeSignature) {
       try {
           PrivateKey privateKey = readPrivateKeyFromFile(filePath);
           Signature privateSignature = Signature.getInstance("SHA256withRSA");
           privateSignature.initSign(privateKey);
           privateSignature.update(payload.getBytes(StandardCharsets.UTF_8));
           byte[] signature = privateSignature.sign();
           if (urlEncodeSignature) {
               return URLEncoder.encode(DatatypeConverter.printBase64Binary(signature), "UTF-8");
           } else {
               return DatatypeConverter.printBase64Binary(signature);
           }
       } catch (IOException | NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
           e.printStackTrace();
       }
       return "sign-failed";
   }
​
   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();
   }
}
<?php
     function generateSign($payload) {
         $privateKey = "<private-key>";
         $signature = "";
         openssl_sign($payload , $signature , $privateKey , "sha256WithRSAEncryption");
         return base64_encode($signature);
     }
 ?>
   
Sample code snippet for reference:

<?php
    function generateSign($payload) {
        $privateKey = "MIIEpQIBAAKCAQEA2R+5+b1HZSNNMotnr7Z/5By4NSTZW4dDMGDy2huOSLn1EF6v\n75sssdY5kRkFoihLIeNQA+yzsi0kzpz3rCCCmo1DJwgoqA48JVQwwBjZ9SHeC0nE\n66VODmMJJGNWe1quHWQb3otIzS+U+rtd1Alzo9up8u8e+FrecyjO6fBMZfd32iO7\nqPtExtA1XDtKMqoRbHMiAz940xA5+BLmJC+gp1IYsVce2KA5BW1laPxbku42aQR7\neZipSa3BYRY8m964Aj6vLj4kTeTbrc4OH7yatRdWbVbrwVWpg936g8Q3Qf3jQY+H\nMu76l1WeXK4GkPkA+oJXY6ag1XhhqtOrLw3rJwIDAQABAoIBAQCjy2thG4lgouD5\n4HC3/dU9IO1WKhZPFht5w6lxIJiWBLL7RnMzLrzo69NBwr6dNgh36CPU0hw9rhC2\nTXQKRfxA25BtQZpqLVLyVjDwuc6zPnljyqLjojDgaZXb/ZSgOihfw8XCfRDOubaJ\n8A84hmjWlEABJKMYeHSYK5Dsqnr37/Oj4OT2NWLaRx8Kk0HPuv/bxx3MHurIHRtG\n514UJZcOfN8Ti69/DoYbtb/Mpg/djXr5s47TafxPa8jyT2E8nWboPvYPDQcdu2CI\nE0mbrj2C0Ak7g20ZYzm9HCbJHVG+2rPSjVgXKW2ZgXoZflte+G5vRfDrZH+TZuo+\nja0pVlIBAoGBAP4ZEtdpRJp5kvj7bUIkXd5E6lrxd2M0VprfMhHk0i+Z1p1nF8St\nF6p9uIGuRIEthvdRZVy56fWCHXOfmquRJDHazQZVnnXcRaqhuMYdZoJ3i1KkmSL4\nX/SdBsB4rY4FQZWNtwKoSeDugQeJzf4bhyn0iZGbWPq+XO70MxXya8CfAoGBANq/\nzL6ul+G6i/nXrfzwUr83EtXh6Zoj51YBK4g3ZIIuWkWvbo9NguV3p9KmeRKMWwYO\nRHC7aVwpHdjzOyzmSFdmC+5dqVe6rkdl9AzxpKt0p0rOznmZUhDcdElCk0p6pC5R\nQDAt2PA4aR3kT+9z2dPV0IHsUGiouF/LtmTmdCB5AoGAIShUdRefhCjpLORiVYc5\nWI/VpRhtY9yokH0fo4Ygh2Wjw9Z4G4oa1HyjXwjGl7TBL/THLVp1VTwta7EgFdNS\nzc6ngnQZwXeE/8cqvW+IuO2wmJAyC4Ytv1XeU69rtmSpMkLT5tzfByMYY0twPgCJ\nmsf2S7Hh4paEugnTwMFpnjECgYEAoTqc/i5RY97LLOr7ImM/mhBNobdRJnswFwPl\nwhCR1CG2B4a2Rokq4VbAK1LoCfPJYz1A1JZNoc/sX+tmwkE5MLHWOWpvVmoR6i4L\nIz83z+e7JjgnlxialDLowtZ/GXYrbLgWR2yDaQsq7w1InYUWGDyP4jL7USiKPJE5\nbkUtcoECgYEAwhbb1NxzteIr8zlytMj52sgeiJPQRjbU5CoMAJuiHvYHT8jQwso7\nlfbz+fXdQamU29v1Hdhc2JR1xWxrTz4bAt1l9lWK8zQBTK3SOlhyvrvNkKtTwjan\nsR6+uwB9KY5mrF++pRA8IL2f0yhx2uqwDkX/Og6ZnFHJn3BvQM/DWPg=";
        $privateKey = openssl_get_privatekey("-----BEGIN RSA PRIVATE KEY-----\n".$privateKey."\n-----END RSA PRIVATE KEY-----"); //private key must be in this format
        $signature = "";
        openssl_sign( $payload , $signature , $privateKey , "sha256WithRSAEncryption" );
        return base64_encode($signature);
    }
    $data ="{\"merchant_id\":\"abc\",\"customer_id\":\"12345\",\"mobile_number\":\"9876543210\",\"email_address\":\"[email protected]\",\"first_name\":\"abc\",\"last_name\":\"def\",\"timestamp\":\"1616068636\"}";
    echo generateSign($data);
?>
require 'openssl'
require 'base64'

private_key_pem = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA2R+5+b1HZSNNMotnr7Z/5By4NSTZW4dDMGDy2huOSLn1EF6v\n75sssdY5kRkFoihLIeNQA+yzsi0kzpz3rCCCmo1DJwgoqA48JVQwwBjZ9SHeC0nE\n66VODmMJJGNWe1quHWQb3otIzS+U+rtd1Alzo9up8u8e+FrecyjO6fBMZfd32iO7\nqPtExtA1XDtKMqoRbHMiAz940xA5+BLmJC+gp1IYsVce2KA5BW1laPxbku42aQR7\neZipSa3BYRY8m964Aj6vLj4kTeTbrc4OH7yatRdWbVbrwVWpg936g8Q3Qf3jQY+H\nMu76l1WeXK4GkPkA+oJXY6ag1XhhqtOrLw3rJwIDAQABAoIBAQCjy2thG4lgouD5\n4HC3/dU9IO1WKhZPFht5w6lxIJiWBLL7RnMzLrzo69NBwr6dNgh36CPU0hw9rhC2\nTXQKRfxA25BtQZpqLVLyVjDwuc6zPnljyqLjojDgaZXb/ZSgOihfw8XCfRDOubaJ\n8A84hmjWlEABJKMYeHSYK5Dsqnr37/Oj4OT2NWLaRx8Kk0HPuv/bxx3MHurIHRtG\n514UJZcOfN8Ti69/DoYbtb/Mpg/djXr5s47TafxPa8jyT2E8nWboPvYPDQcdu2CI\nE0mbrj2C0Ak7g20ZYzm9HCbJHVG+2rPSjVgXKW2ZgXoZflte+G5vRfDrZH+TZuo+\nja0pVlIBAoGBAP4ZEtdpRJp5kvj7bUIkXd5E6lrxd2M0VprfMhHk0i+Z1p1nF8St\nF6p9uIGuRIEthvdRZVy56fWCHXOfmquRJDHazQZVnnXcRaqhuMYdZoJ3i1KkmSL4\nX/SdBsB4rY4FQZWNtwKoSeDugQeJzf4bhyn0iZGbWPq+XO70MxXya8CfAoGBANq/\nzL6ul+G6i/nXrfzwUr83EtXh6Zoj51YBK4g3ZIIuWkWvbo9NguV3p9KmeRKMWwYO\nRHC7aVwpHdjzOyzmSFdmC+5dqVe6rkdl9AzxpKt0p0rOznmZUhDcdElCk0p6pC5R\nQDAt2PA4aR3kT+9z2dPV0IHsUGiouF/LtmTmdCB5AoGAIShUdRefhCjpLORiVYc5\nWI/VpRhtY9yokH0fo4Ygh2Wjw9Z4G4oa1HyjXwjGl7TBL/THLVp1VTwta7EgFdNS\nzc6ngnQZwXeE/8cqvW+IuO2wmJAyC4Ytv1XeU69rtmSpMkLT5tzfByMYY0twPgCJ\nmsf2S7Hh4paEugnTwMFpnjECgYEAoTqc/i5RY97LLOr7ImM/mhBNobdRJnswFwPl\nwhCR1CG2B4a2Rokq4VbAK1LoCfPJYz1A1JZNoc/sX+tmwkE5MLHWOWpvVmoR6i4L\nIz83z+e7JjgnlxialDLowtZ/GXYrbLgWR2yDaQsq7w1InYUWGDyP4jL7USiKPJE5\nbkUtcoECgYEAwhbb1NxzteIr8zlytMj52sgeiJPQRjbU5CoMAJuiHvYHT8jQwso7\nlfbz+fXdQamU29v1Hdhc2JR1xWxrTz4bAt1l9lWK8zQBTK3SOlhyvrvNkKtTwjan\nsR6+uwB9KY5mrF++pRA8IL2f0yhx2uqwDkX/Og6ZnFHJn3BvQM/DWPg=\n-----END RSA PRIVATE KEY-----"
private_key = OpenSSL::PKey::RSA.new(private_key_pem)

payload = '{"timestamp":"1631085704","merchant_id":"random","mobile_number":"9873333333","customer_id":"1324029","email_address":"[email protected]"}'

signature = private_key.sign(OpenSSL::Digest::SHA256.new, payload)

print Base64.encode64(signature).delete!("\n")
print "\n"