Guides & Tutorials
Jenga API
Generate Signature

Why signatures?

To ensure the security of your transactions or requests, we have implemented security controls that ensure transactions can only be initiated by you and no one else. To achieve this, we use message signatures.

How it works

All requests, other than the Identity API, will be required to have a signature in the header. The structure of the header will be as follows:

HTTP: Signature Header
Signature: {{signature_in_base64}}

To generate the signature, you will need create a key pair of private key and public key. You will share the public key with us and use the private key to generate the signature.

Creating private & public keys

Use following command in your preferred terminal to generate a keypair with a self-signed certificate.

In this command, we are using the openssl. You can use other tools e.g. keytool (ships with JDK - Java Developement Kit), keystore explorer (opens in a new tab) e.t.c

Generate Private Key
openssl genrsa -out privatekey.pem 2048

Once you are successful with the above command a file (privatekey.pem) will be created on your present directory, proceed to export the public key from the keypair generated. The command below shows how to do it.

Generate Public Key
openssl rsa -in privatekey.pem -outform PEM -pubout -out publickey.pem

If the above command is successful, a new file (publickey.pem) will be created on your present directory. Copy the contents of this file and add it on our jengaHQ portal. Make sure to copy only the contents of the keyblock and paste as is.

The Generated Key Files

The privatekey.pem file looks something like this:

Example Private Key
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA1BvVbYnuhGGmmIwUdUkFP+WG+tkXyf+o7DopD2MgDh+jwyvA
jwDbSENHOwRuIzYEPBePk1lcchTDraz6VbWbwnDWJNn6cQkDCozRvuN1JnYa88Yy
u7XFQyvskwpk2zgzJ3azuDYAZ0I4yBXAeamLXibOOjm9KFGrBhDMGUtQLVvayZTT
iyyJnDXh5bNISjZeWU1VEiksaMUYujrXmLKDIlFM8xlJJvmvijlwS23J9oP3co3u
Hhd14pGXHKOYXvyVt3Q1taFIps7zS2x2vsGCaK9cdHrExWQdF9fzN95QfagMp7f2
DSMQVhOsXTdZFXMOrkVtWOTlwUJucBGstKOjNwIDAQABAoIBADYvXhh7kgkTgSGb
N2a23rZyBkdyyhb6Tsb6HJ8nrXquLoGfXbOqflo5harX+OLZ278WLcFwpKMoFsz5
UYIvwLitZqdHYCkcKkC5tKNVLApFRaFc0n0NdHUydV8i2pz+AGNmeYbnlLbMPgEv
PVpXK5lDxI8vTNlN86i7Bci4aqULSLYQ9E4/yWOAEAkp9+O7lb6HKcYQ8SgpZ9d9
M0RmxP4Qgc7HdYGo8KzvFJHFtTxOmDMOjWShAxdk77QmnZAznmpmz4v4uUbwR8YK
P7oZV7Lvl1gfma/h+kYR5yd5kJtHSu2+Q2n8gLRfUeFGqD56d1O93VnhSJyhI/OR
zqNhZjECgYEA+zr273pOXDTHRbLVOrYDjxHY6DnD/OeV+qaiCOVFIjTdWAITMuvr
NwKn+Ez0nSBCmKiozdcOzzjxllRs7BhhNGwlQlJloJurfbGIbKb0lSFRQD0bXcxY
32lVscKotMP4lQmmTlALUxbPaCt/MmBsgXg7IA+lGe+I5evAOImE1tUCgYEA2CK6
uUtO6EUBYRC88oyXdlh5lsEM6GpsMHFIlHLeW2EmxTbaaWVf91LRg7lyL+UXEWQ6
zy/oZDuXZI3EVQN2Po2Pcs9V4GFa9FO8YODzdoaHos+f9pe841enZNHkSkXvWXKv
j9HjvVsoKGq3Lg9acLx9dMVmx8ilT1FvDcMz79sCgYBD4soJKgZ0mfpi1hESPU62
4T64eauA8l8vjMlqF/HXbWuGNYFUmDVF9xzGVp0evDHiqGh8vqkMy7lUQtnv7iKO
FM74neVCQe5UF53ipjae+ZLIBfsYHHjDXeY/E3ec6PuJ4kKjFLQKrrY60s4bIb0Q
OxnW7wNQ/84BOvQFEvvnRQKBgQDCwwjf0CzawNPtU9fv+SDDVBa88llfVgcH4A03
OAuG7JSzQiqurts7UzXZLVLoNdgDo/4alWEkcU6LHfS9ZtE2rPmGy67m8tOzN4GZ
CxxYwgGXhODwpOthMat1/m1pQHvebqolP02pZGtbgE5xAwTMcg3bG8byYKwWPZuF
G1HB4QKBgQC5xyHCNq2jQ9YAvja6oqohx59a9Y57R+Xb1Z42w64fouZcbysVWM1f
bfSXnsW49RLDH0Ynns8i5jb+LMdL6W7UujdqrgNMmcNF2GXxHqnYxD10SKRctio5
7gfs5SeS0jvcs7NLCRMhw/yol4pRg12HWcm/YsIpn/na/hUzHesJ+A==
-----END RSA PRIVATE KEY-----
Example Public Key
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1BvVbYnuhGGmmIwUdUkF
P+WG+tkXyf+o7DopD2MgDh+jwyvAjwDbSENHOwRuIzYEPBePk1lcchTDraz6VbWb
wnDWJNn6cQkDCozRvuN1JnYa88Yyu7XFQyvskwpk2zgzJ3azuDYAZ0I4yBXAeamL
XibOOjm9KFGrBhDMGUtQLVvayZTTiyyJnDXh5bNISjZeWU1VEiksaMUYujrXmLKD
IlFM8xlJJvmvijlwS23J9oP3co3uHhd14pGXHKOYXvyVt3Q1taFIps7zS2x2vsGC
aK9cdHrExWQdF9fzN95QfagMp7f2DSMQVhOsXTdZFXMOrkVtWOTlwUJucBGstKOj
NwIDAQAB
-----END PUBLIC KEY-----

