Get PAN Public Key
GET
/v1/rest/keys/pan
Description: Retrieves the public encryption key used for encrypting Primary Account Numbers (PANs) - credit card numbers and bank account numbers.
What is this endpoint used for?
This endpoint provides the encryption key specifically for protecting account numbers:
- Credit Card Numbers: Encrypt 13-19 digit card numbers (Visa, Mastercard, Amex, Discover, etc.)
- Bank Account Numbers: Encrypt checking and savings account numbers for ACH transactions
- PCI Compliance: Ensures sensitive cardholder data is encrypted before transmission
- Lightweight Option: Use this endpoint if you only need to encrypt account numbers (not CVV codes)
Important: This key is ONLY for encrypting account numbers (credit cards or bank accounts). Do NOT use this key to encrypt CVV/CVV2 security codes - use the SAD key from
/v1/rest/keys/sad for that purpose.
Authentication
This endpoint requires bearer token authentication using the Authorization header.
Note: The bearer token must be a valid Cognito token obtained through the OAuth2 authentication flow.
Code Examples
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
public class PublicKey
{
[JsonProperty("keyId")]
public string KeyId { get; set; }
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("exponent")]
public string Exponent { get; set; }
[JsonProperty("modulus")]
public string Modulus { get; set; }
}
public class PanKeyClient
{
private readonly HttpClient _httpClient;
private readonly string _baseUrl = "https://your-api-domain.com";
public PanKeyClient()
{
_httpClient = new HttpClient();
_httpClient.BaseAddress = new Uri(_baseUrl);
}
public async Task<PublicKey> GetPanKeyAsync()
{
try
{
// Make the GET request
HttpResponseMessage response = await _httpClient.GetAsync("/v1/rest/keys/pan");
// Ensure the request was successful
response.EnsureSuccessStatusCode();
// Read and deserialize the response
string responseBody = await response.Content.ReadAsStringAsync();
PublicKey panKey = JsonConvert.DeserializeObject<PublicKey>(responseBody);
return panKey;
}
catch (HttpRequestException e)
{
Console.WriteLine($"Request error: {e.Message}");
throw;
}
}
// Example: Encrypt a credit card number
public string EncryptCardNumber(string cardNumber, PublicKey panKey)
{
// Use RSA encryption library to encrypt with the public key
// This is a placeholder - implement actual RSA encryption
// with PKCS#1 v1.5 padding using your preferred crypto library
// Example using BouncyCastle or System.Security.Cryptography
// return RsaEncrypt(cardNumber, panKey.Key);
throw new NotImplementedException("Implement RSA encryption here");
}
}
// Example usage:
var client = new PanKeyClient();
PublicKey panKey = await client.GetPanKeyAsync();
Console.WriteLine($"PAN Key ID: {panKey.KeyId}");
// Encrypt a credit card number
string encryptedCard = client.EncryptCardNumber("4111111111111111", panKey);
Imports System.Net.Http
Imports System.Threading.Tasks
Imports Newtonsoft.Json
Public Class PublicKey
<JsonProperty("keyId")>
Public Property KeyId As String
<JsonProperty("key")>
Public Property Key As String
<JsonProperty("exponent")>
Public Property Exponent As String
<JsonProperty("modulus")>
Public Property Modulus As String
End Class
Public Class PanKeyClient
Private ReadOnly _httpClient As HttpClient
Private ReadOnly _baseUrl As String = "https://your-api-domain.com"
Public Sub New()
_httpClient = New HttpClient()
_httpClient.BaseAddress = New Uri(_baseUrl)
End Sub
Public Async Function GetPanKeyAsync() As Task(Of PublicKey)
Try
' Make the GET request
Dim response As HttpResponseMessage = Await _httpClient.GetAsync("/v1/rest/keys/pan")
' Ensure the request was successful
response.EnsureSuccessStatusCode()
' Read and deserialize the response
Dim responseBody As String = Await response.Content.ReadAsStringAsync()
Dim panKey As PublicKey = JsonConvert.DeserializeObject(Of PublicKey)(responseBody)
Return panKey
Catch ex As HttpRequestException
Console.WriteLine($"Request error: {ex.Message}")
Throw
End Try
End Function
' Example: Encrypt a bank account number
Public Function EncryptAccountNumber(accountNumber As String, panKey As PublicKey) As String
' Use RSA encryption library to encrypt with the public key
' This is a placeholder - implement actual RSA encryption
' with PKCS#1 v1.5 padding
Throw New NotImplementedException("Implement RSA encryption here")
End Function
End Class
' Example usage:
Dim client As New PanKeyClient()
Dim panKey As PublicKey = Await client.GetPanKeyAsync()
Console.WriteLine($"PAN Key ID: {panKey.KeyId}")
' Encrypt a bank account number for ACH
Dim encryptedAccount As String = client.EncryptAccountNumber("9876543210", panKey)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
class PublicKey {
@SerializedName("keyId")
private String keyId;
@SerializedName("key")
private String key;
@SerializedName("exponent")
private String exponent;
@SerializedName("modulus")
private String modulus;
// Getters
public String getKeyId() { return keyId; }
public String getKey() { return key; }
public String getExponent() { return exponent; }
public String getModulus() { return modulus; }
}
public class PanKeyClient {
private final HttpClient httpClient;
private final String baseUrl;
private final Gson gson;
public PanKeyClient() {
this.baseUrl = "https://your-api-domain.com";
this.httpClient = HttpClient.newHttpClient();
this.gson = new Gson();
}
public PublicKey getPanKey() throws Exception {
// Construct the endpoint URL
String endpoint = baseUrl + "/v1/rest/keys/pan";
// Build the HTTP request
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(endpoint))
.header("Accept", "application/json")
.GET()
.build();
// Send the request and get response
HttpResponse<String> response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofString()
);
// Check response status
if (response.statusCode() == 200) {
return gson.fromJson(response.body(), PublicKey.class);
} else {
throw new Exception("Request failed with status: " + response.statusCode());
}
}
// Example: Encrypt credit card number
public String encryptCardNumber(String cardNumber, PublicKey panKey) {
// Implement RSA encryption with PKCS#1 v1.5 padding
// using javax.crypto or BouncyCastle library
throw new UnsupportedOperationException("Implement RSA encryption here");
}
// Example usage
public static void main(String[] args) {
try {
PanKeyClient client = new PanKeyClient();
PublicKey panKey = client.getPanKey();
System.out.println("PAN Key ID: " + panKey.getKeyId());
// Encrypt credit card
String encryptedCard = client.encryptCardNumber("4111111111111111", panKey);
System.out.println("Encrypted card ready for transmission");
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
e.printStackTrace();
}
}
}
require 'net/http'
require 'uri'
require 'json'
require 'openssl'
require 'base64'
class PanKeyClient
def initialize(base_url = 'https://your-api-domain.com')
@base_url = base_url
end
def get_pan_key
# Construct the endpoint URL
uri = URI("#{@base_url}/v1/rest/keys/pan")
# Create HTTP request
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true if uri.scheme == 'https'
http.read_timeout = 30
request = Net::HTTP::Get.new(uri.path)
request['Accept'] = 'application/json'
# Send request and handle response
response = http.request(request)
case response.code.to_i
when 200
JSON.parse(response.body)
else
raise "Request failed with status: #{response.code} - #{response.message}"
end
rescue StandardError => e
puts "Request error: #{e.message}"
raise
end
# Example: Encrypt credit card number
def encrypt_card_number(card_number, pan_key)
# Import the public key
public_key_pem = pan_key['key']
public_key = OpenSSL::PKey::RSA.new(public_key_pem)
# Encrypt the card number using RSA with PKCS#1 v1.5 padding
encrypted = public_key.public_encrypt(card_number, OpenSSL::PKey::RSA::PKCS1_PADDING)
# Return base64 encoded encrypted data
Base64.strict_encode64(encrypted)
end
end
# Example usage:
client = PanKeyClient.new
pan_key = client.get_pan_key
puts "PAN Key ID: #{pan_key['keyId']}"
# Encrypt a credit card number
encrypted_card = client.encrypt_card_number('4111111111111111', pan_key)
puts "Encrypted card: #{encrypted_card[0..50]}..."
uri.path)
request['Accept'] = 'application/json'
# Send request and handle response
response = http.request(request)
case response.code.to_i
when 200
JSON.parse(response.body)
else
raise "Request failed with status: #{response.code} - #{response.message}"
end
rescue StandardError => e
puts "Request error: #{e.message}"
raise
end
# Example: Encrypt credit card number
def encrypt_card_number(card_number, pan_key)
# Import the public key
public_key_pem = pan_key['key']
public_key = OpenSSL::PKey::RSA.new(public_key_pem)
# Encrypt the card number using RSA with PKCS#1 v1.5 padding
encrypted = public_key.public_encrypt(card_number, OpenSSL::PKey::RSA::PKCS1_PADDING)
# Return base64 encoded encrypted data
Base64.strict_encode64(encrypted)
end
end
# Example usage:
client = PanKeyClient.new
pan_key = client.get_pan_key
puts "PAN Key ID: #{pan_key['keyId']}"
# Encrypt a credit card number
encrypted_card = client.encrypt_card_number('4111111111111111', pan_key)
puts "Encrypted card: #{encrypted_card[0..50]}..."
Response Structure
| Field | Type | Description |
|---|---|---|
| keyId | string | Unique identifier for this key version |
| key | string | The complete public key in PEM format (base64 encoded) |
| exponent | string | The RSA public exponent (typically "AQAB" for value 65537) |
| modulus | string | The RSA modulus (the public component of the RSA key pair) |
Example Response
{
"keyId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1234567890abcdefghij\nklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklm\nnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnop\nqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrst\nuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\n-----END PUBLIC KEY-----",
"exponent": "AQAB",
"modulus": "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
}
HTTP Status Codes
| Status Code | Description |
|---|---|
| 200 | Success - PAN public key retrieved successfully |
| 500 | Internal Server Error - Server encountered an error retrieving the key |
What to Encrypt with the PAN Key
Credit Card Transactions
- Card Number: The 13-19 digit credit card number (remove spaces and dashes)
- Example: "4111111111111111" (Visa test card)
- API Field: Send encrypted value in
encryptedAccountNumber
ACH/Bank Account Transactions
- Account Number: The checking or savings account number
- Example: "9876543210"
- API Field: Send encrypted value in
encryptedAccountNumber - Note: The routing number (ABA) is NOT encrypted - send it in plain text in
abaRoutingNumber
Do NOT encrypt with PAN key:
- CVV/CVV2 security codes (use SAD key from /v1/rest/keys/sad)
- Card expiration dates (send in plain text)
- Cardholder names (send in plain text)
- Billing addresses (send in plain text)
Encryption Implementation Guide
Step 1: Fetch the PAN Key
PublicKey panKey = await GetPanKeyAsync();
Step 2: Parse the Public Key
The key field contains a PEM-formatted RSA public key. Most encryption libraries can import this directly:
// C# example using BouncyCastle
var keyBytes = Convert.FromBase64String(panKey.Key.Replace("-----BEGIN PUBLIC KEY-----", "")
.Replace("-----END PUBLIC KEY-----", "")
.Replace("\n", ""));
// Import into your RSA implementation
Step 3: Encrypt the Account Number
// Use RSA with PKCS#1 v1.5 padding byte[] dataToEncrypt = Encoding.UTF8.GetBytes(cardNumber); byte[] encryptedData = rsaPublicKey.Encrypt(dataToEncrypt, RSAEncryptionPadding.Pkcs1); string encryptedBase64 = Convert.ToBase64String(encryptedData);
Step 4: Send in API Request
var request = new TransactionRequest
{
EncryptedAccountNumber = encryptedBase64, // Your encrypted card number
CardExpiry = "1225", // Plain text
Amount = new Amount { Dollars = 100.00m },
AccountHolderName = "John Doe" // Plain text
};
Common Use Cases
1. ACH-Only Integrations
If you only process bank account payments (no credit cards), you only need the PAN key:
// Fetch PAN key only
PublicKey panKey = await GetPanKeyAsync();
// Encrypt bank account number
string encryptedAccount = EncryptWithPanKey(bankAccountNumber, panKey);
// Create ACH transaction
var achRequest = new TransactionRequest
{
EncryptedAccountNumber = encryptedAccount,
AbaRoutingNumber = "123456789", // Plain text
BankAccountType = "Checking",
Region = "IL", // Required for ACH
// ... other fields
};
2. Saved Payment Method Creation
// Fetch PAN key
PublicKey panKey = await GetPanKeyAsync();
// Encrypt card number
string encryptedCard = EncryptWithPanKey("4111111111111111", panKey);
// Create saved payment method
var paymentMethod = new SavedPaymentMethodRequest
{
EncryptedAccountNumber = encryptedCard,
CardExpiry = "1225",
PaymentMethodType = "CreditCard",
AccountHolderFirstName = "Jane",
AccountHolderLastName = "Smith"
};
3. Mobile/Client-Side Encryption
For mobile apps or client-side JavaScript, fetch the PAN key and encrypt on the device:
// JavaScript example (using Web Crypto API or JSEncrypt)
async function encryptCardNumber(cardNumber) {
// Fetch PAN key
const response = await fetch('https://api-domain.com/v1/rest/keys/pan');
const panKey = await response.json();
// Import public key
const publicKey = await importRSAKey(panKey.key);
// Encrypt card number
const encrypted = await crypto.subtle.encrypt(
{ name: "RSA-OAEP" },
publicKey,
new TextEncoder().encode(cardNumber)
);
return btoa(String.fromCharCode(...new Uint8Array(encrypted)));
}
Best Practices
- Remove formatting: Strip spaces, dashes, and any other characters from card/account numbers before encrypting
- Validate before encrypting: Use Luhn algorithm to validate card numbers before encryption to catch typos early
- Cache appropriately: Cache the PAN key for a session or a few hours, but refresh periodically
- Handle errors gracefully: If encryption fails, don't expose error details to end users
- Test with test cards: Use test card numbers (4111111111111111) in development/sandbox
- Clear memory: Overwrite plain text card numbers in memory immediately after encryption
- Never log sensitive data: Don't log card numbers, even temporarily during debugging
Troubleshooting
"Invalid encrypted account number" Error
- Verify you're using RSA with PKCS#1 v1.5 padding (not OAEP)
- Check that the encrypted data is base64 encoded
- Ensure no extra whitespace or characters in the encrypted string
- Confirm you encrypted the account number (not CVV or other data)
Key Import Fails
- Verify the key is in PEM format with proper BEGIN/END markers
- Check for line breaks (\n) in the PEM-formatted key
- Try using the
modulusandexponentfields to construct the key manually
Decryption Fails on Server
- Fetch a fresh PAN key - the key may have been rotated
- Include the
keyIdin your request if the API supports it - Verify the padding scheme matches (PKCS#1 v1.5)
Related Endpoints
- GET /v1/rest/keys - Get both PAN and SAD keys in one request
- GET /v1/rest/keys/sad - Get the SAD key for encrypting CVV codes
Additional Notes
- The PAN key is separate from the SAD key to provide defense in depth - if one key is compromised, the other remains secure.
- This endpoint returns the same PAN key as the
panKeyfield in theGET /v1/rest/keysresponse. - PAN stands for "Primary Account Number" - the official term for credit card and bank account numbers in payment industry standards.
- Keys are 2048-bit RSA, which provides strong security while maintaining reasonable performance.
- The
keyIdhelps track which key version was used, useful during key rotation periods. - Some payment terminals and point-of-sale systems may fetch the PAN key to encrypt track data from card swipes.