# Card Encryption

## Flow

<figure><img src="/files/xZgJ1Rt1QdDx94spWxsf" alt=""><figcaption></figcaption></figure>

## Encryption Code&#x20;

{% tabs %}
{% tab title="Encryption UI" %}
Download and simulate the encryption without installing a programming language

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Card Encryption</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 600px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
        }

        .container {
            background-color: white;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }

        h1 {
            color: #333;
            text-align: center;
            margin-bottom: 30px;
        }

        .form-group {
            margin-bottom: 20px;
        }

        label {
            display: block;
            margin-bottom: 5px;
            color: #555;
            font-weight: bold;
        }

        input[type="text"],
        input[type="number"],
        textarea {
            width: 100%;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 5px;
            font-size: 14px;
            box-sizing: border-box;
        }

        input[type="text"]:focus,
        input[type="number"]:focus,
        textarea:focus {
            outline: none;
            border-color: #4CAF50;
        }

        .row {
            display: flex;
            gap: 15px;
        }

        .col {
            flex: 1;
        }

        .btn {
            background-color: #4CAF50;
            color: white;
            padding: 12px 30px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            font-size: 16px;
            width: 100%;
            margin-top: 20px;
        }

        .btn:hover {
            background-color: #45a049;
        }

        .btn:disabled {
            background-color: #ccc;
            cursor: not-allowed;
        }

        .result {
            margin-top: 20px;
            padding: 15px;
            background-color: #f9f9f9;
            border-radius: 5px;
            border: 1px solid #ddd;
        }

        .error {
            color: #d32f2f;
            background-color: #ffebee;
            border-color: #f8bbd9;
        }

        .success {
            color: #388e3c;
            background-color: #e8f5e8;
            border-color: #c8e6c9;
        }

        .public-key-section {
            margin-bottom: 30px;
            padding: 20px;
            background-color: #f8f9fa;
            border-radius: 5px;
        }

        .card-form {
            border: 1px solid #ddd;
            padding: 20px;
            border-radius: 5px;
            background-color: #fafafa;
        }

        .loading {
            display: none;
            text-align: center;
            margin-top: 10px;
        }

        .spinner {
            border: 4px solid #f3f3f3;
            border-top: 4px solid #3498db;
            border-radius: 50%;
            width: 30px;
            height: 30px;
            animation: spin 1s linear infinite;
            margin: 0 auto;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Card Encryption</h1>
        
        <!-- Public Key Input Section -->
        <div class="public-key-section">
            <div class="form-group">
                <label for="publicKey">RSA Public Key (Base64 PKIX format):</label>
                <textarea id="publicKey" rows="8" placeholder="Enter your Base64-encoded RSA public key here..."></textarea>
            </div>
        </div>

        <!-- Card Details Form -->
        <div class="card-form">
            <h3>Card Holder Details</h3>
            <form id="cardForm">
                <div class="form-group">
                    <label for="cardNumber">Card Number:</label>
                    <input type="text" id="cardNumber" placeholder="1234 5678 9012 3456" maxlength="19" required>
                </div>

                <div class="form-group">
                    <label for="cardholderName">Cardholder Name:</label>
                    <input type="text" id="cardholderName" placeholder="John Doe" required>
                </div>

                <div class="row">
                    <div class="col">
                        <div class="form-group">
                            <label for="expiryMonth">Expiry Month:</label>
                            <input type="number" id="expiryMonth" min="1" max="12" placeholder="MM" required>
                        </div>
                    </div>
                    <div class="col">
                        <div class="form-group">
                            <label for="expiryYear">Expiry Year:</label>
                            <input type="number" id="expiryYear" min="01" max="99" placeholder="YY" required>
                        </div>
                    </div>
                </div>

                <div class="form-group">
                    <label for="cvv">CVV:</label>
                    <input type="text" id="cvv" placeholder="123" maxlength="4">
                </div>

                <button type="submit" class="btn" id="encryptBtn">Encrypt Card Details</button>
            </form>
        </div>

        <!-- Loading Indicator -->
        <div class="loading" id="loadingIndicator">
            <div class="spinner"></div>
            <p>Encrypting...</p>
        </div>

        <!-- Result Display -->
        <div id="result" class="result" style="display: none;"></div>
    </div>

    <script>
        // Hybrid Encryption Class
        class HybridEncryption {
            /**
             * Encrypts plaintext using hybrid encryption (RSA-OAEP + AES-GCM)
             * @param {string} plaintext - The text to encrypt
             * @param {string} base64PublicKey - Base64-encoded PKIX/SubjectPublicKeyInfo public key
             * @returns {Promise<string>} Base64-encoded encrypted response
             */
            static async encryptHybrid(plaintext, base64PublicKey) {
                try {
                    // Decode the base64 public key
                    const publicKeyBytes = this.base64ToArrayBuffer(base64PublicKey);

                    // Import the public key
                    const publicKey = await crypto.subtle.importKey(
                        'spki', // PKIX/SubjectPublicKeyInfo format
                        publicKeyBytes,
                        { name: 'RSA-OAEP',hash: 'SHA-256' },
                        false,
                        ['encrypt']
                    );

                    // Generate 32-byte AES key
                    const aesKey = await crypto.subtle.generateKey(
                        { name: 'AES-GCM', length: 256 }, true, ['encrypt']
                    );

                    // Generate 12-byte nonce for AES-GCM
                    const nonce = crypto.getRandomValues(new Uint8Array(12));

                    // Encrypt plaintext with AES-GCM
                    const plaintextBuffer = new TextEncoder().encode(plaintext);
                    const ciphertext = await crypto.subtle.encrypt(
                        { name: 'AES-GCM', iv: nonce }, aesKey, plaintextBuffer
                    );

                    // Export AES key as raw bytes
                    const aesKeyBytes = await crypto.subtle.exportKey('raw', aesKey);

                    // Encrypt AES key with RSA-OAEP
                    const encryptedKey = await crypto.subtle.encrypt(
                        { name: 'RSA-OAEP' }, publicKey, aesKeyBytes
                    );

                    // Create response payload
                    const payload = {
                        encryptedKey: this.arrayBufferToBase64(encryptedKey),
                        nonce: this.arrayBufferToBase64(nonce),
                        ciphertext: this.arrayBufferToBase64(ciphertext)
                    };

                    // Convert to JSON and encode as base64
                    const jsonString = JSON.stringify(payload);
                    const jsonBuffer = new TextEncoder().encode(jsonString);

                    return this.arrayBufferToBase64(jsonBuffer);

                } catch (error) {
                    throw new Error(`Encryption failed: ${error.message}`);
                }
            }

            /**
             * Convert base64 string to ArrayBuffer
             * @param {string} base64 - Base64 string
             * @returns {ArrayBuffer} ArrayBuffer
             */
            static base64ToArrayBuffer(base64) {
                const binaryString = atob(base64);
                const bytes = new Uint8Array(binaryString.length);
                for (let i = 0; i < binaryString.length; i++) {
                    bytes[i] = binaryString.charCodeAt(i);
                }
                return bytes.buffer;
            }

            /**
             * Convert ArrayBuffer to base64 string
             * @param {ArrayBuffer} buffer - ArrayBuffer
             * @returns {string} Base64 string
             */
            static arrayBufferToBase64(buffer) {
                const bytes = new Uint8Array(buffer);
                let binary = '';
                for (let i = 0; i < bytes.byteLength; i++) {
                    binary += String.fromCharCode(bytes[i]);
                }
                return btoa(binary);
            }
        }

        // Make it available globally
        window.HybridEncryption = HybridEncryption;

        // UI Event Handlers
        document.addEventListener('DOMContentLoaded', function() {
            const cardForm = document.getElementById('cardForm');
            const encryptBtn = document.getElementById('encryptBtn');
            const loadingIndicator = document.getElementById('loadingIndicator');
            const resultDiv = document.getElementById('result');

            // Format card number input
            document.getElementById('cardNumber').addEventListener('input', function(e) {
                let value = e.target.value.replace(/\D/g, '');
                value = value.replace(/(\d{4})(?=\d)/g, '$1 ');
                e.target.value = value;
            });

            // Format CVV input (numbers only)
            document.getElementById('cvv').addEventListener('input', function(e) {
                e.target.value = e.target.value.replace(/\D/g, '');
            });

            // Handle form submission
            cardForm.addEventListener('submit', async function(e) {
                e.preventDefault();
                
                const publicKey = document.getElementById('publicKey').value.trim();
                const cardNumber = document.getElementById('cardNumber').value.replace(/\s/g, '');
                const cardholderName = document.getElementById('cardholderName').value.trim();
                const expiryMonth = document.getElementById('expiryMonth').value;
                const expiryYear = document.getElementById('expiryYear').value;
                const cvv = document.getElementById('cvv').value;

                // Validation
                if (!publicKey) {
                    showResult('Please enter a valid RSA public key.', 'error');
                    return;
                }

                if (!cardNumber || cardNumber.length < 13 || cardNumber.length > 19) {
                    showResult('Please enter a valid card number.', 'error');
                    return;
                }

                if (!cardholderName) {
                    showResult('Please enter the cardholder name.', 'error');
                    return;
                }

                if (!expiryMonth || expiryMonth < 1 || expiryMonth > 12) {
                    showResult('Please enter a valid expiry month (1-12).', 'error');
                    return;
                }

                if (!expiryYear || expiryYear < 25) {
                    showResult('Please enter a valid expiry year.', 'error');
                    return;
                }

                // Prepare card data for encryption
                const payload = {
                    card: {
                        number: cardNumber,
                        expiryMonth: expiryMonth.padStart(2, '0'),
                        expiryYear: expiryYear,
                        cvc: cvv,
                        nameOnCard: cardholderName,
                    },
                    deviceInformations: {
                        type: "",
                        userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
                        ipAddress: "254.254.254.254",
                        acceptLanguage: "EN",
                        cookieToken: "ZU_0oRV1S3D95Rz06Q1Aa0RTeOlgdXWKeVvZEk7k3LM=",
                        deviceId: "067783c8-29ac-4684-8aa8-71c05ab346df",
                        browserWidth: "1234",
                        browserHeight: "1234",
                        country: "ID"
                    },
                    metadata: {}
                };

                const plaintextData = JSON.stringify(payload);

                // Show loading
                showLoading(true);
                
                try {
                    // Encrypt the card data
                    const encryptedData = await HybridEncryption.encryptHybrid(plaintextData, publicKey);
                    
                    showResult(`
                        <h4>Encryption Successful!</h4>
                        <p><strong>Encrypted Data:</strong></p>
                        <textarea readonly style="width: 100%; height: 100px; font-family: monospace; font-size: 12px;">${encryptedData}</textarea>
                        <p><strong>Original Data:</strong></p>
                        <pre style="background: #f0f0f0; padding: 10px; border-radius: 3px; font-size: 12px;">${JSON.stringify(payload, null, 2)}</pre>
                    `, 'success');
                } catch (error) {
                    showResult(`Encryption failed: ${error.message}`, 'error');
                } finally {
                    showLoading(false);
                }
            });

            function showLoading(show) {
                loadingIndicator.style.display = show ? 'block' : 'none';
                encryptBtn.disabled = show;
            }

            function showResult(message, type) {
                resultDiv.innerHTML = message;
                resultDiv.className = `result ${type}`;
                resultDiv.style.display = 'block';
                resultDiv.scrollIntoView({ behavior: 'smooth' });
            }
        });
    </script>
</body>
</html>
```

{% endtab %}

{% tab title="Typescript / Javascript" %}

```typescript
// Hybrid Encryption Class
class HybridEncryption {
    /**
     * Encrypts plaintext using hybrid encryption (RSA-OAEP + AES-GCM)
     * @param {string} plaintext - The text to encrypt
     * @param {string} base64PublicKey - Base64-encoded PKIX/SubjectPublicKeyInfo public key
     * @returns {Promise<string>} Base64-encoded encrypted response
     */
    static async encryptHybrid(plaintext, base64PublicKey) {
        try {
            // Decode the base64 public key
            const publicKeyBytes = this.base64ToArrayBuffer(base64PublicKey);

            // Import the public key
            const publicKey = await crypto.subtle.importKey(
                "spki", // PKIX/SubjectPublicKeyInfo format
                publicKeyBytes,
                { name: "RSA-OAEP", hash: "SHA-256" },
                false,
                ["encrypt"]
            );

            // Generate 32-byte AES key
            const aesKey = await crypto.subtle.generateKey(
                { name: "AES-GCM", length: 256 },
                true,
                ["encrypt"]
            );

            // Generate 12-byte nonce for AES-GCM
            const nonce = crypto.getRandomValues(new Uint8Array(12));

            // Encrypt plaintext with AES-GCM
            const plaintextBuffer = new TextEncoder().encode(plaintext);
            const ciphertext = await crypto.subtle.encrypt(
                { name: "AES-GCM", iv: nonce },
                aesKey,
                plaintextBuffer
            );

            // Export AES key as raw bytes
            const aesKeyBytes = await crypto.subtle.exportKey("raw", aesKey);

            // Encrypt AES key with RSA-OAEP
            const encryptedKey = await crypto.subtle.encrypt(
                { name: "RSA-OAEP" },
                publicKey,
                aesKeyBytes
            );

            // Create response payload
            const payload = {
                encryptedKey: this.arrayBufferToBase64(encryptedKey),
                nonce: this.arrayBufferToBase64(nonce),
                ciphertext: this.arrayBufferToBase64(ciphertext),
            };

            // Convert to JSON and encode as base64
            const jsonString = JSON.stringify(payload);
            const jsonBuffer = new TextEncoder().encode(jsonString);

            return this.arrayBufferToBase64(jsonBuffer);
        } catch (error) {
            throw new Error(`Encryption failed: ${error.message}`);
        }
    }

    /**
     * Convert base64 string to ArrayBuffer
     * @param {string} base64 - Base64 string
     * @returns {ArrayBuffer} ArrayBuffer
     */
    static base64ToArrayBuffer(base64) {
        const binaryString = atob(base64);
        const bytes = new Uint8Array(binaryString.length);
        for (let i = 0; i < binaryString.length; i++) {
            bytes[i] = binaryString.charCodeAt(i);
        }
        return bytes.buffer;
    }

    /**
     * Convert ArrayBuffer to base64 string
     * @param {ArrayBuffer} buffer - ArrayBuffer
     * @returns {string} Base64 string
     */
    static arrayBufferToBase64(buffer) {
        const bytes = new Uint8Array(buffer);
        let binary = "";
        for (let i = 0; i < bytes.byteLength; i++) {
            binary += String.fromCharCode(bytes[i]);
        }
        return btoa(binary);
    }
}
```

{% endtab %}

{% tab title="Golang" %}

```go
package main

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
)

