Sec Key Calculation

Only required if you can't use one of our gems/packages.

If you use one of the supplied gems/packages there is no reason use the code in the following section as the sec_key can be gotten by calling the generate_sec_key function.

Definitions:

api_key - Half of a secret RSA key pair. Used to create signature token (sec_key).

sec_key - Token that combines your partner ID and a timestamp and is encrypted with the api_key.

API Key

Your api is a base 64 encoded RSA public key. It is used to encode a signature for requests made to our server, and to decode the signature of requests coming from our server. For information about how to create and decode signatures see the authentication section of the documentation. You can rotate your API key at time, however, your previous key will be immediately rendered inert.

You can find and generate your an API key hereTo communicate with our system we require a signature on each request to ensure that both parties are who they say they are. In order to sign your requests to our servers, the first step is to generate an API key here. This will return a base 64 encoded RSA public key. The key is unique to each environment, so you will need a different key for the test environment and the production environments. You will need to know your partner ID, which is available below, to create the signature.

Your partner ID: 5

String Value of your partner ID: "005"

In the calculation of the signature use an integer for your partner ID, everywhere else use a string

  1. Create a timestamp

  2. Join the integer value of your int Partner ID to the timestamp using a semi-colon in the format 5:[timestamp]

  3. Create a SHA256 hash of the string created in Step 2

  4. Public Key encrypt the hash using your Base 64 decoded API key. We encourage that you utilise the base 64 encoded version and then decode the api key on your server to public encrypt the hash, however, should you choose to use the the decoded version of your api key, you will also find it here

  5. Base64 encode the encrypted hash

  6. Finally join the the encrypted hash to the unencrypted hash using the "|" character

Be sure to specify pkcs1 padding when utilizing the public key encrypt function of your rsa key, if that is not the default padding of your language of choice.

Example code for creating the signature

Ruby
JavaScript
Python
Java
C#
Ruby

> Calculating your outgoing signature:
api_key = <Your API key>
partner_id = "<Your partner ID>"
timestamp = Time.now.to_i
hash_signature = Digest::SHA256.hexdigest([partner_id.to_i, timestamp].join(":"))
public_key = OpenSSL::PKey::RSA.new(Base64.decode64(api_key))
signature = [Base64.encode64(public_key.public_encrypt(hash_signature)), hash_signature].join('|')
> Make sure to replace partner_id with your Partner ID.
> Make sure to replace api_key with your API key.
> Calculating an incoming signature:
api_key = "TgorWTFHTG5NL2hEUU5aM3M2ZWJCUndWZGR1aTRLejNCTGZ2RkJwWis1Myty="
partner_id = "001"
timestamp = body["timestamp"]
hash = Digest::SHA256.hexdigest([partner_id.to_i, timestamp].join(":"))
encrypted, hashed = body["sec_key"].split("|")
success = OpenSSL::PKey::RSA.new(Base64.decode64(api_key)).public_decrypt(Base64.decode64(encrypted)) == hash && hash == hashed
## success should equal true
> Make sure to replace partner_id with the String Value of your Partner ID.
> Make sure to replace api_key with your API key.
JavaScript

