JavaScript
This guide shows you how to decrypt sensitive card details using Node.js. The decryption process uses Rivest-Shamir-Adleman Optimal Asymmetric Encryption Padding (RSA-OAEP) to unwrap the Advanced Encryption Standard (AES) key. Then Advanced Encryption Standard 256-bit Galois/Counter Mode (AES-256-GCM) is used to decrypt the card data.
Prerequisites
Before you begin, you need:
- Node.js installed on your system
- Your RSA private key, which was generated when you created the key pair
- An encrypted response from the View card sensitive details endpoint or the Get card PIN endpoint
Understanding the encrypted response
The View card sensitive details and the Get card PIN endpoints return three base64-encoded values:
| Field | Description |
|---|---|
| encrypted_key | AES-256 key encrypted with your RSA public key |
| nonce | 12-byte initialization vector for AES-GCM decryption |
| ciphertext | Encrypted card details |
Decode the base64 values
Convert all three base64-encoded values from the API response into buffers:
const encryptedKey = Buffer.from(payload.encrypted_key, "base64");
const nonce = Buffer.from(payload.nonce, "base64");
const ciphertext = Buffer.from(payload.ciphertext, "base64");
Decrypt the AES key
Use your RSA private key to decrypt the AES key. The decryption uses RSA-OAEP padding with SHA-256:
const aesKey = crypto.privateDecrypt(
{
key: privateKeyPem,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "sha256",
},
encryptedKey
);
Separate the authentication tag
The last 16 bytes of the ciphertext contain the GCM authentication tag. Separate this from the encrypted data:
const authTag = ciphertext.slice(ciphertext.length - 16);
const encryptedData = ciphertext.slice(0, ciphertext.length - 16);
Decrypt the card details
Create an AES-256-GCM decipher, set the authentication tag, and decrypt the card details:
const decipher = crypto.createDecipheriv("aes-256-gcm", aesKey, nonce);
decipher.setAuthTag(authTag);
let decrypted = decipher.update(encryptedData, null, "utf8");
decrypted += decipher.final("utf8");
Complete decryption code
The following example combines all the steps above into a single reusable function, with error handling:
import crypto from "crypto";
export function decryptCardDetails(privateKeyPem, payload) {
try {
// Step 1: Decode base64 values
const encryptedKey = Buffer.from(payload.encrypted_key, "base64");
const nonce = Buffer.from(payload.nonce, "base64");
const ciphertext = Buffer.from(payload.ciphertext, "base64");
// Step 2: Decrypt the AES key using RSA-OAEP
const aesKey = crypto.privateDecrypt(
{
key: privateKeyPem,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "sha256",
},
encryptedKey
);
// Step 3: Separate authentication tag from ciphertext
const authTag = ciphertext.slice(ciphertext.length - 16);
const encryptedData = ciphertext.slice(0, ciphertext.length - 16);
// Step 4: Decrypt using AES-256-GCM
const decipher = crypto.createDecipheriv("aes-256-gcm", aesKey, nonce);
decipher.setAuthTag(authTag);
let decrypted = decipher.update(encryptedData, null, "utf8");
decrypted += decipher.final("utf8");
return decrypted;
} catch (err) {
throw new Error("Decryption failed: " + err.message);
}
}
Usage example
The following example shows how you can pass your PEM-formatted private key and the encrypted payload from the API response to the decryptCardDetails function:
const privateKey = `
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA5llQr/KNtRhTn+2LJMwd5H62QUdMZ8Gq25LDpZRaosYbsoxH
... your private key content ...
-----END RSA PRIVATE KEY-----
`;
// Response from the View card sensitive details endpoint
const encryptedPayload = {
ciphertext: "kJR3z/bVTMTjiXMIQ8ha/eN/69q5vMr1sm6o1iKnCOixCB98tvp4WNsh9YCFXqNEPWfTkuiQ",
encrypted_key: "zmhGe7OkCl2e5lNaSU6rw28u1UvzqWyncHc+4fKfn+GRBua+zleDdaMfHctTPqK0...",
nonce: "W5lMRrL2yKSyWgfq"
};
const cardDetails = decryptCardDetails(privateKey, encryptedPayload);
console.log("Card details:", cardDetails);