๐ Crypto Design โ Algorithm Choice, Key Hierarchy, Envelope Encryption, Signing, Rotation, and Common Coding Mistakes
Intro: Good crypto design starts long before the first library call. Most failures are not caused by attackers breaking AES; they are caused by teams choosing the wrong primitive, mixing up key roles, reusing nonces, storing keys badly, or skipping rotation and verification.
What this page includes
- a practical crypto-design decision model
- key hierarchy and envelope-encryption patterns
- signing and verification design rules
- rotation ownership and operating model
- current algorithm guidance, weak/deprecated choices, and post-quantum direction
- top weak-crypto mistakes in Python, Java, and PHP code
Start with the system questions, not the library
Before choosing an algorithm, ask:
- What are we protecting? password, token, database field, object, backup, message, API call, artifact, or certificate?
- Do we need confidentiality, integrity, authenticity, or all three?
- Does the data need to be recovered later?
- Is the control for data at rest, in transit, or both?
- Who is allowed to decrypt, sign, rotate, or revoke?
- What breaks if the key is rotated, revoked, or unavailable?
What to encrypt at rest vs in transit
| Data state | What to protect | Typical control |
|---|---|---|
| At rest | DB fields, backups, blob/object storage, message persistence, config secrets | storage encryption, application-layer field encryption, tokenization, KMS-wrapped data keys |
| In transit | browser โ edge, service โ service, API โ DB proxy, queue producers โ brokers | TLS 1.3, mTLS where needed, signed tokens, message signing in special cases |
| At use / in memory | short-lived secrets, plaintext after decryption, signing keys loaded into process memory | minimize plaintext lifetime, isolate workloads, prefer managed key operations/HSM where practical |
Good rule of thumb
- Use TLS for data in transit.
- Use encryption at rest for broad platform coverage.
- Add application-layer encryption when platform encryption is not enough because too many systems or operators can still read the plaintext.
Key hierarchy
A strong key hierarchy reduces blast radius and clarifies ownership.
Recommended mental model
| Key type | Purpose | Typical owner |
|---|---|---|
| Root / HSM-protected key | trust anchor inside KMS/HSM boundary | platform / cloud security / key-management function |
| KEK / wrapping key | encrypts data keys or protects service-specific keys | platform or service security owner |
| DEK / data key | directly encrypts the data | application or storage workflow, usually generated per object / file / record group |
| Signing key | signs artifacts, tokens, or messages | release/platform team, identity team, or specific security owner |
| Password-hashing secret / pepper | strengthens password verification workflows | identity/security function |
Important separation rule
Do not use the same key for all purposes.
Separate:
- encryption keys from signing keys;
- production keys from non-production keys;
- customer-data keys from application-secret keys;
- CI/CD signing keys from runtime service keys.
Envelope encryption
Envelope encryption is the most useful default pattern for cloud and distributed systems.
How it works
- Generate a data key (DEK).
- Encrypt the data with the DEK.
- Encrypt the DEK with a wrapping key / KEK in KMS or HSM.
- Store the encrypted DEK next to the ciphertext.
Why this is good
- data keys can be short-lived and highly scoped;
- the wrapping key stays in KMS/HSM;
- rotation becomes more manageable because you can re-wrap keys rather than re-encrypt all data immediately in every case;
- workloads do not need long-lived plaintext master keys.
Signing vs encryption
Teams often confuse these.
| Need | Use | Why |
|---|---|---|
| Keep plaintext secret | encryption | protects confidentiality |
| Prove origin / integrity | signing | protects authenticity and integrity |
| Detect modification in storage or transit | signing or AEAD / MAC | ensures tampering is visible |
| Approve artifacts or releases | signing / attestation | enables verification before deploy |
Design rule
If integrity matters, do not assume confidentiality alone gives it to you.
- For modern symmetric encryption, use AEAD modes such as AES-GCM or ChaCha20-Poly1305.
- For artifact, token, or message authenticity, use proper signature or MAC verification.
Who rotates keys?
Rotation is an ownership problem before it is a technical problem.
| Asset | Typical rotator | Notes |
|---|---|---|
| KMS/HSM wrapping keys | platform/cloud security | policy-driven, auditable, usually centralized |
| app data keys | app/service workflow via KMS APIs | should be generated automatically, not manually tracked |
| TLS cert private keys | platform / PKI automation / service mesh / ingress team | automate issuance and renewal |
| CI/CD signing keys | release engineering or supply-chain security owner | guard closely; tie to provenance and verification |
| password-hashing parameters and peppers | identity/security owner | rollout must be compatible with auth flows |
Rotation rules
- rotate on schedule and on suspected compromise;
- test re-encryption / re-wrap / rollback paths before you need them;
- keep key usage logs and ownership visible;
- never make rotation somebody else's implied task.
Algorithm choices in 2026
Good default choices now
| Use case | Practical defaults |
|---|---|
| symmetric encryption | AES-GCM (128/256) or ChaCha20-Poly1305 |
| password hashing | Argon2id first; scrypt, bcrypt, or PBKDF2 where constraints require |
| key agreement / ECDH | X25519 or NIST P-256 / P-384 where required |
| signatures | Ed25519, ECDSA P-256/P-384, or RSA-PSS when RSA is still needed |
| transport security | TLS 1.3 with AEAD suites |
| future-facing crypto agility | plan for ML-KEM and ML-DSA adoption paths and hybrid rollouts |
Weak / legacy / poor-fit choices
| Algorithm / pattern | Problem |
|---|---|
| MD5 | broken for collision resistance; not for new security use |
| SHA-1 | collision-broken for signature / integrity designs that rely on collision resistance |
| DES | too small key size |
| 3DES / TDEA | legacy-only and retired for modern encryption use |
| RC4 | broken / obsolete |
| AES-ECB | leaks data patterns |
| RSA < 2048 | insufficient security strength for modern designs |
| RSA PKCS#1 v1.5 key transport | legacy / disallowed in current NIST transition guidance |
| CBC without authentication | encryption without robust integrity protection |
| home-grown crypto | almost always a design and implementation trap |
What looks promising
For crypto agility and post-quantum preparation, the important 2026 message is not "replace everything tomorrow." It is:
- keep designs agile;
- avoid hard-coding one irreversible primitive everywhere;
- track support for ML-KEM and ML-DSA in your platform stack;
- prefer managed KMS/HSM services that can absorb algorithm transitions more safely.
Top 5 weak-crypto coding mistakes
1) Weak password hashing
Bad pattern
Using MD5, SHA-1, or plain SHA-256 for password storage.
Why it fails
These are fast hashes and make brute-force and GPU cracking easier.
Better pattern
Use Argon2id or a slow password KDF supported by your platform.
Python
# bad
import hashlib
hashed = hashlib.sha256(password.encode()).hexdigest()
# better (argon2-cffi)
from argon2 import PasswordHasher
ph = PasswordHasher()
hashed = ph.hash(password)
Java
// bad: fast unsalted hash for password storage
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest(password.getBytes(StandardCharsets.UTF_8));
// better: PBKDF2 if Argon2 is not available in your stack
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 310000, 256);
byte[] hash2 = f.generateSecret(spec).getEncoded();
PHP
// bad
$hash = sha1($password);
// better
$hash = password_hash($password, PASSWORD_ARGON2ID);
2) Static IV / nonce reuse
Bad pattern
Hard-coding or reusing IVs/nonces, especially with GCM/CTR.
Why it fails
Nonce reuse can destroy confidentiality and, in some modes, integrity.
Python
# bad
iv = b"0000000000000000"
# better
from os import urandom
iv = urandom(12) # for GCM-style constructions where library expects 96-bit nonce
Java
// bad
byte[] iv = "0000000000000000".getBytes(StandardCharsets.UTF_8);
// better
byte[] iv = new byte[12];
SecureRandom.getInstanceStrong().nextBytes(iv);
PHP
// bad
$iv = str_repeat("A", 12);
// better
$iv = random_bytes(12);
3) ECB mode or unauthenticated CBC
Bad pattern
Using AES/ECB/... or CBC without robust authenticity checks.
Why it fails
ECB leaks structure; CBC alone does not give modern authenticated encryption.
Java
// bad
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
// better
Cipher c2 = Cipher.getInstance("AES/GCM/NoPadding");
PHP
// bad-ish legacy pattern
$ciphertext = openssl_encrypt($plaintext, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
// better use libsodium or an AEAD-capable construction
4) Insecure randomness
Bad pattern
Using random, mt_rand, or java.util.Random for keys, tokens, IVs, or password reset values.
Better pattern
Use CSPRNGs.
Python
# bad
import random
secret = str(random.randint(100000, 999999))
# better
import secrets
secret = secrets.token_urlsafe(32)
Java
// bad
Random r = new Random();
byte[] key = new byte[32];
r.nextBytes(key);
// better
SecureRandom sr = SecureRandom.getInstanceStrong();
sr.nextBytes(key);
PHP
// bad
$token = md5(mt_rand());
// better
$token = bin2hex(random_bytes(32));
5) Disabling verification or doing DIY crypto
Bad pattern
- skipping certificate validation;
- accepting any signature/token algorithm;
- using custom XOR / homemade encryption wrappers.
Why it fails
The code may "work" functionally while quietly removing the trust guarantees you actually need.
Review cues for PRs
Look for:
ECBmd5(/sha1(for password storageRandom()/mt_rand()/random.randint()in security-sensitive code- hard-coded IV / nonce / salt
verify=False,TrustAll, disabled hostname verification, or custom crypto helpers nobody can explain clearly
AWS and Azure KMS / HSM patterns
For provider-specific implementation detail, see:
- AWS and Azure KMS / HSM Key Management Patterns
- Application-Level Encryption, Tokenization, Masking, and Key Management
References and further reading
- OWASP Cryptographic Storage Cheat Sheet
- NIST SP 800-131A Rev. 2
- TLS 1.3 (RFC 8446)
- NIST FIPS 203 / 204 / 205 and related PQC materials
Read next
- Data Classification and Sensitive Data Lifecycle
- Log Redaction, Backups, and Privacy by Design
- AWS and Azure KMS / HSM Key Management Patterns
- Internal PKI for Microservices โ mTLS and Certificate Automation
Author attribution: Ivan Piskunov, 2026 - Educational and defensive-engineering use.