Applied Cryptography and Key Management Questions
Selecting and applying cryptographic primitives correctly: symmetric and asymmetric encryption, hashing, digital signatures, key derivation, secure random number generation, and public key infrastructure. Covers key lifecycle management, key exchange and distribution, choosing appropriate algorithms for a given constraint set including resource-constrained environments, and the forward-looking side of algorithm lifecycle: cryptographic agility and algorithm-migration strategy, forward secrecy, and the post-quantum cryptography transition and planning upgrades without breaking existing data or interoperability. The applied-crypto engineering layer, distinct from compliance-driven crypto standards.
Walk through integrating an application with an HSM using PKCS#11, and separately with a cloud KMS using the provider's SDK or KMIP. For each approach, describe the authentication pattern, key import vs key generation choices, signing vs export restrictions, how you handle session and concurrency management, and your typical error/retry strategy.
Sample Answer
Direct answer: Both integration paths solve the same underlying problem, letting an application use a private key for signing or decryption without ever holding the key material itself, but they differ almost everywhere in how: a hardware security module (HSM) accessed through PKCS#11 (a widely used standard API for talking to cryptographic hardware) exposes a stateful, session-based interface with hard concurrency limits, while a cloud key management service (KMS), accessed via the provider's SDK or the KMIP protocol (a separate, vendor-neutral standard for key management interoperability), exposes stateless API calls governed by a request-rate quota instead.
| Axis | PKCS#11 / HSM | Cloud KMS (SDK or KMIP) |
|---|---|---|
| Authentication pattern | Log into a "slot" (a logical partition on the HSM) with a PIN or password via C_Login, often backed by a physical credential or a partition-scoped user for higher assurance | Authenticate as the calling identity (an IAM role or service account) through the cloud provider's normal credential chain; authorization is enforced per key via a resource policy, not a separate device-login step |
| Key import vs. generation | Generate keys directly on the HSM (non-extractable from birth, the strongest guarantee) or import externally generated key material via a wrapped-import ceremony (import means the key existed in plaintext outside the HSM at some point, a weaker provenance guarantee) | Provider-generated keys (default, strongest) or Bring-Your-Own-Key (BYOK) import via the provider's public-wrapping-key ceremony; the same provenance trade-off applies |
| Signing vs. export restrictions | Enforced via key-object attributes that must be set correctly at creation or import time (CKA_EXTRACTABLE=false, CKA_SENSITIVE=true); a real, common misconfiguration is leaving a key extractable by accident | Non-extractability is the default, and often the only option for standard key types; export is available only for constrained cases like specific BYOK/HYOK workflows |
| Session/concurrency management | Sessions are a scarce, stateful resource with a hard hardware limit on concurrent logins; applications maintain a session pool, similar to a database connection pool, and must detect and transparently recover from session-invalid errors rather than crashing | Calls are stateless HTTPS or RPC requests with no session object at all; concurrency is governed by a request-rate quota instead, a fundamentally different scaling axis (session-limit-bound vs. rate-limit-bound) |
| Error/retry strategy | Distinguish a transient session error (retry after re-establishing the session) from a policy error (attempting to export a non-extractable key, or a PIN lockout after repeated bad logins) from a hardware failure (the unit is down, needs failover to a redundant unit in a high-availability cluster) | Distinguish throttling (retry with exponential backoff and jitter) from access-denied (a policy or permission problem, never blind-retry this) from a genuine service outage (check the provider's status page) |
Worked example. The PIN-lockout risk in the error/retry row is concrete enough to work through: if an HSM partition locks after, illustratively, three consecutive bad PIN attempts (a vendor-configurable policy, not a universal number), a retry loop that blindly re-attempts login on every transient session error without first checking why the session failed could exhaust that budget in well under a second and lock out the entire application, an outage worse than the original transient blip it was trying to recover from. This is exactly why the error/retry row above insists on classifying the error type before deciding to retry, for both integration paths.
Trade-offs and pitfalls. The single biggest practical difference between the two models is state. PKCS#11/HSM sessions are a finite, stateful resource that must be pooled and defended, much like database connections. Cloud KMS trades that operational burden for a request-rate quota and a network dependency on every single cryptographic operation. Choose, or combine the two in a hybrid arrangement, based on which failure mode your system is better equipped to detect and recover from.
Explain the differences between symmetric and asymmetric encryption. For each category, name two concrete algorithms you have used or implemented in production, describe typical system-level use cases (data at rest, key exchange, digital signatures), and discuss the performance, key-distribution, and operational trade-offs that push you toward one over the other. Where does envelope encryption and hardware-backed key storage fit into that choice?
Sample Answer
Direct answer
Symmetric encryption uses one shared secret key for both encrypting and decrypting (for example AES-256-GCM or ChaCha20-Poly1305): it is fast and cheap per byte, but both sides must already possess the same key. Asymmetric encryption uses a mathematically linked key pair, a public key anyone can hold and a private key only the owner holds (for example RSA-2048/3072 or elliptic-curve schemes like ECDH/Ed25519): it is orders of magnitude more expensive per byte, but it solves the problem of getting a shared secret to a party you have never met, and it enables digital signatures. Production systems almost never pick one exclusively; they combine both in a hybrid, envelope-encryption pattern.
Symmetric algorithms and use cases
- AES-256-GCM: an Advanced Encryption Standard (AES) block cipher run in Galois/Counter Mode (GCM), which gives you encryption and a built-in authentication tag in one pass. This is the default choice for encrypting data at rest: database columns, object storage, backups.
- ChaCha20-Poly1305: a stream cipher paired with a message-authentication code, popular where hardware AES acceleration is not available or reliable (some mobile chipsets) or where constant-time behavior in pure software matters. It is one of the two cipher suites every modern Transport Layer Security (TLS) 1.3 stack must support.
Asymmetric algorithms and use cases
- RSA (2048 or 3072-bit modulus): used for digital signatures (RSA-PSS) and, historically, for wrapping a symmetric key during key exchange. It is being phased out for key exchange because it provides no forward secrecy by default.
- Elliptic-Curve Diffie-Hellman Ephemeral (ECDHE), typically over curve X25519 or P-256: the workhorse of modern key exchange. A fresh key pair is generated per session and discarded afterward, which gives forward secrecy: compromising the server's long-term identity key later does not expose past session traffic.
Where each shows up
- Data at rest: symmetric (AES-256-GCM), because you encrypt large volumes repeatedly and asymmetric operations at that scale would be prohibitively slow.
- Key exchange: asymmetric (ECDHE, or historically RSA), because the two parties do not share a secret yet.
- Digital signatures: asymmetric (RSA-PSS or ECDSA/EdDSA), because only a private-key holder should be able to produce a valid signature that anyone with the public key can verify, which symmetric algorithms cannot do (both sides hold the same secret, so either could have produced it).
Worked example: why you cannot just use RSA for everything
RSA with Optimal Asymmetric Encryption Padding (OAEP) can only encrypt messages up to a fixed size, given directly by the padding scheme's own specification:
mLen≤k−2hLen−2
where k is the RSA modulus size in bytes and hLen is the padding hash's output size. For RSA-2048 (k = 2048/8 = 256 bytes) with SHA-256 padding (hLen = 32 bytes):
190=256−2(32)−2
A single RSA-2048 operation can protect at most 190 bytes directly, nowhere near enough for a 1 KB file, let alone a multi-gigabyte backup. That is the concrete reason production systems use envelope encryption instead of asymmetric encryption directly on bulk data.
Where envelope encryption and hardware-backed storage fit
Envelope encryption resolves that mismatch: generate a random symmetric data-encryption key (DEK), use it to encrypt the actual data with AES-256-GCM (fast, no size-limit problem), then encrypt (wrap) that small DEK with a separate key-encryption key (KEK) that never leaves a hardware security module (HSM) or a cloud key-management service (KMS). The wrapped DEK is stored next to the ciphertext; decrypting sends only the small wrapped DEK to the KMS/HSM to unwrap (one cheap operation), then does the bulk decryption locally. Hardware-backed storage matters because the KEK, whose compromise would expose every DEK it ever wrapped, is generated and used inside a tamper-resistant boundary designed to never export raw key material, so a compromised application server can ask the HSM to perform operations but cannot exfiltrate the key itself, and every such operation can be audited and rate-limited.
Trade-offs and pitfalls
- Performance: this is the trade-off the worked example above quantifies directly. Symmetric operations process bulk data at native memory speed with hardware acceleration; a single asymmetric operation like RSA-2048-OAEP costs orders of magnitude more per byte and, as shown, can only touch 190 bytes at a time, which is precisely why performance-sensitive systems route bulk work through the symmetric side of the hybrid design.
- Key distribution: symmetric-only designs need every pair of communicating parties to share a secret in advance, which does not scale (N parties need on the order of N-squared pairwise keys); asymmetric key exchange scales linearly because a public key can be published openly.
- Operational and security-level scaling: asymmetric key sizes must grow much faster than symmetric ones for equivalent strength; roughly, matching AES-128's strength needs an RSA modulus around 3072 bits, and matching AES-256 needs a modulus in the tens of thousands of bits, which is why elliptic-curve schemes (far smaller keys for equal strength) have displaced RSA for key exchange, and why operating a large RSA-based public key infrastructure carries more certificate and bandwidth overhead than an equivalent elliptic-curve one.
- Never hand-roll a mode: use a vetted library's authenticated modes (AES-GCM, ChaCha20-Poly1305) rather than composing an unauthenticated cipher yourself; unauthenticated ciphertext is silently malleable.
Describe the purpose of a salt in password-based key derivation: what properties it needs (uniqueness, length, randomness), where it should be stored relative to the derived hash, and the operational risks of reusing or omitting it at scale. Then explain the difference between a salt and a pepper: how does adding a server-side secret pepper change the threat model for offline attacks, and what controls does protecting a pepper actually require?
Sample Answer
Direct answer
A salt is a unique, random, non-secret value stored alongside each password hash so identical passwords never produce identical stored hashes and precomputed lookup tables (rainbow tables) become useless. A pepper is a separate, secret value, shared across all users and stored outside the database, that adds a layer an attacker who only stole the database, and not the pepper's separate storage, cannot reproduce at all.
Salt properties
- Uniqueness: every stored hash gets its own salt, so no two hashes can ever be compared or looked up in a shared precomputed table, even if two users chose the same password.
- Length and randomness: generated fresh with a cryptographically secure random number generator, long enough (16 bytes is a common minimum) that collisions across an entire user base are effectively impossible.
- Storage: the salt is not secret and is stored right alongside the derived hash, often concatenated into the same stored string as most KDF libraries' output format does, because verification needs it: a login attempt cannot be checked without knowing which salt was used.
Operational risks of reusing or omitting a salt at scale
Reusing one salt across many users collapses back to the pre-salt world for that group: identical passwords produce identical hashes again, so one cracked hash cracks every account sharing that password. Omitting a salt entirely makes precomputed rainbow-table attacks viable against an entire user base at once, and is the single most common real-world password-storage mistake salting exists to prevent.
Salt vs pepper
A pepper adds an ingredient a salt deliberately lacks: it is the same value for every user, unlike a salt, and it is never stored in the same place as the password hashes, typically in application configuration, a secrets manager, or a hardware security module (HSM), specifically so that a database breach alone, a SQL injection or a stolen backup, does not hand an attacker everything needed to attempt offline cracking.
How a pepper changes the threat model
Without a pepper, stealing the password-hash database is sufficient to start an offline brute-force or dictionary attack immediately. With a pepper, the attacker also needs to compromise wherever the pepper is stored, a genuinely separate system with its own access controls, before offline attacks become possible at all; a database-only breach becomes far less immediately damaging.
Controls a pepper actually requires
A pepper only helps if it is genuinely harder to steal than the database: its own access control (ideally an HSM/KMS-backed secret, not an environment variable sitting next to application code that also has database access), its own rotation plan (rotating a pepper means re-verifying and re-deriving every stored hash, a real migration, not a config change), and monitoring, since a pepper stored in the same place, or reachable via the same compromise path, as the database it protects provides no real additional security at all.
What is a Key Management Service, and what does the full key-management lifecycle look like for symmetric and asymmetric keys in an enterprise environment? For each stage (generation, provisioning, storage/usage, rotation, revocation, archival, secure destruction), name concrete controls and automation options you'd expect a KMS or HSM to provide, and the audit/logging you'd want at each stage. Compare cloud-managed KMS, HSM-backed KMS, and self-hosted key stores (like HashiCorp Vault) from an operational and decision-criteria standpoint: when would you reach for each?
Sample Answer
Direct answer
A key-management service (KMS) generates, stores, and controls the use of cryptographic keys without ever handing raw key material to the applications that use it: applications send data (or a wrapped key) and get back a result, never the key itself. Whether that KMS is a cloud-managed service, an on-premises hardware security module (HSM), or a self-hosted secrets manager is a decision about who controls the boundary the key never crosses, not about whether encryption happens.
The key lifecycle, stage by stage (the stages are the same for symmetric and asymmetric keys; only the storage/usage operations differ)
| Stage | What happens | Controls and automation | Audit and logging |
|---|---|---|---|
| Generation | Key material is created with a cryptographically secure random number generator, ideally inside the HSM/KMS boundary | Hardware random-number generator inside the HSM; enforced key-length/algorithm policy | Who requested generation, for what purpose, and the resulting key identifier and version |
| Provisioning | The key is made available to authorized services | Access policy binding the key to specific roles/services (least privilege); for asymmetric keys, distributing only the public half where possible | Every policy grant or change |
| Storage and usage | The key sits encrypted at rest, often wrapped by a KMS/HSM master key, and is used via API calls (encrypt/decrypt/sign/verify) rather than exported | Usage restricted to specific operations (a signing key that can only sign, never decrypt); rate limiting and anomaly detection on usage volume | Every single use: caller identity, operation, timestamp, and key version |
| Rotation | The active key is periodically replaced by a new version while old versions remain available to decrypt or verify data they already protected | Automated rotation schedules; for symmetric data-encryption keys, a key-encryption-key hierarchy so rotating the top key does not require touching the data | Rotation events and which version range remains valid for verification |
| Revocation | A key is marked untrusted before its normal retirement, typically on suspected compromise | Immediate propagation to every relying party's trust/verification list | Revocation reason, initiator, and propagation confirmation across systems |
| Archival | Retired keys are kept, encrypted, for as long as needed to decrypt or verify historical data, but are no longer used for new operations | Read-only access policy; a separate, more restrictive storage tier | Every archival access, since it is rarer and more suspicious by default |
| Secure destruction | Key material is irrecoverably deleted; cryptographic shredding is often the practical version of "destroying" data you cannot otherwise find and erase | Dual control (two-person authorization) before permanent deletion; a mandatory waiting or appeal window | The destruction request, dual-control approvals, and a permanent record that the key existed and was destroyed on a given date |
Comparing the three approaches
| Option | Where the key boundary lives | Best when |
|---|---|---|
| Cloud-managed KMS | Inside the cloud provider's infrastructure; you control policy, not physical hardware | You want low operational overhead and tight integration with the rest of a cloud provider's services, and accept its shared-responsibility model for the hardware layer |
| HSM-backed KMS | A dedicated hardware module, often validated against FIPS 140-3 (the current U.S. federal cryptographic-module standard; FIPS 140-2 certificates move to historical status by September 2026) | Regulatory or contractual requirements demand a specific, auditable hardware assurance level, or you need "hold your own key" control where the provider never sees the key at all |
| Self-hosted key store (for example HashiCorp Vault) | Entirely within your own infrastructure | You need full control over the software stack and deployment model, operate outside a single cloud provider, or have requirements a shared multi-tenant service cannot satisfy, at the cost of owning the operational burden yourself |
Worked example
A data platform storing per-tenant records typically holds a small number of key-encryption keys in the KMS/HSM (one or a handful per tenant, or a shared key with per-tenant data-encryption keys) and generates a fresh data-encryption key per object or per batch; only that small key ever needs to be sent to the KMS to unwrap, so the KMS's per-operation cost stays proportional to the number of distinct data-encryption keys, not the volume of underlying data.
Trade-offs and pitfalls
Do not conflate "we use a cloud KMS" with "we have a FIPS 140-3 validated system": the certification applies to a specific hardware/firmware module at a specific validation level (1 through 4), not automatically to every service built on top of it or to your own key-management practices. The most common real-world gap is not choosing the wrong option above, it is under-provisioning the audit-logging stage: if you cannot answer who used a key, when, and for what, after the fact, the KMS choice does not matter.
Design a migration strategy to move a user database from PBKDF2 to Argon2id without forcing a mass password reset. Cover the schema changes needed to version hashes, the authentication-flow change that detects an old hash and re-hashes on successful login, options for migrating accounts that never log in again, and the metrics you'd watch to confirm the migration is succeeding.
Sample Answer
Direct answer
Store the algorithm and its parameters alongside every hash so old (PBKDF2) and new (Argon2id) records are distinguishable, verify a login against whichever algorithm the stored hash says it used, and silently re-hash with Argon2id the moment a login succeeds, since a successful login is the one moment the plaintext password is legitimately available in memory to spend the cost of the new, stronger hash on.
Schema changes to version hashes
Store a self-describing hash string rather than a bare hash value: the algorithm identifier, its parameters, the salt, and the derived hash, all together, for example the widely used PHC string format ($argon2id$v=19$m=65536,t=3,p=4$<salt>$<hash>) versus a PBKDF2 equivalent naming its iteration count. This avoids a separate "algorithm version" column that could drift out of sync with the actual stored value; parsing the stored string alone tells you everything needed to verify it.
Authentication-flow change: detect old hash, re-hash on success
- On login, parse the stored hash string to determine which algorithm and parameters it used.
- Verify the submitted password against that algorithm (PBKDF2 for old records, Argon2id for already-migrated ones).
- If verification succeeds and the stored hash is not already the current target algorithm/parameters, immediately derive a new Argon2id hash from the plaintext password, available only during this one request, and overwrite the stored record with the new hash string.
- If verification fails, leave the stored record untouched; a failed login gives no legitimate opportunity to touch it, and doing so would be actively wrong.
Accounts that never log in again
Some accounts never trigger the natural re-hash-on-login path because they simply stop logging in. Options, usually combined: leave them on the old algorithm indefinitely, accepting the residual risk since these tend to also be the least active, lowest-value targets, with a monitoring dashboard tracking what fraction of the user base remains un-migrated over time; force a password reset for accounts that have not logged in, and therefore not migrated, after a defined long window, one of the rare cases where forcing a reset for a subset of users, rather than the whole base, is reasonable, trading a small, bounded friction against an indefinite tail of weakly hashed accounts; or, for genuinely dormant accounts past a retention policy, consider whether they should simply be deactivated rather than migrated at all.
Metrics to confirm the migration is succeeding
- Percentage of the user base still on the old algorithm, trending down over time as logins naturally trigger re-hashing; a stalled or flat trend signals the natural mechanism alone will not finish in a reasonable timeframe and the forced-reset path for dormant accounts needs to trigger sooner or more aggressively.
- Error rate on the re-hash step itself, distinct from the login verification step, since a bug here could silently fail to upgrade accounts while users still log in successfully, hiding the problem from ordinary login-success monitoring.
- Latency impact on logins that include a re-hash, since Argon2id's memory-hard computation adds real cost to that one request; this should show up as a small, bounded latency bump specifically on migrating logins, not a general regression across all logins.
Trade-offs and pitfalls
Never derive the new hash from anything except the plaintext password already available during a successful verification; there is no way to re-hash an existing PBKDF2 output into an Argon2id one without the original plaintext, since these are one-way functions by design. If the login endpoint is heavily rate-limited or load-shed under peak traffic, remember the re-hash step adds real, non-trivial compute cost, that is the point of Argon2id, so capacity-plan for a transition period where a meaningful fraction of logins carry that extra cost, not just the eventual steady state once migration is complete.
Unlock Full Question Bank
Get access to all 16 Applied Cryptography and Key Management interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.