type DataEncryption struct {
	EncryptedKey string `json:"encryptedKey"`
	Nonce        string `json:"nonce"`
	Ciphertext   string `json:"ciphertext"`
}

func EncryptDataWithRSAHybrid(plaintext, base64PublicKey string) (string, error) {
	publicKeyBytes, err := base64.StdEncoding.DecodeString(base64PublicKey)
	if err != nil {
		return "", fmt.Errorf("failed to decode base64 public key: %w", err)
	}

	pub, err := x509.ParsePKIXPublicKey(publicKeyBytes)
	if err != nil {
		return "", fmt.Errorf("failed to parse public key: %w", err)
	}
	publicKey, ok := pub.(*rsa.PublicKey)
	if !ok {
		return "", errors.New("not an RSA public key")
	}

	aesKey := make([]byte, 32)
	if _, err := rand.Read(aesKey); err != nil {
		return "", fmt.Errorf("failed to generate aes key: %w", err)
	}

	block, err := aes.NewCipher(aesKey)
	if err != nil {
		return "", fmt.Errorf("failed to create aes cipher: %w", err)
	}

	nonce := make([]byte, 12)
	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
		return "", fmt.Errorf("failed to generate nonce: %w", err)
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", fmt.Errorf("failed to create aes gcm: %w", err)
	}

	ciphertext := gcm.Seal(nil, nonce, []byte(plaintext), nil)

	encryptedKey, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, publicKey, aesKey, nil)
	if err != nil {
		return "", fmt.Errorf("failed to encrypt aes gcm key: %w", err)
	}

	payload := DataEncryption{
		EncryptedKey: base64.StdEncoding.EncodeToString(encryptedKey),
		Nonce:        base64.StdEncoding.EncodeToString(nonce),
		Ciphertext:   base64.StdEncoding.EncodeToString(ciphertext),
	}
	raw, _ := json.Marshal(payload)

	return base64.StdEncoding.EncodeToString(raw), nil
}

