Secure Data Add-On
Setu Encrypted APIs
Overview
Our secure data add-on is designed to be integrated with any KYC API to enhance the protection of sensitive data during transmission.
With this add-on, non-sensitive fields (such as id and traceId) remain visible for tracking and logging, while all sensitive information is securely packaged inside the encrypted_data field.
Setu’s public key, fetched dynamically, is used to secure the sensitive data in your request. The response from Setu will also be secured, and you must decrypt it to reconstruct the complete message.
Integration Steps
1. Public Key Exchange
Before transmitting any secured data, you must retrieve Setu's latest public key dynamically.
This ensures that all secured requests use the current key, as keys are rotated periodically.
GET /v2/public-keySample Response:
{
"public-key": {
"ECIES": "03d…",
"RSA": "MIIBI…"
},
"traceId": "1-67dbb.."
}Note: The public key may be updated over time.
2. Securing Sensitive Data (Request)
Use Setu's public key (obtained from the previous step) to secure your request payload. Below are the two available methods for securing the sensitive data in your request.
- Process:
The sensitive payload is directly secured using the ECIES mechanism and Setu’s public key. - Request Message Structure:
{
"id": "unique-request-id-123",
"encrypted_data": "Encrypted_Payload"
}- Process:
- A temporary AES key is generated.
- The sensitive payload is encrypted using this AES key.
- The AES key is then encrypted using Setu’s RSA public key.
- Request Message Structure:
{
"id": "unique-request-id-123",
"encrypted_data": "Base64_Encrypted_Payload",
"encrypted_key": "Base64_Encrypted_AES_Key"
}Note: In your secured request, use Setu's public key (from section 1) to secure the data.
3. Response Handling
Setu's response will include unprotected fields (like id and traceId) along with the secured sensitive data. Depending on the method used:
- For ECIES:
The response will include anencrypted_datafield.
To recover the sensitive payload, decrypt theencrypted_datausing your ECIES private key.
{
"id": "unique-request-id-123",
"traceId": "trace-id-456",
"encrypted_data": "Encrypted_Payload"
}- For AES-RSA (Hybrid):
The response will include bothencrypted_dataandencrypted_keyfields.
First, unwrap the AES key by decryptingencrypted_keyusing your RSA private key.
Then, use the recovered AES key to decrypt theencrypted_dataand retrieve the sensitive payload.
{
"id": "unique-request-id-123",
"traceId": "trace-id-456",
"encrypted_data": "Base64_Encrypted_Payload",
"encrypted_key": "Base64_Encrypted_AES_Key"
}After decryption, merge the recovered sensitive data with the unprotected fields to reconstruct the complete response.
Note: Store your private keys securely and share your public key to setu
Code Examples
import base64
import os
from Crypto.Cipher import AES, PKCS1_v1_5
from Crypto.Util.Padding import pad, unpad
from Crypto.PublicKey import RSA
import json
def generate_aes_key() -> str:
key = os.urandom(32) # 256-bit key
return base64.b64encode(key).decode('utf-8')
def aes_encrypt(message: str, key_base64: str) -> str:
key = base64.b64decode(key_base64)
iv = os.urandom(16)
cipher = AES.new(key, AES.MODE_CBC, iv)
ciphertext = cipher.encrypt(pad(message.encode(), AES.block_size))
return base64.b64encode(iv + ciphertext).decode('utf-8')
def aes_decrypt(message: str, key_base64: str) -> str:
key = base64.b64decode(key_base64)
encrypted_data = base64.b64decode(message)
iv = encrypted_data[:16]
ciphertext = encrypted_data[16:]
cipher = AES.new(key, AES.MODE_CBC, iv)
return unpad(cipher.decrypt(ciphertext), AES.block_size).decode('utf-8')
def rsa_encrypt(message, public_key_b64):
public_key_der = base64.b64decode(public_key_b64)
public_key = RSA.import_key(public_key_der)
cipher = PKCS1_v1_5.new(public_key)
encrypted = cipher.encrypt(message.encode())
return base64.b64encode(encrypted).decode('utf-8')
def rsa_decrypt(encrypted_message_b64, private_key_b64):
private_key = RSA.import_key(base64.b64decode(private_key_b64))
encrypted_message = base64.b64decode(encrypted_message_b64)
cipher = PKCS1_v1_5.new(private_key)
decrypted_message = cipher.decrypt(encrypted_message, None).decode('utf-8')
return decrypted_message
def encrypt(rsa_public_key: str, data_str: str) -> dict:
aes_key = generate_aes_key()
secured_data = aes_encrypt(message=data_str, key_base64=aes_key)
wrapped_key = rsa_encrypt(message=aes_key, public_key_b64=rsa_public_key)
return {"encrypted_data": secured_data, "encrypted_key": wrapped_key}
def decrypt(rsa_private_key: str, encrypted_key: str, encrypted_data: str) -> dict:
aes_key = rsa_decrypt(encrypted_message_b64=encrypted_key, private_key_b64=rsa_private_key)
data = aes_decrypt(message=encrypted_data, key_base64=aes_key)
return json.loads(data)
# Example usage:
private_key = "<YOUR_RSA_PRIVATE_KEY>"
public_key = "<YOUR_RSA_PUBLIC_KEY>"
data = '{"ifsc":"ABCD0123456","accountNumber":"1234567890","narration":"test transaction","matchKey":"gaurav"}'
secured_response = encrypt(rsa_public_key=public_key, data_str=data)
print("Secured Request:", secured_response)
recovered_data = decrypt(rsa_private_key=private_key, encrypted_key=secured_response['encrypted_key'], encrypted_data=secured_response['encrypted_data'])
print("Recovered Data:", recovered_data)import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.security.spec.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.security.interfaces.RSAPrivateKey;
public class AesRsaExample {
// AES Utility Methods
public static String generateAESKey() {
try {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey secretKey = keyGen.generateKey();
return Base64.getEncoder().encodeToString(secretKey.getEncoded());
} catch (Exception e) {
throw new RuntimeException("Error generating AES key", e);
}
}
public static String aesEncrypt(String message, String keyBase64) {
try {
byte[] key = Base64.getDecoder().decode(keyBase64);
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
byte[] ciphertext = cipher.doFinal(message.getBytes());
byte[] encryptedData = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, encryptedData, 0, iv.length);
System.arraycopy(ciphertext, 0, encryptedData, iv.length, ciphertext.length);
return Base64.getEncoder().encodeToString(encryptedData);
} catch (Exception e) {
throw new RuntimeException("Error encrypting message", e);
}
}
public static String aesDecrypt(String encryptedMessage, String keyBase64) {
try {
byte[] key = Base64.getDecoder().decode(keyBase64);
byte[] encryptedData = Base64.getDecoder().decode(encryptedMessage);
byte[] iv = new byte[16];
byte[] ciphertext = new byte[encryptedData.length - 16];
System.arraycopy(encryptedData, 0, iv, 0, 16);
System.arraycopy(encryptedData, 16, ciphertext, 0, ciphertext.length);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
byte[] plaintext = cipher.doFinal(ciphertext);
return new String(plaintext);
} catch (Exception e) {
throw new RuntimeException("Error decrypting message", e);
}
}
// RSA Utility Methods
public static String[] generateRSAKeys() {
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair pair = keyGen.generateKeyPair();
String privateKeyBase64 = Base64.getEncoder().encodeToString(pair.getPrivate().getEncoded());
String publicKeyBase64 = Base64.getEncoder().encodeToString(pair.getPublic().getEncoded());
return new String[]{privateKeyBase64, publicKeyBase64};
} catch (Exception e) {
throw new RuntimeException("Error generating RSA keys", e);
}
}
public static String rsaEncrypt(String message, String publicKeyBase64) {
try {
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyBase64);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(message.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
} catch (Exception e) {
throw new RuntimeException("Error encrypting with RSA", e);
}
}
public static String rsaDecrypt(String encryptedMessageBase64, String privateKeyBase64) {
try {
byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyBase64);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedMessageBase64);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes);
} catch (Exception e) {
throw new RuntimeException("Error decrypting with RSA", e);
}
}
public static String getPublicKeyFromPrivateKey(String privateKeyBase64) {
try {
byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyBase64);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
PublicKey publicKey = keyFactory.generatePublic(new RSAPublicKeySpec(
((RSAPrivateKey) privateKey).getModulus(),
((RSAPrivateKey) privateKey).getPrivateExponent()
));
return Base64.getEncoder().encodeToString(publicKey.getEncoded());
} catch (Exception e) {
throw new RuntimeException("Error extracting public key", e);
}
}
// Hybrid Encryption Methods
public static Map<String, String> encrypt(String rsaPublicKey, String dataStr) {
String aesKey = generateAESKey();
String encData = aesEncrypt(dataStr, aesKey);
String encAESKey = rsaEncrypt(aesKey, rsaPublicKey);
Map<String, String> result = new HashMap<>();
result.put("encrypted_data", encData);
result.put("encrypted_key", encAESKey);
return result;
}
public static String decrypt(String rsaPrivateKey, String encryptedKey, String encryptedData) {
String aesKey = rsaDecrypt(encryptedKey, rsaPrivateKey);
return aesDecrypt(encryptedData, aesKey);
}
public static void main(String[] args) {
String privateKey = "<YOUR_RSA_PRIVATE_KEY>";
String publicKey = "<YOUR_RSA_PUBLIC_KEY>";
String data = "{\"ifsc\":\"ABCD0123456\",\"accountNumber\":\"1234567890\",\"narration\":\"test transaction\",\"matchKey\":\"gaurav\"}";
Map<String, String> encryptedResponse = encrypt(publicKey, data);
System.out.println("Encrypted Response: " + encryptedResponse);
String decryptedData = decrypt(privateKey, encryptedResponse.get("encrypted_key"), encryptedResponse.get("encrypted_data"));
System.out.println("Decrypted Data: " + decryptedData);
}
}import ecies
from ecies.utils import generate_key
def generate_key_pair():
private_key = generate_key()
public_key = private_key.public_key
return private_key.to_hex(), public_key.format(True).hex()
def encrypt_payload(public_key_hex, payload):
encrypted_data = ecies.encrypt(public_key_hex, payload.encode())
return encrypted_data.hex()
def decrypt_payload(private_key_hex, encrypted_data_hex):
decrypted_data = ecies.decrypt(private_key_hex, bytes.fromhex(encrypted_data_hex))
return decrypted_data.decode()
private_key, public_key = generate_key_pair()
payload = '{"ifsc":"ABCD0123456","accountNumber":"1234567890","narration":"test transaction","matchKey":"gaurav"}'
secured_payload = encrypt_payload(public_key, payload)
print("Secured Payload:", secured_payload)
recovered_payload = decrypt_payload(private_key, secured_payload)
print("Recovered Payload:", recovered_payload)