> ## Documentation Index
> Fetch the complete documentation index at: https://rain-sandbox-trial.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Manually Implementing Encryption and Decryption

You can encrypt and decrypt card details using cryptographic functions. This method ensures only authorized users can securely retrieve full card details (PAN and CVC).

<Warning>
  #### Security best practices

  * Never store decrypted card details.
  * Only request full card details when absolutely necessary.
  * Always use the latest encryption libraries to maintain security.
</Warning>

Follow the steps below to generate a session ID, send an API request, and decrypt the response manually.

## Step 1: Generating the session ID

Use the `generateSessionID` method to generate the `SessionId`. This ensures that only the correct user can decrypt the data. You'll need the [public RSA key for your environment](/docs/resource-sessionid-keys):

### Requirements

* The secret must be a 32-character hexadecimal string with no spaces or dashes.
* The encryption must use RSA-OAEP padding.

### Example session ID generation

```javascript theme={null}
async function generateSessionId(pem, secret) {
  if (!pem) throw new Error("pem is required");
  if (secret && !/^[0-9A-Fa-f]+$/.test(secret)) {
    throw new Error("secret must be a hex string");
  }

  const secretKey = secret ?? window.crypto.randomUUID().replace(/-/g, "");
  const hbytes = [];
  for (let i = 0; i < secretKey.length; i += 2) {
    hbytes.push(parseInt(secretKey.substr(i, 2), 16));
  }
  const byteArray = new Uint8Array(hbytes);
  let hbinary = "";
  byteArray.forEach((byte) => (hbinary += String.fromCharCode(byte)));
  const secretKeyBase64 = window.btoa(hbinary);

  // fetch the part of the PEM string between header and footer
  const pemHeader = "-----BEGIN PUBLIC KEY-----";
  const pemFooter = "-----END PUBLIC KEY-----";
  const pemContents = pem.substring(
    pemHeader.length,
    pem.length - pemFooter.length - 1,
  );
  // base64 decode the string to get the binary data
  const binaryDerString = window.atob(pemContents);
  // convert from a binary string to an ArrayBuffer
  const buf = new ArrayBuffer(binaryDerString.length);
  const bufView = new Uint8Array(buf);
  for (let i = 0, strLen = binaryDerString.length; i < strLen; i++) {
    bufView[i] = binaryDerString.charCodeAt(i);
  }
  const binaryDer = buf;

  const rsaPublicKey = await window.crypto.subtle.importKey(
    "spki",
    binaryDer,
    {
      name: "RSA-OAEP",
      hash: "SHA-1",
    },
    true,
    ["encrypt"],
  );

  const encryptedArrayBuffer = await window.crypto.subtle.encrypt(
    {
      name: "RSA-OAEP",
    },
    rsaPublicKey,
    new TextEncoder().encode(secretKeyBase64),
  );
  let binary = "";
  const bytes = new Uint8Array(encryptedArrayBuffer);
  const len = bytes.byteLength;
  for (let i = 0; i < len; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  const sessionId = window.btoa(binary);

  return {
    secretKey,
    sessionId,
  };
}
```

* The `sessionId` is required for making an API request.
* The `secretKey` will be needed for decryption later.

## Step 2: Sending the API request

Once the `sessionId` is generated, you can send a request to retrieve encrypted card details. Send a request to the [get a card's encrypted data](/reference/cards/get-a-cards-encrypted-data) endpoint.

### Example API request

```curl theme={null}
curl --request GET \
     --url https://api-dev.rain.xyz/v1/issuing/cards/cardId/secrets \
     --header 'SessionId: sessionId' \
     --header 'accept: application/json'
```

### Example API response

The API will return the encrypted card number (PAN) and CVC, each with an initialization vector (IV) for decryption.

```json theme={null}
{
  "encryptedPan": {
    "iv": "base64_iv_string",
    "data": "base64_encrypted_pan"
  },
  "encryptedCvc": {
    "iv": "base64_iv_string",
    "data": "base64_encrypted_cvc"
  }
}
```

## Step 3: Decrypting the encrypted card data

To decrypt the received encrypted card details, use AES-128-GCM decryption.

### Example card data decryption

```javascript theme={null}
async function decryptSecret(base64Secret, base64Iv, secretKey) {
  if (!base64Secret) throw new Error("base64Secret is required");
  if (!base64Iv) throw new Error("base64Iv is required");
  if (!secretKey || !/^[0-9A-Fa-f]+$/.test(secretKey)) {
    throw new Error("secretKey must be a hex string");
  }

  const secret = Uint8Array.from(window.atob(base64Secret), (c) =>
    c.charCodeAt(0),
  );
  const iv = Uint8Array.from(window.atob(base64Iv), (c) => c.charCodeAt(0));
  const secretKeyArrayBuffer = Uint8Array.from(
    secretKey.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)),
  );

  const cryptoKey = await window.crypto.subtle.importKey(
    "raw",
    secretKeyArrayBuffer,
    { name: "AES-GCM" },
    false,
    ["decrypt"],
  );

  const decrypted = await window.crypto.subtle.decrypt(
    {
      name: "AES-GCM",
      iv: iv,
    },
    cryptoKey,
    secret,
  );

  return new TextDecoder().decode(decrypted);
}
```

### Example final output

```javascript theme={null}
const decryptedCardNumber = await decryptSecret(data.encryptedPan.data, data.encryptedPan.iv, secretKey);
const decryptedCVC = await decryptSecret(data.encryptedCvc.data, data.encryptedCvc.iv, secretKey);

console.log("Decrypted Card Number:", decryptedCardNumber);
console.log("Decrypted CVC:", decryptedCVC);
```