func main() {
	payload := `{
"card": {
	"number": "4440000112200001",
	"expiryMonth": "01",
	"expiryYear": "29",
	"cvc": "123",
	"nameOnCard": "John Doe"
},
"deviceInformations": {
	"type": "",
	"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
	"ipAddress": "182.253.147.99",
	"acceptLanguage": "EN",
	"cookieToken": "ZU_0oRV1S3D95Rz06Q1Aa0RTeOlgdXWKeVvZEk7k3LM=",
	"deviceId": "067783c8-29ac-4684-8aa8-71c05ab346df",
	"browserWidth": "1234",
	"browserHeight": "1234",
	"country": "ID"
},
"metadata": {}
}`

	publicKey := `MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwtAVSVluXY/8QEnXRuAqJ97+ZbAQprE5RtQ8cWlXfznGpTvtbcYEF7tooa4WXnPrDaR0ETcRQlNhP/qYDc3ODRC1w57TLbZL+Wtm4kc3VsWEyM8lklxFV02fiqMaDz+solgeHMOsiwMShTd8FqQ+OnCgVB+R1n1v+bbFZ3EzraIjGzbiffQwyNoCtLO+S3THQRTcDX/KjjANFejjCezGeLZGOUtRFrnhZ4k0x7/xV+9cn3ETuqDuZGJ1Hu7uOswD170N3nMVdrNHv2qn4qkGKjHwngL9sP+xaJpYkzeiMyQxT8VuRe6TdyCpbQGwVIepHTYBGMkfWeXT0LFeh5XUgwIDAQAB`

	encryptedCard, err := EncryptDataWithRSAHybrid(payload, publicKey)
	if err != nil {
		fmt.Println("Encryption Error:", err)
		return
	}
	fmt.Println("Encrypted base64:", encryptedCard)
}
```

{% endtab %}

{% tab title="PHP" %}
{% hint style="info" %}
PHP minimum version 8.0+
{% endhint %}

```php
<?php