> Calculating your outgoing signature:
> Please note, you will need to decode your base64 API Key before use
const crypto = require('crypto');
const https = require('https');
const request = require('request'); // npm install request
const URLSafeBase64 = require('urlsafe-base64');
const uuid = require('uuid4'); // npm install uuid4
let apiKey = <Your API key>
let timestamp = Date.now();
let partner_id = "<Your partner ID>";
let hash = crypto.createHash('sha256').update(parseInt(partner_id, 10) + ":" + timestamp).digest('hex');
let encrypted = crypto.publicEncrypt({
key: Buffer.from(apiKey, 'base64'),
padding: crypto.constants.RSA_PKCS1_PADDING
}, Buffer.from(hash)).toString('base64');
let signature = [encrypted, hash].join('|');
> Make sure to replace partner_id with the String Value of your Partner ID.
> Make sure to replace api_key with your API key.
> Calculating an incoming signature:
const crypto = require('crypto');
const URLSafeBase64 = require('urlsafe-base64');
let timestamp = body["timestamp"]
let encrypted, hashed;
[encrypted, hashed] = body["sec_key"].split("|")
let partner_id = "001";
let apiKey = "TgorWTFHTG5NL2hEUU5aM3M2ZWJCUndWZGR1aTRLejNCTGZ2RkJwWis1Myty=";
let hash = crypto.createHash('sha256').update(parseInt(partner_id) + ":" + timestamp).digest('hex');
let decrypted = crypto.publicDecrypt({
key: Buffer.from(Buffer.from(apiKey, 'base64')),
padding: crypto.constants.RSA_PKCS1_PADDING
}, Buffer.from(encrypted, 'base64'));
let success = decrypted == hashed && hashed == hash;
// success should equal true
> Make sure to replace partner_id with the String Value of your Partner ID.
> Make sure to replace api_key with your API key.
Python
> Calculating your outgoing signature:
> Make sure to replace partner_id with the String Value of your Partner ID.
> Make sure to replace api_key with your API key.
import base64
import hashlib
import uuid
from Crypto.PublicKey import RSA
from Crypto import Random
from Crypto.Cipher import PKCS1_v1_5
api_key = base64.b64decode('<Your API key>')
partner_id = "<Your partner ID>"
timestamp = <Time In Milliseconds>
hashed = hashlib.sha256('{}:{}'.format(int(partner_id), timestamp).encode("utf-8")).hexdigest()
public_key = RSA.importKey(api_key)
cipher = PKCS1_v1_5.new(public_key)
encrypted = base64.b64encode(cipher.encrypt(hashed.encode("utf-8")))
signature = "{}|{}".format(encrypted.decode("utf-8"), hashed)
> Calculating an incoming signature:
import rsa
import base64
import hashlib
api_key = "TgorWTFHTG5NL2hEUU5aM3M2ZWJCUndWZGR1aTRLejNCTGZ2RkJwWis1Myty="
partner_id = "001"
timestamp = body["timestamp"]
encrypted, hashed = body["sec_key"].split("|")
hash = hashlib.sha256('{}:{}'.format(int(partner_id), timestamp)).hexdigest()
## Note: python does not handle RSA keys natively so I use the rsa library
success = hashed == base64.b64encode(rsa.decrpy(encrypted, base64.b64decode(api_key))) && hashed == hash
## success should equal true
> Make sure to replace partner_id with the String Value of your Partner ID.
> Make sure to replace api_key with your API key.
Java

