Revision History
Sl# | Ver# | Modified By | Update | Updated On |
---|---|---|---|---|
1 | 1.0 | Tech Team | - Initial Draft | 29/05/2019 |
1. Introduction
Khosla Labs’ Veri5Digital Platform VideoIdKyc API provides various features related to the requirement of completing Know-Your-Customer (KYC) by companies. Video ID KYC APIs are standalone APIs which can be used by clients to digitize their customer onboarding process. Khosla Labs’ Video ID KYC product allows for digital, instant and paperless Know Your Customer (KYC) capability.
KYC is a tedious and costly activity required by businesses for their regulatory compliance. Traditionally, KYC was done in a Face-to-Face manner where the business’ employees, agents or Business Correspondents (BCs) would do KYC.
KYC process involves two primary activities - Identification (Identifying customer details) and Authentication (confirming details belong to the same individual). Khosla Labs’ Video ID KYC solution provides the capability to perform above KYC process in a completely digital fashion which eliminates the need for a physical visit and automates the process using easily integratable APIs which you can call from your Web or Mobile application.
The list of core APIs include :
docInfoExtract
faceExtract
faceCompare
2. Veri5 VideoIdKyc API Options
Video ID KYC API collection will have the following APIs:
docInfoExtract
faceExtract
faceCompare
2.1 DocInfoExtract
This API will do a full Optical Character Recognition (OCR) by scanning single or both sides of an identity document and automatically extract the details for you. The documents that are currently being accepted are:
Aadhaar Card
PAN Card
Voter ID Card
Passport
You can send an ID Card image along with the type of ID Card to this API, and it will do 2 things
Classification Check to confirm whether ID Card is the same as you claim it to be.
OCR of data on the card, extraction and then sharing details on the ID Card.
Using the above API, you can confirm the authenticity of the ID Card document and also get digitized data on the ID Card and store in your systems. Currently, we support Government ID Cards such as PAN, Aadhaar, Voter ID and Passport. We are in the process of adding other IDCards in subsequent releases.
curl -X POST \
https://sandbox.veri5digital.com/video-id-kyc/api/1.0/docInfoExtract \
-H 'Content-Type: application/json' \
-H 'cache-control: no-cache' \
-d '{
"headers": {
"client_code": "<<client_code>>",
"sub_client_code": "dummy-code",
"channel_code": "ANDROID_SDK",
"channel_version": "1.0.0",
"stan": "<<unique_stan>>",
"client_ip": "<<client_ip>>",
"transmission_datetime": "1428393481435",
"operation_mode": "SELF",
"run_mode": "TRIAL",
"actor_type": "OTHER",
"user_handle_type": "EMAIL",
"user_handle_value": "a@b.com",
"location": "NA",
"function_code": "DEFAULT",
"function_sub_code": "DEFAULT"
},
"request": {
"api_key": "<<api-key>>",
"request_id": "<<unique_request_id>>",
"purpose": "testing in Khosla",
"hash": "b9bbb3e188faae7190a41be8f35fd85ed0f49a093cb8f9ff916f29b1a9fe0749",
"document_type": "AADHAAR",
"document_side": "FRONT",
"extraction_type": "BOTH",
"document_front_image": "<<base64image>>",
"document_back_image": "<<base64image>>"
}
}'
2.2 Face Extract
This API will extract a face image embedded in a document or ID. Input for this API will be a single image.
You can use this API to replace sticking passport photos from Account Opening Forms and make the process digital.
Use this API to get the photo from ID Card or any other document and store in your systems according to business requirements.
2.3 FaceCompare
Authenticate the Identity through Face Biometrics based match. Check the likelihood that two faces belong to the same person through our AI-driven Face Compare API. This API will take 2 images as input. You can send 2 images through this API, we would use our ML-driven Face match algorithms to get back with Match Status and Accuracy %. You can use this API to check if the photo on the ID Card is the same as the photo of the person.
curl -X POST \
https://sandbox.veri5digital.com/video-id-kyc/api/1.0/faceCompare \
-H 'Content-Type: application/json' \
-H 'Postman-Token: 259f5ce7-e3fe-41cb-8524-38b11084880a' \
-H 'cache-control: no-cache' \
-d '{
"headers": {
"client_code": "<<client_code>>",
"sub_client_code": "<<client_code>>",
"channel_code": "ANDROID_SDK",
"channel_version": "1.0.0",
"stan": "<<unique_stan>>",
"client_ip": "49.205.221.149",
"transmission_datetime": "1428393481435",
"operation_mode": "SELF",
"run_mode": "TRIAL",
"actor_type": "OTHER",
"user_handle_type": "EMAIL",
"user_handle_value": "a@b.com",
"location": "NA",
"function_code": "DEFAULT",
"function_sub_code": "DEFAULT"
},
"request": {
"api_key": "<<api-key>>",
"request_id": "<<request_id>>",
"purpose":"Testing",
"hash": "357a8931d6ea23e2bb341f45e57805070c9b35c73ae276631b120b588d390404",
"image_1": "<<img_1>>",
"image_2": "<<img_2>>"
}
}'
3. Prerequisite
For a client to get access to APIs, client registration is mandatory for the chosen environment, ie either SandBox or Production. Based on the chosen environment , APIs will be accessible.
Its recommended to gain access to SandBox Environment and do technical integration and testing before accessing the same in Production Environment.
The client has to obtain the following values for the following params :
Client Code (client_code), that uniquely identifies the client that is onboarded with the Khosla Labs.
API-Key (api_key), will be provided during onboarding and is used for managing subscription.
Salt ( salt), this is the salt for calculating hash of request payload and will be shared during onboarding. Each api_key will have its own salt.
Hash ( hash ), this is the dynamic param that should be calculated during each SDK invocation.
For invoking API Intent call client_code, api_key, and hash have to be mandatorily passed
Refer Appendix A for Request Headers Information.
Refer Appendix B to see how to calculate the hash and encryption mechanism.
Refer Appendix C for Input Image Specifications for achieving better results.
Refer Appendix D for the encryption mechanism
Refer Appendix E for the Certificates Generation for response encryption
Refer Appendix F for Error Codes
4. API Specification
Khosla Lab’s APIs are exposed as REST Endpoints. Request and Response Payloads are JSON documents. API communications should be made over HTTPS.
APIs are exposed to consumers in different environments like SandBox and Production. The primary purpose of the sandbox environment is to expose the APIs to consumers where they can test and do technical integrations before moving to actual production.
4.1 API - docInfoExtract
The primary objective of this API is to extract the KYC information from the document image..
At a high level below given is the specification.
### 4.1.1 Request Payload
Method : POST
Url : https://<hostname>/video-id-kyc/api/1.0/docInfoExtract
{
"headers": // Please refer Appendix A for details.
{
"client_code": "<your_client_code>",
"sub_client_code": "<your_sub_client_code>",
"channel_code": "", // Can be Empty
"channel_version": "", // Can be Empty
// This should be a unique alphanumeric string generated by the calling system.
"stan": "<Unique Alphanumeric String>",
"client_ip": "",
"transmission_datetime": "",
"operation_mode": "SELF",
"run_mode": "",
"actor_type": "",
"user_handle_type": "",
"user_handle_value": "",
"location": "",
"function_code": "DEFAULT",
"function_sub_code": "DEFAULT"
},
"request": {
"api_key": "<your_api_key>",
"document_type": "<PAN|AADHAAR|VOTER_ID|PASSPORT>",
"document_side": "<FRONT|BOTH>",
"extraction_type": "<FACE|OCR|BOTH>",
"document_front_image": "<base_64_encoded_string>",
"document_back_image": "<base_64_encoded_string>"
}
}
### 4.1.2 Response Payload
Response:
{
"response_data": {
"encrypted": "<YES|NO>",
// This flag indicates whether the Kyc response is Encrypted or Not. If encrypted is ‘YES’, refer to Appendix D for decryption logic
"hash": "", // Generated Hash of the response payload
"document_data": "" // Please refer below for the details.
},
"response_status": {
"status": "<SUCCESS|FAIL>",
"code": "",
"message": ""
}
}
Document_data
In case document_data is encrypted ( based on flag “encrypted” :
This field encapsulates the KYC data extracted from the ID Document. By default, it will be an encoded JSON, whose structure is given below. Depending on the encrypted flag as “YES/NO”, this encoded string will be either encrypted or not.
Details of document_data are as given below :
{
"photo":{
"document_image":"" // Image of the input ID Document
},
"original_kyc_info":{
// This tag encapsulated the OCRed data from the ID Card
// Based on the ID Card Type, any of the following information will be present or not.
"doc_type":"", // Possible values : PAN|AADHAAR|PASSPORT|VOTER_ID
"first_name":"",
"second_name":"",
"dob":"",
"document_id":"", // ID Docs Number. Eg : PAN#, Aadhaar#
"address":"",
"gender":"",
},
"doc_image":{
// Any of the below-mentioned parameters (Based on Request)
"pan":"",
"aadhaar_front":"",
"aadhaar_back":"",
"passport_front":"",
"passport_back":"",
"voter_id_front":"",
"voter_id_back":"",
},
"quality_check":{
"front_image":{
"image_aspect_ratio":"",
"image_exposure":"",
"image_blur":"",
"image_colour":"",
"image_reflection":""
},
"back_image":{
"image_aspect_ratio":"",
"image_exposure":"",
"image_blur":"",
"image_colour":"",
"image_reflection":""
}
}
}
4.2 API - faceExtract
The primary objective of this API is to extract the face from the input Id image.
Method : POST
Url : https://hostname
/video-id-kyc/api/1.0/faceExtract
### 4.2.1 Request Payload
{
"headers": // Please refer Appendix A for details.
{
"client_code": "<your_client_code>",
"sub_client_code": "<your_sub_client_code>",
"channel_code": "",
"channel_version": "",
"stan": "<a_unique_integer>",
"client_ip": "",
"transmission_datetime": "",
"operation_mode": "SELF",
"run_mode": "",
"actor_type": "",
"user_handle_type": "",
"user_handle_value": "",
"location": "",
"function_code": "DEFAULT",
"function_sub_code": "DEFAULT"
},
"request": {
"api_key": "<your_api_key>",
"request_id": "<REQUEST_ID>",
"purpose":"<PURPOSE>",
"hash": "Refer Appendix B",
"image": "<base_64_encoded_string>"
}
}
### 4.2.2 Response Payload
{
"response_data": {
"encrypted": "<YES|NO>",
"hash": "",
"face": "" // Base64 Encoded image of the extracted face.
},
"response_status": {
"status": "<SUCCESS|FAIL>",
"code": "",
"message": ""
}
}
In case face is encrypted ( based on flag “encrypted” :
4.3 API - faceCompare
This API can be used to compare two face images. Input Images can be
Two ID card images containing one face each
Two images that contain one face each
An Image that contains one face and an ID card image that contains one face
Method : POST
Url : https://hostname
/video-id-kyc/api/1.0/faceCompare
### 4.3.1 Request Payload
{
"headers": // Please refer Appendix A for details.
{
"client_code": "<your_client_code>",
"sub_client_code": "<your_sub_client_code>",
"channel_code": "",
"channel_version": "",
"stan": "<a_unique_integer>",
"client_ip": "",
"transmission_datetime": "",
"operation_mode": "SELF",
"run_mode": "",
"actor_type": "",
"user_handle_type": "",
"user_handle_value": "",
"location": "",
"function_code": "DEFAULT",
"function_sub_code": "DEFAULT"
},
"request": {
"api_key": "<your_api_key>",
"request_id": "<REQUEST_ID>",
"purpose":"<PURPOSE>",
"hash": "Refer Appendix B",
"image_1": "<base_64_encoded_string>",
"image_2": "<base_64_encoded_string>"
}
}
### 4.3.2 Response Payload
{
"response_data": {
"status": "",
"hash": "",
"score": ""
},
"response_status": {
"status": "<SUCCESS|FAIL>",
"code": "",
"message": ""
}
}
Appendix A: Request Header Field Details
Every APIs Request Payload has a custom “header” field, whose details are given below. Fields marked ( * ) are mandatory.
S.NO | Field Name | Mandatory | Description | Example |
---|---|---|---|---|
1 | client_code* | A unique code assigned to a client while onboarding. | client123 | |
2 | sub_client_code* | Unique code assigned to a sub client of a hosted client.In case not applicable, an EMPTY (“”) string can be passed. | client123 | |
3 | actor_type* | Type of person who is making the request.If not relevant, "DEFAULT" can also be used. | CUSTOMER/DEFAULT | |
4 | channel_code* | Channel code used by the client to initiate the request. | ANDROID_SDK | |
5 | stan* | System Trace Number - A unique number generated per request by the client. It is used to uniquely identify a request from a client. It can be used for troubleshooting. | 98321892319 | |
6 | user_handle_type* | Indicates the type of the user identifier, who has initiated the request. Eg: UUID, MSISDN, EMAIL. If not relevant, "DEFAULT" can also be used. | ||
7 | user_handle_value* | Value of user_handle_type.If not relevant, "DEFAULT" can also be used. | a@b.com/DEFAULT | |
8 | location | Specifies the geo location of the device that initiated the request. | ||
9 | transmission_datetime* | Time in System Milli Seconds at which the request was initiated by the client. | 155325508029 | |
10 | run_mode* | TRIAL - Indicates need for mock run of the transaction. No transaction posting will happen. REAL - Indicates the actual transaction posting request. If not relevant, "DEFAULT" can also be used. | DEFAULT/TRIAL/REAL/RETRY | |
11 | client_ip | IP Address of the client device.In case not applicable, an EMPTY ( "") string can be passed. | ||
12 | operation_mode* | The way in which the request is executed. SELF - aadhaar holder doing the transaction. ASSISTED - where auth/ekyc is done in operator assisted mode. SYSTEM- where transaction is automated by system (batch processes). If not relevant, "DEFAULT" can also be used. | DEFAULT/SELF/ASSISTED/SYSTEM | |
13 | channel_version* | The version of the channel application like ANDROID_SDK. | 3.1.7 | |
14 | function_code* | DEFAULT: Indicates default behavior for this API | DEFAULT | |
15 | function_sub_code | DEFAULT: Indicates default behavior for this API | DEFAULT | |
16 | request_id* | Client generated Id for referencing the transaction uniquely. | requestid123 | |
17 | hash* | Hash to be calculated by the client to verify the integrity of the request. Please refer APPENDIX B to see how to generate the hash. | ||
18 | api_key* | api_key shared with the client during onboarding. | samplekey123 |
Appendix B: Hash Generation
It is essential that we have a definitive protocol to verify the integrity of all the communication between Khosla Labs Platform and Client.
So for every request coming to KL, client has to supply a hash which KL will use as the first step of verification.
In return, all responses will also contain hash supplied by KL Platform.
Hash should be calculated using the following method: hash=SHA256(Hash-Sequence)
Hash Sequence is specified as follows(no space, no commas, no single/double quotes)
--> docInfoExtract :
client_code
|request_id
|api_key
|salt
--> faceExtract :
client_code
|request_id
|api_key
|salt
--> faceCompare :
client_code
|request_id
|api_key
|salt
Example (for _init request):
If your
client_code=a1b2c3,
api_key=123,
requestId=1234567890101112,
salt=e1d2c3b4a,
then
Hash-Sequence=a1b2c3|1234567890101112|123|e1d2c3b4a
hash =SHA-256(Hash-Sequence)
For validation:
- Receiving end should calculate hash based on request parameters and match it against the received hash.
- If receivedHash=calculatedHash, then only you should proceed with your application logic.
api_key and salt are the key parameters here. It is known only to the client and Khosla Labs.
One api_key is mapped to one salt. This allows us to have multiple salts active at a time.
api_key and hash need to be passed in each API call. Salt is never transmitted in any API call.
Appendix C : Input Image Specification
For better results, it is recommended to ensure that Input Image complies to the following specifications :
Images should not be blurry.
Images with skew of more than 45 degrees is not a good input.
Out of focus images are not recommended.
Damaged images are not recommended.
Images with some signature or stamp over text area or fingers covering images or MRZ in passports (not standard one) may not yield better results.
Recommended Image formats include JPEG, PNG.
For better results, it is recommended to have image size < 500 KB.
Images with less than 480 pixel width (horizontal shaped ids) or 480 pixel height (vertical shaped ids - voter). We don’t actually check for pixel size but should work for < 480 in cases where the image quality is good.
Images with bad exposure (very dark or very bright) will not yield good results.
300 DPI for best results
Appendix D: Decryption Logic
To decrypt the response, please follow the steps below:
// byte array of the base64 decoded response
byte[] decodeResponse = <<base64_decode_response>>;
// open your p12 file
// to generate your p12 file, refer Appendix E
InputStream inputStream = new FileInputStream("<<your_p12_file>>.p12");
// your keystore credentials
char[] keystorePassword = "<<your_keystore_password>>".toCharArray();
char[] aliasPassword = "<<your_alias_password>>".toCharArray();
String alias = "<<your_alias>>";
KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");
keyStore.load(inputStream, keystorePassword);
// get the private key
PrivateKey privateKey =
(PrivateKey) keyStore.getKey(alias, aliasPassword);
ResponseByteArraySpliter splitter =
new ResponseByteArraySpliter(decodeResponse);
// get the symmetric key
byte[] sKey =
DecryptionUtil.decryptUsingPrivateKey(
privateKey, splitter.getEncryptedSymmetricKey()
);
// byte array of the decrypted data
byte[] decryptedData =
DecryptionUtil.decryptUsingSymmetricKey(
sKey, splitter.getEncryptedData()
);
// ResponseByteArraySplitter class
// Note: Please alter the access modifiers according to your packaging
// strategy.
public class ResponseByteArraySplitter {
private static final int SYMMETRIC_KEY_SIZE = 256;
private final byte[] encryptedSymmetricKey;
private final byte[] encryptedData;
public ResponseByteArraySplitter(byte[] data) throws Exception {
int offset = 0;
encryptedSymmetricKey = new byte[SYMMETRIC_KEY_SIZE];
copyByteArray(
data,
offset,
encryptedSymmetricKey.length,
encryptedSymmetricKey
);
offset = offset + SYMMETRIC_KEY_SIZE;
encryptedData = new byte[data.length - offset];
copyByteArray(data, offset, encryptedData.length, encryptedData);
}
byte[] getEncryptedSymmetricKey() {
return encryptedSymmetricKey;
}
byte[] getEncryptedData() {
return encryptedData;
}
private void copyByteArray(byte[] src, int offset, int length,byte[] dest) throws Exception {
try {
System.arraycopy(src, offset, dest, 0, length);
} catch (Exception e) {
throw new Exception("Decryption failed, Corrupted packet!", e);
}
}
}
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import javax.crypto.Cipher;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
public class DecryptionUtil {
private static final String ASYMMETRIC_ALGO =
"RSA/ECB/PKCS1Padding";
public static byte[] decryptUsingPrivateKey(
PrivateKeyprivateKey,
byte[] data)
throws IOException, GeneralSecurityException {
Cipher pkCipher = Cipher.getInstance(ASYMMETRIC_ALGO);
pkCipher.init(Cipher.DECRYPT_MODE, privateKey);
return pkCipher.doFinal(data);
}
public static byte[] decryptUsingSymmetricKey(byte[] symmetricKey, byte[] data) throws InvalidCipherTextException {
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new AESEngine(), new PKCS7Padding()
);
cipher.init(false, new KeyParameter(symmetricKey));
int outputSize = cipher.getOutputSize(data.length);
byte[] tempOP = new byte[outputSize];
int processLen = cipher.processBytes(data,0,data.length,tempOP,0);
int outputLen = cipher.doFinal(tempOP, processLen);
byte[] result = new byte[processLen + outputLen];
System.arraycopy(tempOP, 0, result, 0, result.length);
return result;
}
}
Appendix E: Certificates Generation for response encryption
How to generate a CSR?
Steps to generate a CSR file using OpenSSL.
Create private key with a validity
- keytool -genkey -alias ALIAS_NAME -keyalg RSA -keystore COMP_NAME.jks
- keysize 2048 -dname "CN=COMMON_NAME, EMAILADDRESS=sample@abc.com, C=IN,OU=ORG_UNIT, O=ORGANIZATION_NAME"
Create CSR for above key
- keytool -certreq -alias ALIAS_NAME -file COMP_NAME.csr -keystore COMP_NAME.jks
Convert jks store to p12
- keytool -importkeystore -srckeystore COMP_NAME.jks -destkeystore COMP_NAME.p12 -srcstoretype jks -deststoretype pkcs12
Get private key from .p12 file
- openssl pkcs12 -in COMP_NAME.p12 -nodes -out COMP_NAME.key -nocerts
Self sign .csr file using the private key to get .cer file
- openssl x509 -req -in COMP_NAME.csr -signkey COMP_NAME.key -out COMP_NAME.cer
Provide us the .cer file (public key certificate) generated above using which KYC response will be encrypted and use .p12 (private key) for decryption.
Glossary
Details to be ensured while generating the “CSR” through the terminal window by the customer are:
ALIAS_NAME: This could be anything.
COMP_NAME: This could be anything.
COMMON_NAME : This should be the same as the contact person’s name.
sample@abc.com : This should be some valid email id.
ORG_UNIT : Value should be “Khosla Labs”
ORGANIZATION_NAME : This should be the same as the organization name.
Appendix F : Error Codes
Error Code | Scenario | Message |
---|---|---|
380024 | General | user_id is mandatory |
380025 | General | hash is mandatory |
380026 | General | api_key is mandatory |
380048 | General | Invalid api_key or client_code |
380048 | General | Invalid api_key or client_code |
380091 | General | Hash validation failed |
380076 | General | No such user-id exists |
333 | General | Unexpected Error Occurred |
380135 | faceExtract | The face could not be extracted from the image. |
380136 | faceCompare | image_1 is mandatory |
380147 | faceCompare | image_2 is mandatory |
380090 | faceCompare | Hash is mandatory |
380124 | docInfoExtract | Ocr failed on the given document image. |
380122 | docInfoExtract | Document type {} is not supported. |
380123 | docInfoExtract | Could not extract the face from the document image. |
380125 | docInfoExtract | The document could not be detected. |
Appendix G : Important Things To consider before Uploading the Image
Place the camera right above the document and not sideways
Take image of the document in front of a white background.
Make sure there is sufficient lighting in the room and no white light on the document while taking its photo.
Try to get a stable photo and not a shaky one.