require_once 'vendor/autoload.php';

use phpseclib3\Crypt\RSA;
use phpseclib3\Crypt\PublicKeyLoader;

/**
 * Performs hybrid encryption using AES-GCM and RSA-OAEP
 * 
 * @param string $plaintext The text to encrypt
 * @param string $publicKeyBase64 Base64-encoded PKIX format public key
 * @return string Base64-encoded JSON result containing encrypted components
 * @throws Exception If encryption fails or invalid parameters provided
 */
function hybridEncrypt(string $plaintext, string $publicKeyBase64): string
{   
    // Decode and validate the public key
    $publicKeyPem = base64_decode($publicKeyBase64, true);
    if ($publicKeyPem === false) {
        throw new InvalidArgumentException('Invalid Base64 public key');
    }
    
    // Convert DER to PEM format if needed
    if (strpos($publicKeyPem, '-----BEGIN') === false) {
        $publicKeyPem = "-----BEGIN PUBLIC KEY-----\n" . 
                       chunk_split(base64_encode($publicKeyPem), 64, "\n") . 
                       "-----END PUBLIC KEY-----\n";
    }
    
    $publicKey = openssl_pkey_get_public($publicKeyPem);
    if ($publicKey === false) {
        throw new InvalidArgumentException('Invalid public key format');
    }
    
    try {
        // Generate secure random AES key
        $aesKey = random_bytes(32);
        
        // Generate secure random nonce for AES-GCM
        $nonce = random_bytes(12);
        
        // Encrypt plaintext using AES-GCM
        $tag = '';
        $ciphertext = openssl_encrypt(
            $plaintext, 'aes-256-gcm', $aesKey, OPENSSL_RAW_DATA, $nonce, $tag
        );
        if ($ciphertext === false) {
            throw new RuntimeException('AES-GCM encryption failed');
        }
        
        // Combine ciphertext and authentication tag
        $encryptedData = $ciphertext . $tag;
        
        // Encrypt AES key using RSA-OAEP with SHA-256
        $key = PublicKeyLoader::load($publicKeyPem)
            ->withPadding(RSA::ENCRYPTION_OAEP)
            ->withHash('sha256')
            ->withMGFHash('sha256');
        $encryptedAesKey = $key->encrypt($aesKey);
        if ($encryptedAesKey === false) {
            throw new RuntimeException('RSA-OAEP encryption failed');
        }
        
        // Construct JSON object
        $result = [
            'encryptedKey' => base64_encode($encryptedAesKey),
            'nonce' => base64_encode($nonce),
            'ciphertext' => base64_encode($encryptedData)
        ];
        
        // Encode JSON as Base64
        $jsonString = json_encode($result, JSON_THROW_ON_ERROR);
        return base64_encode($jsonString);

    } catch (Exception $e) {
        throw new RuntimeException('Encryption failed: ' . $e->getMessage(), 0, $e);
    }
}
?>
```

Dependency

```php
composer require phpseclib/phpseclib:~3.0
```

{% endtab %}

{% tab title="Java" %}

```java
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import java.security.*;
import java.security.spec.X509EncodedKeySpec;
import java.security.spec.MGF1ParameterSpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import com.google.gson.Gson;

