# Crypto Design — Key Hierarchy, Envelope Encryption, Signing, Rotation, and Common 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:

1. **What are we protecting?** password, token, database field, object, backup, message, API call, artifact, or certificate?
2. **Do we need confidentiality, integrity, authenticity, or all three?**
3. **Does the data need to be recovered later?**
4. **Is the control for data at rest, in transit, or both?**
5. **Who is allowed to decrypt, sign, rotate, or revoke?**
6. **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.

```mermaid
flowchart TD
  A[Root of trust / HSM / cloud KMS root] --> B[KEK / wrapping key]
  B --> C[DEK / data key for dataset or object]
  C --> D[Encrypted data]
  A --> E[Signing key]
  A --> F[Separate admin / break-glass controls]
```

### 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

1. Generate a **data key (DEK)**.
2. Encrypt the data with the DEK.
3. Encrypt the DEK with a **wrapping key / KEK** in KMS or HSM.
4. Store the encrypted DEK next to the ciphertext.

```mermaid
sequenceDiagram
  participant App
  participant KMS
  participant Store
  App->>KMS: Generate data key / wrap request
  KMS-->>App: plaintext DEK + encrypted DEK
  App->>App: Encrypt data with plaintext DEK
  App->>Store: ciphertext + encrypted DEK
  App->>App: Zero plaintext DEK from memory
```

### 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**

```python
# bad
import hashlib
hashed = hashlib.sha256(password.encode()).hexdigest()

# better (argon2-cffi)
from argon2 import PasswordHasher
ph = PasswordHasher()
hashed = ph.hash(password)
```

**Java**

```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**

```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**

```python
# bad
iv = b"0000000000000000"

# better
from os import urandom
iv = urandom(12)  # for GCM-style constructions where library expects 96-bit nonce
```

**Java**

```java
// bad
byte[] iv = "0000000000000000".getBytes(StandardCharsets.UTF_8);

// better
byte[] iv = new byte[12];
SecureRandom.getInstanceStrong().nextBytes(iv);
```

**PHP**

```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**

```java
// bad
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");

// better
Cipher c2 = Cipher.getInstance("AES/GCM/NoPadding");
```

**PHP**

```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**

```python
# bad
import random
secret = str(random.randint(100000, 999999))

# better
import secrets
secret = secrets.token_urlsafe(32)
```

**Java**

```java
// bad
Random r = new Random();
byte[] key = new byte[32];
r.nextBytes(key);

// better
SecureRandom sr = SecureRandom.getInstanceStrong();
sr.nextBytes(key);
```

**PHP**

```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:

* `ECB`
* `md5(` / `sha1(` for password storage
* `Random()` / `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](/cloud-kubernetes-and-infrastructure-security/index/aws-and-azure-kms-hsm-key-management-patterns.md)
* [Application-Level Encryption, Tokenization, Masking, and Key Management](/architecture-api-crypto-and-identity/index-3/application-level-encryption-tokenization-masking-and-key-management.md)

## 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](/architecture-api-crypto-and-identity/index-3/data-classification-and-sensitive-data-lifecycle.md)
* [Log Redaction, Backups, and Privacy by Design](/architecture-api-crypto-and-identity/index-3/log-redaction-backups-and-privacy-by-design.md)
* [AWS and Azure KMS / HSM Key Management Patterns](/cloud-kubernetes-and-infrastructure-security/index/aws-and-azure-kms-hsm-key-management-patterns.md)
* [Internal PKI for Microservices — mTLS and Certificate Automation](/cloud-kubernetes-and-infrastructure-security/index/internal-pki-for-microservices-mtls-and-certificate-automation.md)

***

*Author attribution: Ivan Piskunov, 2026 - Educational and defensive-engineering use.*


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.product-security.expert/architecture-api-crypto-and-identity/index-3/crypto-design-key-hierarchy-envelope-encryption-signing-rotation-and-common-mistakes.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
