Cryptography questions separate engineers who can name algorithms from engineers who can be trusted to design a system that handles money, health records or credentials. At the senior level, interviewers rarely ask you to implement AES from scratch — they ask you to reason about threat models: what happens when a database leaks, when a key is compromised, when a certificate expires at 2 a.m., or when a regulator asks how you'd migrate off an algorithm that's about to be broken. This page covers the questions that come up most often in senior and architect loops: hashing versus encryption, password storage, key management with Key Vault and HSMs, rotation, the ASP.NET Core Data Protection API in a web farm, TLS and certificates, and where post-quantum cryptography actually stands today.
Q1 Explain the difference between hashing, encryption and encoding. Why is "we encrypt passwords" the wrong answer in an interview?#
Short answer: Encoding (Base64, URL encoding) is a reversible transformation with no key and no security value — anyone can decode it. Encryption is reversible with a key, so it's for data you need to read back later. Hashing is one-way by design, so it's for data you only ever need to verify, which is exactly what a password is.
The moment you say "we encrypt passwords," you've told the interviewer that your system can recover a user's plaintext password — which means a compromised key, a rogue admin, or a subpoena of your key material exposes every password in the database, not just the hashes. A password should never need to be read back; the system only ever needs to answer "does this input match what the user originally chose?" That's a verification problem, not a confidentiality problem, so it belongs to a slow, salted key derivation function (KDF), not a cipher. General-purpose hashes like SHA-256 are the wrong tool too, even though they're one-way: they're engineered to be fast, so an attacker with a leaked hash list can test billions of guesses per second on commodity GPUs. A password hash needs to be deliberately slow and, ideally, memory-hard.
// Wrong: fast, general-purpose hash — crackable at billions of guesses/second
byte[] wrong = SHA256.HashData(Encoding.UTF8.GetBytes(password));
// Right: a slow, salted key derivation function tuned for password storage
byte[] salt = RandomNumberGenerator.GetBytes(16);
byte[] right = Rfc2898DeriveBytes.Pbkdf2(password, salt, 210_000, HashAlgorithmName.SHA512, 32);What interviewers look for: whether you treat "reversible vs. one-way" as the load-bearing distinction, and whether you can explain why a fast hash is dangerous for passwords specifically, not just that it's "less secure."
Common mistakes: saying "hashing and encryption are basically the same, just different algorithms"; using Base64 and calling it "encoding for security"; storing passwords with a single global salt instead of one salt per user.
Q2 How would you design password storage for a new system today, and how would you migrate users off a weaker scheme later without a mass password reset?#
Short answer: Use a memory-hard KDF — Argon2id if you can choose freely, PBKDF2-HMAC-SHA256/512 if you need FIPS-validated cryptography — with a unique random salt per user, tuned so a single hash takes on the order of 100-300ms on your production hardware, and version the hash format so you can raise the cost parameters or swap algorithms later without forcing a reset.
ASP.NET Core Identity's built-in PasswordHasher<TUser> is a good reference design: its current format (V3) uses PBKDF2 with HMAC-SHA512, a 128-bit salt and a 256-bit derived key, defaulting to 100,000 iterations, configurable through PasswordHasherOptions.IterationCount. The part senior candidates often miss is the migration path: VerifyHashedPassword doesn't just return success or failure — it can return PasswordVerificationResult.SuccessRehashNeeded when the stored hash was produced with fewer iterations (or a weaker PRF) than the current configuration calls for. The application checks that result after a successful login and transparently re-hashes the password with current parameters, so your entire user base migrates to stronger settings over time, one login at a time, with no forced reset and no plaintext ever touching disk twice.
var result = hasher.VerifyHashedPassword(user, user.PasswordHash, suppliedPassword);
if (result == PasswordVerificationResult.SuccessRehashNeeded)
{
user.PasswordHash = hasher.HashPassword(user, suppliedPassword);
await userStore.UpdateAsync(user); // opportunistic upgrade on login
}If you're not on Identity, the same pattern applies with a community Argon2id implementation (for example, Konscious.Security.Cryptography.Argon2): store the algorithm name, cost parameters and salt alongside the hash so old rows remain verifiable while new rows use stronger settings.
What interviewers look for: that you know a concrete, current default (not "bcrypt, I think") and that you have an actual plan for raising cost parameters over the life of the system, since "what we chose in year one" is rarely what you want in year five.
Common mistakes: hardcoding iteration counts with no version marker in the stored hash, so you can never safely change them; picking an iteration count once in dev on fast hardware and never re-tuning it for production load.
Q3 Walk through how ASP.NET Core's Data Protection API works, and what breaks when you deploy behind a load balancer without extra configuration?#
Short answer: Data Protection is ASP.NET Core's general-purpose API for encrypting data you need to decrypt later — antiforgery tokens, cookies, TempData — built around a key ring of time-boxed keys with a default 90-day lifetime. By default the keys are written to local disk and, on Windows, encrypted at rest with DPAPI; on a farm of machines that each have their own local key ring, node A can't decrypt a payload node B encrypted, so authentication cookies and antiforgery tokens randomly fail depending on which instance handles a given request.
The fix is to make the key ring shared and machine-independent instead of per-instance: persist keys to a location every instance can reach — a UNC share, Azure Blob Storage, Redis, or a database via PersistKeysToDbContext — and protect them at rest with something that isn't tied to one machine's DPAPI store, typically an X.509 certificate or Azure Key Vault via ProtectKeysWithAzureKeyVault. If multiple distinct applications share that same key repository and need to read each other's protected payloads (rare, but it happens with shared session state), you also call SetApplicationName with an identical value in each app, because by default Data Protection isolates apps from each other using the app's content-root path as a discriminator, even when they point at the same key store.
builder.Services.AddDataProtection()
.SetApplicationName("orders-api")
.PersistKeysToAzureBlobStorage(new Uri("https://contoso.blob.core.windows.net/keys/keys.xml"), credential)
.ProtectKeysWithAzureKeyVault(new Uri("https://contoso.vault.azure.net/keys/dp-key"), credential);What interviewers look for: recognizing that the failure mode in a farm is intermittent, not a hard crash — "works on one box, fails randomly behind the load balancer" is the signature symptom, and naming it unprompted is a strong signal.
Follow-up questions:
- What happens to already-issued cookies during a key rotation, and why doesn't rotation invalidate them immediately?
- Why is Data Protection the wrong tool for data you need to keep readable for years, independent of the app's key ring lifecycle?
Q4 Compare symmetric and asymmetric encryption. When does a real system need both, and what does AES-GCM give you that AES-CBC doesn't?#
Short answer: Symmetric encryption (AES) uses one shared key for both directions and is fast enough for bulk data; asymmetric encryption (RSA, ECDH) uses a public/private key pair so two parties can establish trust or a shared secret without ever transmitting a shared key, but it's orders of magnitude slower and size-limited. Real systems almost always combine them — TLS is the canonical example — and AES-GCM adds authentication on top of confidentiality, which AES-CBC does not.
Hybrid encryption works like this: use asymmetric cryptography (or a key-exchange protocol like ECDHE) only to agree on or wrap a short-lived symmetric key, then do the actual bulk encryption with that symmetric key using a fast, authenticated mode. AES-CBC only provides confidentiality — an attacker who flips bits in the ciphertext produces garbled-but-valid-looking plaintext after decryption, which is how padding-oracle attacks against CBC (like the classic ones against unauthenticated CBC + PKCS7 padding) become possible. AES-GCM is an AEAD (Authenticated Encryption with Associated Data) mode: it produces an authentication tag alongside the ciphertext, so any tampering causes decryption to fail outright instead of silently succeeding with corrupted data, and it also lets you bind unencrypted "associated data" (like a header or a key ID) to the ciphertext without encrypting it.
using var aes = new AesGcm(key, AesGcm.TagSizeInBytes);
byte[] nonce = RandomNumberGenerator.GetBytes(AesGcm.NonceByteSizes.MaxSize);
byte[] tag = new byte[AesGcm.TagSizeInBytes];
byte[] ciphertext = new byte[plaintext.Length];
aes.Encrypt(nonce, plaintext, ciphertext, tag);
// Store nonce, ciphertext and tag together; decryption throws if the tag doesn't verify.What interviewers look for: the authenticated-vs-unauthenticated distinction specifically — it's the detail that tells you whether a candidate has actually debugged a crypto implementation rather than just read the acronyms.
Common mistakes: reusing a nonce with the same AES-GCM key (which catastrophically breaks GCM's confidentiality guarantees), or defaulting to CBC out of habit without adding a separate HMAC for integrity.
Q5 What's the practical difference between Azure Key Vault and a Hardware Security Module, and when do you actually need HSM-backed keys?#
Short answer: Key Vault is a managed service for storing and controlling access to secrets, keys and certificates, backed by software or HSM protection depending on the tier; an HSM is the underlying tamper-resistant hardware that generates and holds key material so the private key material itself never leaves the device, not even to the cloud provider's own operators.
Standard-tier Key Vault stores keys in software-protected storage, which is sufficient for most application secrets and even most encryption keys — it still enforces access policies, RBAC, auditing and network restrictions. Premium-tier Key Vault backs keys with FIPS 140-2 Level 2 validated HSMs, and Azure Managed HSM goes further, giving you a single-tenant HSM pool with FIPS 140-2 Level 3 validation, full administrative control and dedicated capacity, for organizations under regulatory mandates (payment card key management, certain government or financial workloads) that specifically require HSM-level assurance and isolation from other tenants. The practical decision isn't "HSM is always better" — it's cost and operational complexity versus a specific compliance requirement: if no regulation or customer contract requires HSM-backed keys, software-protected Key Vault with strict access policies, private endpoints and Managed Identity is the right default, and you reach for Premium or Managed HSM when an auditor specifically asks for it or when you're doing high-volume signing where an HSM's throughput and isolation guarantees matter.
What interviewers look for: that you don't default to "always use an HSM" as a reflexive best practice — cost-aware, requirement-driven decisions are what separates an architect answer from a checklist answer.
Common mistakes: conflating "Key Vault" with "HSM" as if they're the same thing, or assuming Key Vault Standard provides no meaningful protection just because it isn't HSM-backed.
Follow-up questions:
- How would you grant an App Service access to a Key Vault secret without storing a connection secret anywhere?
- What's the operational cost of losing access to an HSM's key material versus a software-protected key?
Q6 How do you design a key rotation strategy that doesn't break already-encrypted data or already-issued tokens?#
Short answer: Rotation only works cleanly if you never delete the old key the moment you mint a new one — you keep a versioned key ring where new writes use the newest key, but decryption/verification can still resolve older, still-valid key versions by an embedded key identifier, until everything encrypted or signed under the old key has naturally expired or been re-encrypted.
Concretely, every ciphertext or signed token should carry a key identifier (a kid-style field) alongside the payload, not just the raw bytes — that's what lets a verifier pick the right key version instead of guessing. Azure Key Vault supports this natively for keys and secrets: you can rotate to a new version while old versions remain retrievable, and ProtectKeysWithAzureKeyVault with a versionless key identifier lets Data Protection or your own crypto code pick up rotated key material automatically without a redeploy. For data actively at rest — encrypted columns, blobs — rotation typically means re-encrypting on a rolling basis (read with the old key, write with the new one) rather than a single cutover, because you rarely control every consumer's exact access pattern well enough to guarantee nothing is mid-flight. For signing keys behind tokens (JWTs, cookies), the standard approach is to publish both the old and new public keys during an overlap window — via a JWKS endpoint, for Data Protection its own key ring metadata — so tokens signed just before rotation still verify until they expire naturally.
// Key Vault: rotate by creating a new version; old versions stay retrievable for
// anything still encrypted or signed with them until you explicitly disable them.
KeyVaultKey newVersion = await keyClient.CreateKeyAsync("payment-encryption-key", KeyType.Rsa);What interviewers look for: understanding rotation as a staged, overlapping process rather than an atomic swap, and specifically how the system tells old and new key material apart at verification time.
Common mistakes: rotating a key and immediately revoking the old one, which instantly breaks every payload encrypted under it; treating rotation as purely a compliance checkbox with no plan for re-encrypting data at rest.
Q7 Walk through a TLS handshake at the level you'd use to debug a production certificate failure. What roles do the certificate chain and revocation checking play?#
Short answer: The client and server negotiate a protocol version and cipher suite, the server presents its certificate chain, the client validates that chain up to a trusted root and checks it hasn't been revoked, then (in TLS 1.3) an ephemeral Diffie-Hellman exchange derives a shared symmetric key that encrypts everything from that point on — most "TLS is broken" incidents are actually chain-validation or clock-skew failures, not cryptographic ones.
For a production incident, the chain is usually where you start: a server certificate is signed by an intermediate CA, which is signed by a root CA the client already trusts; if the server doesn't send the intermediate certificate (a very common misconfiguration), most browsers succeed anyway because they can often fetch it, but many non-browser clients and older TLS stacks fail outright with a chain-validation error — that's the classic "works in the browser, fails from the service" bug. Revocation checking (CRLs or OCSP) is the next suspect: if a client can't reach the CA's OCSP responder — often because of outbound firewall rules in a locked-down environment — some TLS stacks fail closed, and OCSP stapling exists specifically to avoid that dependency by having the server periodically fetch its own revocation status and staple it into the handshake, so the client never needs a live connection to the CA. TLS 1.3, which .NET has supported for a while and which .NET 10 extended with support for TLS 1.3 on macOS clients, also removed several legacy negotiation steps and older cipher suites entirely, which is why a client pinned to very old ciphers can fail to connect to a server that only offers modern suites — the fix there is upgrading the client stack, not weakening the server.
What interviewers look for: a debugging mental model (chain, then trust, then revocation, then protocol/cipher negotiation) rather than a recitation of the handshake steps from a textbook.
Common mistakes: assuming "the cert is valid in the browser" proves the chain is correctly configured server-side; not distinguishing an expired-certificate failure from a revoked-certificate failure from a hostname-mismatch failure, which each need a different fix.
Q8 What is post-quantum cryptography, and what should a .NET team actually be doing about it today?#
Short answer: Post-quantum cryptography (PQC) replaces the asymmetric algorithms (RSA, ECDH, ECDSA) that a sufficiently large quantum computer could break, with algorithms believed to resist quantum attacks; .NET 10 shipped concrete support for the three NIST-standardized PQC algorithm families, so this has moved from "future concern" to "an API you can call today," even though most teams don't need to migrate production traffic yet.
.NET 10 introduced System.Security.Cryptography.MLKem, MLDsa and SlhDsa, implementing NIST's ML-KEM (FIPS 203, key encapsulation — the asymmetric replacement for RSA/ECDH key exchange), ML-DSA (FIPS 204, digital signatures) and SLH-DSA (FIPS 205, a hash-based signature scheme as a conservative fallback). Unlike the older AsymmetricAlgorithm-derived types, these use static factory methods (GenerateKey, ImportFromPem) rather than the classic "create then import" pattern, and each exposes an IsSupported property because availability depends on the underlying platform crypto library — OpenSSL 3.5+ on Linux/macOS, or Windows CNG with PQC support, which .NET 10 also added. .NET 10 further added CompositeMLDsa, implementing the IETF's composite-signature draft that pairs ML-DSA with a classical algorithm like RSA in a single signature, which is the pragmatic near-term deployment pattern: you get quantum resistance without dropping a classical algorithm that auditors, hardware and interop partners still expect. Most of these types are marked [Experimental] under diagnostic SYSLIB5006 because the underlying IETF/NIST specifications are still settling — which is exactly why the realistic answer for most teams isn't "migrate now," it's "know where your long-lived, high-value secrets and signatures are (things that need confidentiality for 10+ years), track algorithm agility in your own key-handling code so a future swap isn't a rewrite, and evaluate composite signatures for anything that needs to start hedging today."
What interviewers look for: whether you know PQC has already shipped concretely in .NET rather than treating it as purely theoretical, and a realistic, risk-based take on urgency rather than "we should rewrite everything in ML-KEM now."
Follow-up questions:
- Why does key exchange (ML-KEM) matter more urgently than signatures (ML-DSA) for data being harvested today and decrypted later?
- What does "crypto-agility" mean in a codebase, and how would you retrofit it into a system that hardcodes
RSA.Create()everywhere?
Q9 What's wrong with secrets in appsettings.json, environment variables or source control — and what's the right pattern in Azure?#
Short answer: Anything committed to source control is effectively permanent — it lives in history even after you delete it — and both appsettings.json and plain environment variables are readable by anything with file-system or process access on the host, so none of them provide access control, rotation, or an audit trail; the right pattern is a managed secret store the app authenticates to with an identity it already has, not a credential it has to protect.
In Azure, that means Key Vault plus Managed Identity: the app authenticates to Azure AD as itself (no client secret to leak, no connection string to rotate) and Key Vault enforces fine-grained access policies or RBAC on top, with every read audited. For local development, ASP.NET Core's user-secrets tool (dotnet user-secrets) keeps secrets out of the repo entirely, stored outside the project tree, while IConfiguration picks them up the same way it would pick up Key Vault values in production — so the code doesn't branch on environment. The same principle generalizes beyond Azure: HashiCorp Vault, AWS Secrets Manager and GCP Secret Manager all offer the same shape — identity-based access, rotation, and audit logs — versus a static string sitting in a config file or a CI variable that anyone with pipeline access can read in plaintext.
builder.Configuration.AddAzureKeyVault(
new Uri("https://contoso.vault.azure.net/"),
new DefaultAzureCredential());What interviewers look for: identifying the access control and audit gap, not just "it's in plaintext" — a config file protected by file-system permissions is still missing rotation, scoping and a record of who read what and when.
Common mistakes: treating environment variables as inherently safe because they're "not in the file"; putting secrets in CI/CD pipeline variables without restricting which pipelines and branches can read them.
Q10 Explain digital signatures versus HMACs. How do you decide which one fits a given integrity/authenticity problem, like a JWT or a webhook payload?#
Short answer: An HMAC proves integrity and authenticity to anyone who holds the same shared secret — so it only works when both sides already trust each other with that secret. A digital signature (RSA, ECDSA, ML-DSA) uses a private key to sign and a public key to verify, so anyone can confirm authenticity without ever being able to forge a signature themselves — the right choice whenever the verifier shouldn't also be able to produce valid tokens.
This is exactly the trade-off behind JWT's alg header: HS256 (HMAC-SHA256) is appropriate when one service issues tokens and the same service (or a tightly-trusted set of services holding the same secret) is the only verifier — a single API validating its own session tokens, for example. RS256/ES256 (RSA/ECDSA signatures) are the right choice the moment verification needs to happen somewhere that shouldn't be able to issue valid tokens — multiple microservices verifying tokens from a central identity provider, or a third party verifying a webhook payload you sent them. Using HMAC in that second scenario is a real vulnerability class: if every microservice that needs to verify a token also holds the signing secret, any one of them (or anything that compromises any one of them) can forge tokens for every other service. Webhook signing follows the same logic in reverse but the principle is identical: the sender holds a shared secret and HMAC-signs the payload (most webhook providers use HMAC-SHA256 over the raw body plus a timestamp, to also prevent replay), and the receiver, who already shares that secret out-of-band, verifies it — signatures aren't needed there because sender and receiver are a fixed, pre-agreed pair.
What interviewers look for: the "who can verify vs. who can forge" framing specifically — it's the detail that explains every real HMAC-vs-signature decision, rather than "signatures are asymmetric and HMACs are symmetric" as an isolated fact.
Common mistakes: using HS256 JWTs across multiple independently-deployed services that all need the shared secret; forgetting to include a timestamp or nonce in an HMAC-signed webhook payload, which allows replay even though the signature itself is valid.
Quick-Fire Round#
| Question | Answer |
|---|---|
| What's the default key lifetime in ASP.NET Core Data Protection? | 90 days. |
Which PasswordVerificationResult value signals an opportunistic rehash on login? | SuccessRehashNeeded. |
What NIST standard does .NET's MLKem type implement? | ML-KEM, FIPS 203 (key encapsulation). |
| What does AES-GCM add that AES-CBC lacks? | Built-in authentication (an AEAD tag), not just confidentiality. |
| What method shares a Data Protection key ring's protected payloads across distinct apps? | SetApplicationName, set identically in each app. |
| What Azure Key Vault tier is backed by FIPS 140-2 Level 3 HSMs with single-tenant isolation? | Managed HSM. |
| What lets a server avoid depending on a live OCSP connection during a TLS handshake? | OCSP stapling. |
| Why is HMAC the wrong choice for a token verified by many independent services? | Every verifier also holds the signing secret and can forge tokens. |
| What .NET 10 type pairs a post-quantum signature with a classical one in one signature? | CompositeMLDsa. |
| What ASP.NET Core tool keeps local development secrets out of source control? | dotnet user-secrets. |
How to Prepare#
- Be able to state one concrete, current password-hashing default (algorithm, salt size, iteration count) instead of a vague "use bcrypt or something."
- Practice the Data Protection web-farm failure mode as a story: symptom (intermittent auth failures), root cause (per-instance key ring), fix (shared store plus
SetApplicationName). - Know the difference between Key Vault (a managed service) and an HSM (the hardware backing it), and when regulation — not habit — should drive you to Premium or Managed HSM.
- Rehearse key rotation as a staged, overlapping process with a key identifier in every payload, not an atomic swap.
- Be ready to name .NET's concrete post-quantum types (
MLKem,MLDsa,SlhDsa,CompositeMLDsa) and give a risk-based, not alarmist, view of urgency. - Have a real TLS debugging story ready — chain, trust, revocation, protocol — since it's asked in almost every architect-level security loop.