← Back to Blogs Admin, Developer
System.Crypto · Apex Reference Guide

Encrypt & decrypt data in Apex — the standard way

A hands-on guide to the built-in Crypto class: AES encryption in CBC and GCM modes, hashing, HMACs, and digital signatures. Follow the animated path, run the live demo, flip through every method, then test yourself.

Step 1 · The mental model

Follow the encryption path

Every AES call in Apex walks the same path. Watch it advance — or click any stage to read what happens there.

  1. PlaintextBlob.valueOf(data)
  2. AES KeygenerateAesKey(256)
  3. CipherCBC or GCM
  4. IV handlingmanaged or manual
  5. Store / Sendbase64Encode

What the encrypted blob actually looks like

With managed IV, Salesforce packs the IV into the output for you — but the layout differs by mode:

CBC · encryptWithManagedIV('AES256', ...)
IV — 16 bytesrandom, generated by Salesforce
CiphertextAES-CBC, PKCS7 padded
GCM · encryptWithManagedIV('AES256-GCM', ...)
IV lengthalways 12
IV — 12 bytesSalesforce-generated
CiphertextAES-GCM, no padding

With GCM you never supply an IV yourself — passing any non-null IV to encrypt() throws an error. With CBC + encrypt(), your IV must be exactly 16 bytes.

Step 2 · Choose your algorithm

CBC, GCM & everything else the class supports

