Encryption
Encryption
Encryption transforms plaintext into ciphertext so that only authorized parties with the decryption key can recover the original data. The correct encryption approach depends on the data state: at rest (stored), in transit (moving across a network), or in use (actively processed).
Intent
Encryption transforms plaintext into ciphertext so that only authorized parties with the decryption key can recover the original data. The correct encryption approach depends on the data state: data at rest (stored on disk or in a database), data in transit (moving across a network), or data in use (actively processed in memory or a shared compute environment).
When NOT to Use
- Do not encrypt data that is already protected by a secure access control layer alone — encryption adds key management complexity; use it when the storage or channel itself can be compromised independently of your access controls (i.e., always for sensitive data).
- Do not implement custom cryptographic algorithms. Use established primitives: AES-256-GCM for symmetric encryption, TLS 1.3 for transport, RSA-OAEP or ECDH for key exchange. Custom crypto is almost always broken.
- Do not use ECB mode for block ciphers — ECB is deterministic and leaks patterns. Always use GCM (authenticated encryption) or CBC+HMAC.
- Do not use MD5 or SHA-1 for integrity-sensitive contexts — both are broken for collision resistance. Use SHA-256 or SHA-3.
How It Works — Three Data States
flowchart TD
A[What state is the data?] --> B{At rest}
A --> C{In transit}
A --> D{In use}
B --> E[AES-256-GCM\nDatabase TDE or application-level]
C --> F[TLS 1.3\nmTLS for service-to-service]
D --> G{Isolation needed?}
G -->|Compute isolation| H[Secure enclave\nSGX / AMD SEV]
G -->|Third-party processor| I[Tokenization]
G -->|Computation on encrypted data| J[Homomorphic encryption\nhigh overhead -- specialized]
Data at rest: data stored in databases, file systems, object storage, or backups. Protect with AES-256-GCM (symmetric, authenticated encryption). The database engine or object store may offer transparent encryption (TDE), but application-level encryption provides defense in depth — the storage layer cannot read plaintext even with administrator access.
Data in transit: data moving over networks. Protect with TLS 1.3. TLS 1.3 eliminates the RSA key exchange (forward secrecy by default), reduces handshake to 1-RTT, and removes deprecated cipher suites (RC4, 3DES, SHA-1). Mutual TLS (mTLS) extends this by requiring the client to present a certificate — used in service-to-service communication (see Service-Mesh-Pattern).
Data in use: data actively processed by a CPU or shared across memory boundaries. Protect with:
- Secure enclaves (Intel SGX, AMD SEV) for hardware isolation;
- Homomorphic encryption for computation on ciphertext without decryption (high overhead, specialized use cases);
- Tokenization for replacing sensitive values with opaque tokens before handing data to third-party processors.
Data-in-use protection is the hardest state — enclave complexity is high; tokenization is the most practical mitigation for most systems.
Envelope Encryption (DEK + KEK)
Envelope encryption solves the key distribution problem: encrypting data directly with a master key means every encrypted record depends on that key being available, and rotating the key requires re-encrypting all data.
The pattern:
- Generate a Data Encryption Key (DEK) — a random AES-256 key specific to this record or data set.
- Encrypt the plaintext with the DEK (fast, symmetric).
- Encrypt the DEK with the Key Encryption Key (KEK) — a long-lived master key stored in a key management system (KMS).
- Store the encrypted DEK alongside the ciphertext. The KEK never leaves the KMS.
Rotation: to rotate the KEK, decrypt each DEK with the old KEK, re-encrypt with the new KEK, and store the new encrypted DEK. The data ciphertext is never re-encrypted. To rotate a DEK for a specific record, generate a new DEK, re-encrypt the plaintext, and wrap the new DEK with the current KEK.
sequenceDiagram
participant App as Application
participant KMS as Key Management System
participant DB as Database
App->>KMS: GenerateDataKey (AES-256)
KMS-->>App: DEK (plaintext) + DEK (encrypted with KEK)
App->>App: Encrypt plaintext with DEK (plaintext)
App->>App: Discard DEK plaintext from memory
App->>DB: Store ciphertext + encrypted DEK
AES-256-GCM Specifics
AES-256-GCM provides both confidentiality (AES-256 in CTR mode) and authenticity (GHASH authentication tag). Always generate a random 96-bit IV (nonce) for each encryption operation — never reuse an IV with the same key. IV reuse with GCM is catastrophic (leaks the authentication key). Store the IV alongside the ciphertext.
TypeScript Example
AES-256-GCM encryption/decryption using Node.js node:crypto module. Each call generates a fresh 96-bit IV.
import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';
const ALGORITHM = 'aes-256-gcm';
export function encrypt(plaintext: string, key: Buffer): { iv: string; ciphertext: string; tag: string } {
const iv = randomBytes(12); // 96-bit nonce — never reuse with same key
const cipher = createCipheriv(ALGORITHM, key, iv);
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
return {
iv: iv.toString('hex'),
ciphertext: encrypted.toString('hex'),
tag: cipher.getAuthTag().toString('hex'),
};
}
export function decrypt(iv: string, ciphertext: string, tag: string, key: Buffer): string {
const decipher = createDecipheriv(ALGORITHM, key, Buffer.from(iv, 'hex'));
decipher.setAuthTag(Buffer.from(tag, 'hex'));
return Buffer.concat([
decipher.update(Buffer.from(ciphertext, 'hex')),
decipher.final(),
]).toString('utf8');
}Java Example
AES-256-GCM with JCE (javax.crypto). IV is prepended to the ciphertext for storage.
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
public class AesGcmEncryption {
private static final String ALGORITHM = "AES/GCM/NoPadding";
private static final int TAG_LENGTH_BITS = 128;
private static final int IV_LENGTH_BYTES = 12;
public static byte[] encrypt(byte[] plaintext, byte[] key) throws Exception {
byte[] iv = new byte[IV_LENGTH_BYTES];
new SecureRandom().nextBytes(iv); // Never reuse IV with same key
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"),
new GCMParameterSpec(TAG_LENGTH_BITS, iv));
byte[] ciphertext = cipher.doFinal(plaintext);
// Prepend IV to ciphertext for storage
byte[] result = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, result, 0, iv.length);
System.arraycopy(ciphertext, 0, result, iv.length, ciphertext.length);
return result;
}
}Related Concepts
- Secrets-Management — keys (DEK, KEK) are secrets requiring lifecycle management
- Zero-Trust-Architecture — encryption is a control in the workload plane; TLS enforces the network plane
- Service-Mesh-Pattern — mTLS is the service mesh implementation of data-in-transit encryption
- JWT — JWT signing (RS256, ES256) uses asymmetric cryptography; JWT encryption (JWE) is a separate concern
Backlinks
(Leave as placeholder — backlinks added in Phase 40 sweep)