public class HybridEncryption {

    private static final int AES_KEY_SIZE = 256;
    private static final int GCM_NONCE_LENGTH = 12;
    private static final int GCM_TAG_LENGTH = 128;

    public static String encryptHybrid(String plaintext, String base64PublicKey) throws Exception {
        // Decode the public key
        byte[] decodedKey = Base64.getDecoder().decode(base64PublicKey);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));

        // Generate 256-bit AES key
        KeyGenerator keyGen = KeyGenerator.getInstance("AES");
        keyGen.init(AES_KEY_SIZE);
        SecretKey aesKey = keyGen.generateKey();

        // Generate 12-byte nonce (IV)
        byte[] nonce = new byte[GCM_NONCE_LENGTH];
        SecureRandom secureRandom = new SecureRandom();
        secureRandom.nextBytes(nonce);

        // AES-GCM encryption
        Cipher aesCipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH, nonce); // 128-bit tag
        aesCipher.init(Cipher.ENCRYPT_MODE, aesKey, gcmSpec);
        byte[] ciphertext = aesCipher.doFinal(plaintext.getBytes("UTF-8"));

        // Encrypt AES key with RSA-OAEP (SHA-256)
        Cipher rsaCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
        OAEPParameterSpec oaepParams = new OAEPParameterSpec(
            "SHA-256",
            "MGF1",
            MGF1ParameterSpec.SHA256,
            PSource.PSpecified.DEFAULT
        );
        rsaCipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParams);
        byte[] encryptedKey = rsaCipher.doFinal(aesKey.getEncoded());

        // Build the payload
        Map<String, String> payload = new HashMap<>();
        payload.put("encryptedKey", Base64.getEncoder().encodeToString(encryptedKey));
        payload.put("nonce", Base64.getEncoder().encodeToString(nonce));
        payload.put("ciphertext", Base64.getEncoder().encodeToString(ciphertext));

        // Convert to JSON
        Gson gson = new Gson();
        String json = gson.toJson(payload);

        // Return Base64-encoded JSON string
        return Base64.getEncoder().encodeToString(json.getBytes("UTF-8"));
    }
}
```

Maven Dependency

```java
<dependency>
  <groupId>com.google.code.gson</groupId>
  <artifactId>gson</artifactId>
  <version>2.11.0</version>