ModeVariantsDescription
CBCAES128 / AES128-CBCAES 128-bit, CBC mode, PKCS7 padding
AES192 / AES192-CBCAES 192-bit, CBC mode, PKCS7 padding
AES256 / AES256-CBCAES 256-bit, CBC mode, PKCS7 padding
GCMAES256-GCMAES 256-bit, GCM mode, no padding. Only 256-bit is currently supported for GCM
⚠️ Heads up: the docs state AES/CBC/PKCS7 is vulnerable to a Padding Oracle attack. Protect CBC with Encrypt-then-MAC (encrypt with key #1, generateMac over the ciphertext with key #2, and verifyHMac before decrypting) — or simply use a GCM algorithm.
✅ GCM perks: authenticated encryption with optional AAD (additional authentication data) via the 4-argument managed-IV overloads. AAD works only with AES256-GCM — passing it with CBC throws "AAD can only be used with AESGCM algorithms".
TypeVariantsDescription
RSARSA / RSA-SHA1RSA signature of a SHA1 hash
RSA-SHA256RSA signature of a SHA256 hash
RSA-SHA384RSA signature of a SHA384 hash
RSA-SHA512RSA signature of a SHA512 hash
ECDSA (DER)ECDSA-SHA256ECDSA signature of a SHA256 hash
ECDSA-SHA384ECDSA signature of a SHA384 hash
ECDSA-SHA512ECDSA signature of a SHA512 hash
ECDSA (P1363)ECDSA-SHA256-P1363ECDSA SHA256, P1363 format
ECDSA-SHA256-PLAINP1363 format — the fix when a JWT flow returns invalid_client
ECDSA-SHA384-P1363ECDSA, P1363 format
ECDSA-SHA512-P1363ECDSA, P1363 format

JWT gotcha from the docs: if you sign with ECDSA (P-256 curve, P1363 format) and verify with ECDSA-SHA256, the JWT service can return invalid_client. Switch to ECDSA-SHA256-PLAIN and the error is resolved.

MethodAccepted algorithm values
generateDigestMD5 · SHA1 · SHA-256 · SHA-512 · SHA3-256 · SHA3-384 · SHA3-512
generateMac / verifyHMachmacMD5 · hmacSHA1 · hmacSHA256 · hmacSHA512 — key up to 4 KB

If you Base64-encode the private key for generateMac, you must supply the same Base64-encoded key to verifyHMac.

Step 3 · Try it yourself

Live encrypt / decrypt demo

Runs real AES-256-CBC in your browser and mimics Crypto.encryptWithManagedIV('AES256', ...) — including prepending the 16-byte IV to the output, exactly like the byte diagram above. Nothing leaves this page.

🔐
Encryption WorkbenchAES-256 · CBC · managed-IV layout · in-browser only
No key yet — click Generate key

🧪 Tamper a byte flips one byte of the ciphertext so you can watch decryption fail — the browser's CBC throws just like Apex raises a SecurityException ("Given final block isn't properly padded").

Step 4 · Ship it

Copy-paste recipes

the doc's own round-trip sample
String algorithmName = 'AES128';
Blob key = Crypto.generateAesKey(128);
Blob data = Blob.valueOf('Data to be encrypted');
Blob encrypted = Crypto.encryptWithManagedIV(algorithmName, key, data);
Blob decrypted = Crypto.decryptWithManagedIV(algorithmName, key, encrypted);
Assert.areEqual('Data to be encrypted', decrypted.toString());

// store ciphertext in a text field
String stored = EncodingUtil.base64Encode(encrypted);

Best default when Salesforce is on both sides. Key sizes must match the algorithm: 16 / 24 / 32 bytes for AES 128 / 192 / 256. Input max: 1,048,576 bytes (1 MB).

authenticated encryption — no IV to manage
String algorithmName = 'AES256-GCM';
// No IV if you specify AES256-GCM
Blob key = Crypto.generateAesKey(256);
Blob data = Blob.valueOf('Data to be encrypted');
Blob aad = Blob.valueOf('Additional tag');
Blob encrypted = Crypto.encryptWithManagedIV(algorithmName, key, data, aad);
Blob decrypted = Crypto.decryptWithManagedIV(algorithmName, key, encrypted, aad);
Assert.areEqual('Data to be encrypted', decrypted.toString());

AAD is bound to the ciphertext but not encrypted — change it and decryption fails. The AAD overloads support only AES256-GCM.

you own the 16-byte IV — for cross-system CBC
// 16-byte value (doc sample uses a literal; make it random in production)
Blob exampleIv = Blob.valueOf('Example of IV123');
Blob key = Crypto.generateAesKey(128);
Blob data = Blob.valueOf('Data to be encrypted');
Blob encrypted = Crypto.encrypt('AES128', key, exampleIv, data);
Blob decrypted = Crypto.decrypt('AES128', key, exampleIv, encrypted);
System.debug(decrypted.toString());

You must transmit or store the IV alongside the ciphertext. Never reuse a fixed IV with the same key in production.

one-way hash + tamper-proof MAC
// Digest: one-way fingerprint
Blob hash = Crypto.generateDigest('SHA-256', Blob.valueOf('hello world'));
System.debug(EncodingUtil.convertToHex(hash));

// HMAC: keyed hash — webhook verification, integrity checks
Blob macKey = Crypto.generateAesKey(256);
Blob body = Blob.valueOf(requestBody);
Blob mac = Crypto.generateMac('hmacSHA256', body, macKey);

// verify — don't hand-roll a comparison
Boolean ok = Crypto.verifyHMac('hmacSHA256', body, macKey, mac);
certificates, raw keys, and XML envelopes
// 1) Sign with an org certificate (Setup → Certificate and Key Management)
Blob input = Blob.valueOf('Test Sign With Certificate.');
Blob sig = Crypto.signWithCertificate('RSA', input, 'your-cert-unique-name');

// 2) Sign with a raw PKCS #8 key (base64Decoded, max 4 KB, no BEGIN/END lines)
Blob privateKey = EncodingUtil.base64Decode(rawPkcs8Key);
Blob sig2 = Crypto.sign('RSA', input, privateKey);

// 3) Verify with a public key or an org certificate
Boolean ok = Crypto.verify('RSA-SHA256', input, sig2, publicKeyBlob);

// 4) Envelop a signature inside an XML document
Crypto.signXML('RSA', doc.getRootElement(), null, 'your-cert-unique-name');

Prep a PKCS8 key with openssl: openssl genrsa -out k.pem 1024 then openssl pkey -in k.pem -out k.pkcs8.pem.

Step 5 · Know every tool

All 12 methods — flip to learn

👆 Click or tap any card to flip it.

🔑generateAesKey(size)Random AES key
Sizes: 128 / 192 / 256 bits. Returns a Blob. Generate once, store in a protected custom setting, reuse forever.
🔒encryptWithManagedIV()Salesforce handles the IV
CBC: IV = first 16 bytes of output. GCM: [12][12-byte IV][ciphertext]. The aaData overload is GCM-only.
🔓decryptWithManagedIV()Reads IV from the blob
Throws InvalidParameterValue if the blob is too short to contain the IV. AAD used to encrypt must match at decrypt.
🧰encrypt() / decrypt()You supply the IV
CBC: IV must be exactly 16 bytes. GCM: IV must be null — anything else errors. For third-party handshakes.
🧬generateDigest()One-way hash
MD5, SHA1, SHA-256, SHA-512, SHA3-256/384/512. No decrypt exists — use for fingerprints, not storage you need back.
🏷️generateMac()Keyed hash (HMAC)
hmacMD5 / SHA1 / SHA256 / SHA512. Key ≤ 4 KB. Base64-encoded key in → same encoded key required by verifyHMac.
verifyHMac()Check a MAC
The gatekeeper of Encrypt-then-MAC: verify authenticity & integrity first; only decrypt if it passes.
✍️sign()Raw-key signature
RSA & ECDSA (incl. -PLAIN). Key: base64Decoded PKCS #8, ≤ 4 KB, no BEGIN/END PRIVATE KEY lines.
📜signWithCertificate()Org-certificate signature
References a cert by Unique Name from Setup → Certificate and Key Management. Same algorithm list as sign().
🧾signXML()Signature inside XML
Envelops the signature into a Dom.XmlNode. Overload with refChild inserts before a chosen child node.
🔎verify()Check a signature
Two overloads: raw public key Blob, or an org certificate's public key. JWT invalid_client? Try ECDSA-SHA256-PLAIN.
🎲getRandomInteger / Long()Secure randomness
Backed by java.security.SecureRandom. Prefer these over Math.random() for anything security-adjacent.

When things go wrong

ExceptionTypical messageCause & fix
InvalidParameterValue"Unable to parse the initialization vector…"Managed-IV ciphertext shorter than the IV header — check you stored the full blob
InvalidParameterValue"Invalid private key. Must be size bytes."Key length ≠ algorithm: 16/24/32 bytes for AES 128/192/256
InvalidParameterValue"Invalid initialization vector…"CBC needs a 16-byte IV; GCM's IV is 12 bytes and Salesforce-generated
InvalidParameterValue"AAD can only be used with AESGCM algorithms."You passed aaData with CBC — switch to AES256-GCM
InvalidParameterValue"…exceeds the limit of 1,048,576 bytes."Input over 1 MB — chunk the payload
NullPointerException"Argument can't be null."A required argument was null
SecurityException"Given final block isn't properly padded."Wrong key, wrong IV, or corrupted ciphertext — try the Tamper button above!
Step 6 · Earn your badge

Quick knowledge check

1. Calling Crypto.encrypt('AES256-GCM', key, myIv, data) with a non-null IV…
The docs: "For GCM, don't provide an IV. Any non-null IV will result in an error." Salesforce always generates GCM's 12-byte IV.
2. Where does encryptWithManagedIV('AES256', ...) put the IV?
For CBC managed IV, the IV is stored as the first 128 bits (16 bytes) of the encrypted blob.
3. In Encrypt-then-MAC, before decrypting you must…
Verify authenticity & integrity first; if either check fails, throw and never decrypt. Decryption only happens as step two.
4. Which key size does AES256-GCM accept?
Currently, only the 256-bit size is supported for GCM.
Score: 0 / 0
Built from the official Apex Reference Guide → Crypto Class (Summer '26 · API v67.0) · developer.salesforce.com
System.Crypto · AES-CBC / AES-GCM · RSA & ECDSA · Not affiliated with Salesforce — a community developer guide.

Welcome Back

OR
Don't have an account? Sign up