Generating the signature

The first step to generating the signature is to prepare the data to be signed. This is done by concatenating (joining) particular values within the API request payload. Different APIs will have different formulae for concatenation and this is detailed in each API's documentation.

To illustrate this, we use the Opening and Closing Account Balance API (opens in a new tab).

The request payload looks like this:

{
  "countryCode": "KE",
  "accountId": "0011547896523",
  "date": "2018-08-09"
}

The instructions further say:

A SHA-256 signature to proof that this request is coming from the merchant. Build a String of concatenated values of the request fields with the following order: accountId, countryCode, date. The resulting text is then signed with Private Key and Base64 encoded.

We can see that the data to be signed is a concatenation of the values in the fields: accountId, countryCode and date and in that order and from our payload above, that would be: 0011547896523KE2018-08-09. This is the data we will sign.

Now onto signing. The mechanism of signing this data will depend on the language you are using. For instance, if you are using PHP or Python, you would go about it as follows;

<?php
$plainText  = "0011547896523KE2018-08-09"; // See separate instruction on how to create this concatenation
$privateKey = openssl_pkey_get_private(("file://path/to/privatekey.pem"));
$token      = "QNg9X7cLJSpZVOpaJJ33wX0AbcRF";
 
openssl_sign($plainText, $signature, $privateKey, OPENSSL_ALGO_SHA256);
from base64 import b64encode
 
from Crypto.Hash import SHA256
from Crypto.Signature import PKCS1_v1_5
from Crypto.PublicKey import RSA
 
message = "0011547896523KE2018-08-09".encode('utf-8') # See separate instruction on how to create this concatenation
digest = SHA256.new()
digest.update(message)
 
private_key = False
with open("privatekey.pem", "r") as myfile:
    private_key = RSA.importKey(myfile.read())
 
signer = PKCS1_v1_5.new(private_key)
sigBytes = signer.sign(digest)
signBase64 = b64encode(sigBytes)

Your generated signature is now in a variable $signature or signBase64 in the Python example.

You can now include it in your request as follows;

<?php
$plainText  = "0011547896523KE2018-08-09";
$privateKey = openssl_pkey_get_private(("file://path/to/privatekey.pem"));
$token      = "QNg9X7cLJSpZVOpaJJ33wX0AbcRF";
 
openssl_sign($plainText, $signature, $privateKey, OPENSSL_ALGO_SHA256);
 
 
$curl        = curl_init();
$data_string = '{
    "countryCode":"KE",
    "accountId":"0011547896523",
    "date":"2018-08-09"
    }';
 
curl_setopt_array($curl, array(
    CURLOPT_URL => "https://sandbox.jengahq.io/account-test/v2/accounts/accountbalance/query",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => "",
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_POSTFIELDS => $data_string,
    CURLOPT_HTTPHEADER => array(
        "Authorization: Bearer " . $token,
        "cache-control: no-cache",
        "Content-Type: application/json",
        "signature: " . base64_encode($signature)
    )
));
$result = curl_exec($curl);
$err    = curl_error($curl);
 
curl_close($curl);
 
if ($err) {
    echo "cURL Error #:" . $err;
} else {
    echo $result;
}
?>
import json
import requests
 
 
from base64 import b64encode
 
from Crypto.Hash import SHA256
from Crypto.Signature import PKCS1_v1_5
from Crypto.PublicKey import RSA
 
message = "0011547896523KE2018-08-09".encode('utf-8')
digest = SHA256.new()
digest.update(message)
 
private_key = False
with open("privatekey.pem", "r") as myfile:
    private_key = RSA.importKey(myfile.read())
 
signer = PKCS1_v1_5.new(private_key)
sigBytes = signer.sign(digest)
signBase64 = b64encode(sigBytes)
 
headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ipARkr578zcp2fYPiZpEL1cpG6qE',
    'signature': signBase64
}
 
params = {}
 
payload = {
    "countryCode": "KE",
    "accountId": "0011547896523",
    "date": "2018-08-09"
}
 
url = 'https://sandbox.jengahq.io/account-test/v2/accounts/accountbalance/query'
response = requests.post(url, headers=headers, params=params,
                         data=json.dumps(payload))
print(response.text)

And viola! You have done your first secure API request