Decrypting Card Data Algorithm

This document explains the algorithmic process to decrypt the cardEncryptedData object.


1. Hybrid Encryption Structure

The encryption uses a hybrid approach:

  • RSA (asymmetric): Protects the AES key, IV, and Auth Tag.

  • AES-GCM (symmetric): Encrypts the actual card data.


2. Decryption Steps

Step 1: RSA Decryption

  • The encryptedKey is a base64-encoded string.

  • It contains the AES key, IV, and Auth Tag, all encrypted with the merchant's RSA public key.

  • The private RSA key is used to decrypt encryptedKey, yielding a buffer with:

    • AES Key: First 32 bytes (256 bits)

    • IV: Next 12 bytes (96 bits)

    • Auth Tag: Last 16 bytes (128 bits)

Step 2: Extract AES Parameters

  • AES Key: Used for AES-256-GCM decryption.

  • IV (Initialization Vector): Required for AES-GCM.

  • Auth Tag: Used to verify data integrity in AES-GCM.

Step 3: AES-GCM Decryption

  • The encryptedData is a base64-encoded string, encrypted with AES-256-GCM.

  • Using the extracted AES key, IV, and Auth Tag, the data is decrypted.

  • The output is the original card data in JSON string format.


3. Security Notes

  • RSA ensures only the intended recipient can access the AES key.

  • AES-GCM provides both confidentiality and integrity for the card data.


4. Sample codes (in NodeJS)

import * as crypto from "crypto";
import * as fs from "fs";

const PRIVATE_KEY = fs.readFileSync(`path/to/your/private-key.pem`, "utf8");
const AES_KEY_LENGTH = 32; // 256 bits
const IV_LENGTH = 12; // 96 bits
const AUTH_TAG_LENGTH = 16; // 128 bits

function hybridDecrypt(cardEncryptedData) {
  const { encryptedData, encryptedKey } = cardEncryptedData;
  try {
    // 1. RSA Decryption
    const decryptedKeyBuf = crypto.privateDecrypt(
      {
        key: PRIVATE_KEY,
        padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
        oaepHash: "sha256"
      },
      encryptedKey
    );

    // 2. Extract AES Parameters
    const aesKey = decryptedKeyBuf.subarray(0, AES_KEY_LENGTH);
    const iv = decryptedKeyBuf.subarray(AES_KEY_LENGTH, AES_KEY_LENGTH + IV_LENGTH);
    const authTag = decryptedKeyBuf.subarray(-AUTH_TAG_LENGTH);

    // 3. AES-GCM Decryption
    const decipher = crypto.createDecipheriv("aes-256-gcm", aesKey, iv);
    decipher.setAuthTag(authTag);
    let decrypted = decipher.update(encryptedData, "base64", "utf8");
    decrypted += decipher.final("utf8");
    return decrypted;
  } catch (error) {
    console.log(`call to hybridDecrypt failed with error: ${error.message}`);
    return "{}";
  }
}

// Test with NeoX virtual card encrypted data. Replace with actual encrypted data
const cardEncryptedData = {
  encryptedData:
    "Some encryptedData data here that is base64 encoded",
  encryptedKey:
    "Some encryptedKey data here that is base64 encoded"
};

const result = hybridDecrypt(cardEncryptedData);
console.log("result", JSON.parse(result));

Last updated

Was this helpful?