encryptedCard on the issue response, encrypted to your own RSA
public key — so only your private key can read them. Endl holds only the public
key and cannot decrypt a card afterwards.
Generate your key pair
RSA, 2048 bits or more — 3072 recommended.Register the public key
There is no API call for this. Share the public key with your Endl integration contact and Endl sets it up for your account. The private key never leaves your systems.- Generate the pair with the commands above.
-
Send
card-public.pem— PEM,-----BEGIN PUBLIC KEY-----— plus an optionalkeyIdlabel, up to 64 characters, such asacme-cards-2026. -
Endl confirms with the
keyIdand a fingerprint. Check it matches yours: -
Issue cards. Every
encryptedCardcarries thekeyIdit was encrypted to.
400 ERRCRD_1004.
Rotation
Send a new public key the same way. Once Endl registers it, it replaces the old one: cards issued afterwards use the new key andkeyId. Keep the old
private key until you no longer need cards issued before the switch.
How Endl encrypts each card
RSA-OAEP-256+A256GCM.
- Builds the plaintext
{"pan":"4111111111112464","cvv":"123","expiryMonth":"12","expiryYear":"2030"}. - Generates a fresh random 32-byte AES key and a 12-byte nonce, for this card only.
- Encrypts with AES-256-GCM, using the
cardIdas additional authenticated data (AAD) — so the payload is bound to this card. - Encrypts the AES key with your RSA public key (RSA-OAEP, SHA-256).
- Returns
encryptedKey,iv,ciphertext,tag,aadand thekeyId, then wipes the AES key and card data from memory. Nothing is logged or stored.
How to decrypt
- RSA-OAEP (SHA-256, MGF1-SHA-256) decrypt base64
encryptedKeywith your private key → a 32-byte AES key. - AES-256-GCM decrypt base64
ciphertextwith that key, nonce = base64iv, auth tag = base64tag, AAD =aadas UTF-8. - Check
aadequalscardId, and thatpanends withlast4. A wrong key or tampered data fails the GCM check. - Parse the JSON —
pan,cvv,expiryMonth,expiryYear. Never log it.
Java’s GCM cipher expects ciphertext followed by the tag in one buffer,
which is why the sample concatenates them. Node and Python take the tag
separately.