> Calculating your outgoing signature:
> Make sure to replace partner_id with the String Value of your Partner ID.
> Make sure to replace api_key with the decoded version of your API key. This is special to the Java implementation
import java.util.Base64;
import java.security.spec.X509EncodedKeySpec;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.MessageDigest;
import java.security.Signature;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.*;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.BadPaddingException;
import javax.crypto.NoSuchPaddingException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.UUID;
import java.util.Iterator;
import java.io.StringReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.*;
import java.lang.Exception;
public class MyClass {
public static PublicKey loadPublicKey(String apiKey) throws GeneralSecurityException, IOException {
byte[] data = Base64.getDecoder().decode((apiKey.getBytes()));
X509EncodedKeySpec spec = new X509EncodedKeySpec(data);
KeyFactory factObj = KeyFactory.getInstance("RSA");
PublicKey lPKey = factObj.generatePublic(spec);
return lPKey;
}
public static byte[] encryptString(PublicKey key, String plaintext) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(plaintext.getBytes());
}
public static String bytesToHexStr(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
public static void main(String args[]) {
try {
String api_key = <Your API key>;
int partner_id = Integer.parseInt("<Your partner ID>")
long timestamp = System.currentTimeMillis();
String toHash = partner_id + ":" + timestamp;
System.out.println("Original: " + toHash);
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(toHash.getBytes());
byte[] hashed = md.digest();
String toEncryptString = bytesToHexStr(hashed);
System.out.println("Key used to encrypt: " + api_key);
PublicKey publicKey = loadPublicKey(api_key);
byte[] encSignature = encryptString(publicKey, toEncryptString);
String signature = Base64.getEncoder().encodeToString(encSignature) + "|" + toEncryptString;
System.out.println("sec_key: " + signature);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
C#
using System;
using System.Security.Cryptography;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.OpenSsl;
// Due to limitations of the default C# crypto library
// we use the bouncy castle crypto library available here
// https://www.bouncycastle.org/csharp/
namespace smile_identity_csharp
{
class Program
{
public static string RsaDecryptWithPublic(string base64Input, string publicKey)
{
var bytesToDecrypt = Convert.FromBase64String(base64Input);
var decryptEngine = new Pkcs1Encoding(new RsaEngine());
using (var txtreader = new System.IO.StringReader(publicKey))
{
var keyParameter = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();
decryptEngine.Init(false, keyParameter);
}
var decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
return decrypted;
}
public static string RsaEncryptWithPublic(string clearText, string publicKey)
{
var bytesToEncrypt = Encoding.UTF8.GetBytes(clearText);
var encryptEngine = new Pkcs1Encoding(new RsaEngine());
using (var txtreader = new System.IO.StringReader(publicKey))
{
var keyParameter = (AsymmetricKeyParameter)new PemReader(txtreader).ReadObject();
encryptEngine.Init(true, keyParameter);
}
var encrypted = Convert.ToBase64String(encryptEngine.ProcessBlock(bytesToEncrypt, 0, bytesToEncrypt.Length));
return encrypted;
}
public static string ByteArrayToHexString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
static void Main(string[] args)
{
// Download your API key from the Smile Identity portal
// and either copy below or read it from a file.
string api_key =
"-----BEGIN PUBLIC KEY-----\n" +
"1234567890123456789012345678901223456789012345678901234567890123\n" +
"-------------Example to show required formatting ---------------\n" +
"abcdefghijklmonpqrstuvwxyzabcdefghijklmonpqrstuvwxyzabcdefghijkl\n" +
"123456789012345678901234\n" +
"-----END PUBLIC KEY-----";
// Your partnerID is available froom the portal
// Note:Most transactions with the server use the partnerID as a string
string strPartnerID = "<Put your 3 digit partner ID here>";
// But for the sec_key calculation we need an int.
int partnerID = Int32.Parse(strPartnerID);
// Get a Uxix timestamp
Int32 timestamp = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
string plaintext = partnerID.ToString() + ":" + timestamp.ToString();
Console.WriteLine("plaintext: " + plaintext);
// Hash the plaintext
SHA256 mySHA256 = SHA256.Create();
var bytesToEncrypt = mySHA256.ComputeHash(Encoding.UTF8.GetBytes(plaintext));
var encrypted = RsaEncryptWithPublic(ByteArrayToHexString(bytesToEncrypt), api_key);
Console.WriteLine("Encrypted Message len : " + encrypted.Length); // should be 172
Console.WriteLine("Encrypted Message:\r\n" + encrypted);
string sec_key = encrypted + "|" + ByteArrayToHexString(bytesToEncrypt);
Console.WriteLine("sec_key Len :" + sec_key.Length); // Should be 237
Console.WriteLine("sec_key:\r\n" + sec_key);
// Use sec_key and timestamp in sending messages to the server.
// To verify retunred signature from the server
var signature = "<place 237 length signature here";
char[] seps = { '|' };
string[] splitSig = signature.Split(seps, 2, StringSplitOptions.RemoveEmptyEntries);
string decoded_sig = RsaDecryptWithPublic(splitSig[0], api_key);
Console.WriteLine("decoded_sig Len :" + decoded_sig.Length); // should be 64
Console.WriteLine("decoded_sig:\r\n" + decoded_sig);
if ((string.Compare(splitSig[1], decoded_sig) == 0))
{
Console.WriteLine("Verified");
}
else
{
Console.WriteLine("Not Verified");
}
Console.WriteLine("\r\nPress any key to exit.");
Console.ReadKey();
}
}
}