Retrieve sensitive card information
Learn the secure method for retrieving sensitive card information, such as the full card number and CVV. This guide explains the security requirements for accessing these critical card transaction fields.
The user in this document pertains to both the individual and business user types.
Use Cases
- You want to retrieve the sensitive and transaction-critical details of a specific card, such as the security code and unmasked PAN.
- You want to display a visual image of the card, along with the sensitive data needed to initiate a card transaction, in your application.
Prerequisites
- The user profile risk assessment status must not be
risk_flagged. - The user profile must be in an
activestatus. - The user must have an
approvedKYC or KYB status. - The user account or wallet must be in an
activestatus. - The user card must be in an
activestatus.
API Workflow
1
Retrieve sensitive information via API
Call the Retrieve Card Sensitive Details GET /users/wallets/cards/{{card_id}}/sensitive-data passing the X-Data-Key-ID to retrieve the sensitive card information. The X-Data-Key-ID key will be provided to you alongside the program resources.
Sensitive card information
| The encrypted card number or the PAN |
| The card expiry number |
| The encrypted security code (CVV/CVC) |
Depending on your need, the API has an optional query parameter info_type to only retrieve either pan_info or security_code_info.
Retrieve Card Sensitive Details request
curl --location 'https://{{cards_base_url}}/{{program_code}}/v2/users/wallets/cards/180ac588b91dc6bff6a5d42a671d1d6a/sensitive-data' \
--header 'X-Auth-User-Id: {{user_id}}' \
--header 'X-Auth-User-Id: {{data_key_id}}' \
--header 'Authorization: ••••••'Retrieve Card Sensitive Details response
{
"pan_info": {
"number": "d6Lf1qAYOvgBgxKKuD0FO/L1czVkEZWJwMlLSe0GY1g=",
"date": {
"expiry": "2030-01"
}
},
"security_code_info": {
"value": "RVRdEqm6NV+7+Kar0ismVg=="
}
}2
Decrypt the encrypted card details
An encryption key will also be provided with the program resources. This encryption key is mapped to the X-Data-Key-ID used in the Retrieve Card Sensitive Details.
Encryption Specifications
- Algorithm:
AES-256-CBC - Key Size:
256 bits (32 bytes) - Padding:
PKCS#7 - Initialization Vector (IV): Derived from the first 16 bytes of the
card_id
Decryption Steps
- Derive the IV: Extract the first 16 bytes from the card_id.
- Configure the AES Cipher: Use AES-256-CBC mode with PKCS#7 padding.
- Decrypt the Encrypted Data: Decode the Base64-encoded encrypted string and decrypt using the derived IV and AES key.
Sample decryption code
Go
Go
Golang code snippet to help decrypt the encrypted data
package main
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"errors"
"fmt"
)
// pkcs7Unpadding removes the padding from the decrypted data
func pkcs7Unpadding(data []byte) ([]byte, error) {
paddingLength := int(data[len(data)-1])
if paddingLength > len(data) {
return nil, errors.New("invalid padding size")
}
return data[:len(data)-paddingLength], nil
}
// decryptAES decrypts AES-256-CBC encrypted data
func decryptAES(encryptedData, key, iv []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
if len(encryptedData) < aes.BlockSize {
return nil, errors.New("ciphertext too short")
}
mode := cipher.NewCBCDecrypter(block, iv)
// Decrypt into a new buffer
decrypted := make([]byte, len(encryptedData))
mode.CryptBlocks(decrypted, encryptedData)
// Remove PKCS#7 padding
decrypted, err = pkcs7Unpadding(decrypted)
if err != nil {
return nil, err
}
return decrypted, nil
}
// DecryptBase64AES256CBC decrypts base64-encoded AES-256-CBC ciphertext using the given key and cardHashId as IV
func DecryptBase64AES256CBC(base64Ciphertext, cardHashId, base64Key string) (string, error) {
// Base64 decode the ciphertext
encryptedData, err := base64.StdEncoding.DecodeString(base64Ciphertext)
if err != nil {
return "", err
}
// Base64 decode the key
key, err := base64.StdEncoding.DecodeString(base64Key)
if err != nil {
return "", err
}
if len(key) != 32 {
return "", errors.New("invalid key size: must be 32 bytes for AES-256")
}
// Use the first 16 bytes of the cardHashId as the IV
iv := []byte(cardHashId)[:aes.BlockSize]
if len(iv) != aes.BlockSize {
return "", errors.New("invalid IV size")
}
// Decrypt the data
decryptedData, err := decryptAES(encryptedData, key, iv)
if err != nil {
return " ", err
}
return string(decryptedData), nil
}
func main() {
// Example usage
cardHashId := "your_card_hash_id_here" // Example card identifier (must be at least 16 bytes)
// Original 32-byte key (as a hex string)
originalKey := "your_original_key"
// Convert the hex string to bytes
keyBytes, err := hex.DecodeString(originalKey)
if err != nil {
fmt.Println("Error decoding hex:", err)
}
// Base64 encode the key
base64Key := base64.StdEncoding.EncodeToString(keyBytes)
base64Ciphertext := "your_base64_encrypted_data_here"
// Decrypt the data
decrypted, err := DecryptBase64AES256CBC(base64Ciphertext, cardHashId, base64Key)
if err != nil {
fmt.Println("Error decrypting:", err)
} else {
fmt.Println("Decrypted data:", decrypted)
}
}PHP
PHP
PHP code snippet to help decrypt the encrypted data
<?php
// Decrypt AES-256-CBC encrypted data
function decryptAES($encryptedData, $key, $iv) {
$decrypted = openssl_decrypt($encryptedData, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
if ($decrypted === false) {
throw new Exception('Decryption failed');
}
return $decrypted;
}
// Decrypt Base64-encoded AES-256-CBC ciphertext using the given key and cardHashId as IV
function decryptBase64AES256CBC($base64Ciphertext, $cardHashId, $base64Key) {
// Base64 decode the ciphertext
$encryptedData = base64_decode($base64Ciphertext);
if ($encryptedData === false) {
throw new Exception('Invalid base64 ciphertext');
}
// Base64 decode the key
$key = base64_decode($base64Key);
if ($key === false || strlen($key) !== 32) {
throw new Exception('Invalid key size: must be 32 bytes for AES-256');
}
// Use the first 16 bytes of the cardHashId as the IV
$iv = substr($cardHashId, 0, 16);
if (strlen($iv) !== 16) {
throw new Exception('Invalid IV size');
}
// Decrypt the data
return decryptAES($encryptedData, $key, $iv);
}
// Example usage
try {
$cardHashId = "your_card_hash_id_here"; // Example card identifier (must be at least 16 bytes)
// Original hex string
$originalKey = "your_original_key";
// Convert hex string to byte array
$keyBytes = hex2bin($originalKey);
// Base64 encode the byte array
$base64Key = base64_encode($keyBytes);
$base64Ciphertext = "your_base64_encrypted_data_here";
$decrypted = decryptBase64AES256CBC($base64Ciphertext, $cardHashId, $base64Key);
echo "Decrypted data: $decrypted";
} catch (Exception $e) {
echo 'Error decrypting: ' . $e->getMessage();
}
?>NodeJS
NodeJS
NodeJS code snippet to help decrypt the encrypted data
const crypto = require('crypto');
// Decrypt AES-256-CBC encrypted data
function decryptAES(encryptedData, key, iv) {
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
decipher.setAutoPadding(true); // Ensure auto padding is enabled
let decrypted = Buffer.concat([decipher.update(encryptedData), decipher.final()]);
return decrypted;
}
// Decrypt Base64-encoded AES-256-CBC ciphertext using the given key and cardHashId as IV
function decryptBase64AES256CBC(base64Ciphertext, cardHashId, base64Key) {
try {
// Base64 decode the ciphertext
const encryptedData = Buffer.from(base64Ciphertext, 'base64');
// Base64 decode the key
const key = Buffer.from(base64Key, 'base64');
if (key.length !== 32) {
throw new Error('Invalid key size: must be 32 bytes for AES-256');
}
// Use the first 16 bytes of the cardHashId as the IV
const iv = Buffer.from(cardHashId).slice(0, 16);
if (iv.length !== 16) {
throw new Error('Invalid IV size');
}
// Decrypt the data
const decryptedData = decryptAES(encryptedData, key, iv);
return decryptedData.toString('utf8');
} catch (error) {
console.error('Error decrypting:', error.message);
return '';
}
}
// Example usage
const cardHashId = "your_card_hash_id_here"; // Example card identifier (must be at least 16 bytes)
// Convert hex string to a byte array
// Original 32-byte key (as a hex string)
const originalKey = "your_original_key";
const bytes = [];
for (let i = 0; i < originalKey.length; i += 2) {
bytes.push(parseInt(originalKey.substr(i, 2), 16));
}
// Convert byte array to Base64 string
const byteArray = new Uint8Array(bytes);
let binaryString = '';
for (let i = 0; i < byteArray.length; i++) {
binaryString += String.fromCharCode(byteArray[i]);
}
const base64Key = btoa(binaryString);
const base64Ciphertext = "your_base64_encrypted_data_here";
const decrypted = decryptBase64AES256CBC(base64Ciphertext, cardHashId, base64Key);
console.log('Decrypted data:', decrypted);Java
Java
JAVA code snippet to help decrypt the encrypted data
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class AESDecryption {
// Decrypt AES-256-CBC encrypted data
public static byte[] decryptAES(byte[] encryptedData, byte[] key, byte[] iv) throws Exception {
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
return cipher.doFinal(encryptedData);
}
// Decrypt Base64-encoded AES-256-CBC ciphertext using the given key and cardHashId as IV
public static String decryptBase64AES256CBC(String base64Ciphertext, String cardHashId, String base64Key) throws Exception {
// Base64 decode the ciphertext
byte[] encryptedData = Base64.getDecoder().decode(base64Ciphertext);
// Base64 decode the key
byte[] key = Base64.getDecoder().decode(base64Key);
if (key.length != 32) {
throw new IllegalArgumentException("Invalid key size: must be 32 bytes for AES-256");
}
// Use the first 16 bytes of the cardHashId as the IV
byte[] iv = cardHashId.getBytes("UTF-8");
iv = java.util.Arrays.copyOf(iv, 16); // Ensure the IV is exactly 16 bytes
// Decrypt the data
byte[] decryptedData = decryptAES(encryptedData, key, iv);
return new String(decryptedData, "UTF-8");
}
// Helper function to convert hex string to byte array
public static byte[] hexStringToByteArray(String hexString) {
int len = hexString.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
+ Character.digit(hexString.charAt(i + 1), 16));
}
return data;
}
public static void main(String[] args) {
try {
String cardHashId = "your_card_hash_id_here"; // Example card identifier (must be at least 16 bytes)
// Original hex string
String originalKey = "your_original_key";
// Convert hex string to byte array
byte[] keyBytes = hexStringToByteArray(originalKey);
// Base64 encode the byte array
String base64Key = Base64.getEncoder().encodeToString(keyBytes);
String base64Ciphertext = "your_base64_encrypted_data_here";
String decrypted = decryptBase64AES256CBC(base64Ciphertext, cardHashId, base64Key);
System.out.println("Decrypted data: " + decrypted);
} catch (Exception e) {
System.out.println("Error decrypting: " + e.getMessage());
}
}
}3
Display decrypted card details to the customer
You can display the decrypted card details to your frontend, so the customer can use the details on their online card transaction.
You are recommended to have your own encryption mechanism between server and client to ensure that the information is transmitted in a secure manner from your backend to the client application.
You are also recommended to have a mechanism to automatically mask the information to your end user after a reasonable duration.
You are encouraged to review the guide for the Managing Card Security section.
Related Links
On this page
- Retrieve sensitive card information