</dependency>
```

{% endtab %}

{% tab title="Python" %}

```python
import os, json, base64

from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.ciphers.aead import AESGCM


def encrypt_hybrid(plaintext: str, base64_public_key: str) -> str:
    # Load RSA public key from base64 (PKIX format)
    public_key_bytes = base64.b64decode(base64_public_key)
    public_key = serialization.load_der_public_key(public_key_bytes)

    # Generate AES key (256-bit) and nonce (12 bytes)
    aes_key = os.urandom(32)  # 256 bits
    nonce = os.urandom(12)    # 96 bits

    # Encrypt plaintext with AES-GCM
    aesgcm = AESGCM(aes_key)
    ciphertext = aesgcm.encrypt(nonce, plaintext.encode(), None)

    # Encrypt AES key with RSA-OAEP using SHA-256
    encrypted_key = public_key.encrypt(
        aes_key,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None
        )
    )

    # Create JSON object
    payload = {
        "encryptedKey": base64.b64encode(encrypted_key).decode(),
        "nonce": base64.b64encode(nonce).decode(),
        "ciphertext": base64.b64encode(ciphertext).decode()
    }

    # Encode the JSON object as base64 string
    json_bytes = json.dumps(payload).encode()

    return base64.b64encode(json_bytes).decode()
```

Dependency&#x20;

```python
pip install cryptography
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://pivot-payment.gitbook.io/pivot-docs/api-references/api-lists/payments/card-encryption.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
