Using Sec Key (Legacy)
The Sec Key security implementation has been deprecated. We still support authentication via Sec Key but we strongly suggest migrating to the streamlined Signature authentication approach.
If you use one of the SDKs, there is no reason to use the code in the following section as the sec_key can be generated 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 for Sec Key (Legacy)
To generate your Sec Key, you will need the API Key for Sec Key (Legacy)
Your api key is a Base64 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 any time, however your previous key will be immediately rendered inert.
You can find and generate your an API key here. To 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 Base64 encoded RSA public key. The key is unique to each environment, so you will need a different key for the test and production environments. You will need to know your partner ID, which is available below, to create the signature.
Your partner ID: 85
String Value of your partner ID: "085"
In the calculation of the signature use an integer for your partner ID, everywhere else use a string
Be sure to specify pkcs1
padding when utilising 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
> 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 = "<Your api key>"
partner_id = "<Your partner ID>"
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.
> 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.
> 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.
$timestamp = time();
$plaintext = intval($this->partner_id) . ":" . $timestamp;
$hash_signature = hash('sha256', $plaintext);
openssl_public_encrypt($hash_signature, $sec_key, base64_decode($this->api_key), OPENSSL_PKCS1_PADDING);
$sec_key = base64_encode($sec_key);
$sec_key = $sec_key . "|" . $hash_signature;
return array($sec_key, $timestamp);
> 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();
}
}
}
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 ID 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();
}
}
}
Last updated