Cryptography for Developers
Make safer engineering decisions about encoding, password storage, encryption, nonces, and signature verification.
Start here
Course introduction
Most application cryptography failures are selection, storage, or verification mistakes rather than broken mathematics. This course focuses on those engineering decisions with short evidence reviews rather than mathematical puzzles.
The examples are intentionally language-neutral. In production, use a maintained library and a documented high-level construction instead of implementing a primitive yourself.
By the end of this course, you can
- Select a cryptographic primitive based on the property the feature needs.
- Recognize unsafe nonce reuse, key rotation, and unverified-token patterns.
- Put verification, validation, and authorization in the right order.
- Choose failure behavior that does not expose a useful oracle.
Learn, then practice
Course lessons
Work through these 5 lessons in order. Each lesson introduces one concept, then gives you a practical activity or lab to apply it.
Name the property you need
Not startedEncoding, hashing, encryption, and signing solve different problems even when their output looks similarly unreadable.
Encoding changes representation and is reversible without a secret. Hashing creates a fixed-size digest. Encryption protects confidentiality with a key. A signature lets someone verify origin and integrity.
Start by naming the property. If the requirement is password verification, reversible encryption is the wrong primitive. Use a password-hashing function designed to be slow and salted.
c2VjcmV0LXRva2Vu → secret-token
Base64 is useful for transport. Anyone can decode it, so it provides no confidentiality.
Course challenge
Review the password record
This record must support password verification without allowing recovery of the original password. Enter the missing primitive.
Need: verify a submitted password
Must not: recover the original password
Per-record random salt: yes
Primitive: ?
Uniqueness is part of the design
Not startedModern encryption modes depend on nonces or initialization values that must follow precise reuse rules.
Developers tend to protect the key and treat the nonce as decoration. In several common modes, reusing a nonce with the same key can reveal relationships between messages or break authentication.
Let a maintained cryptographic library generate the nonce. Store it beside the ciphertext, and design rotation so keys and encrypted records remain traceable.
Course challenge
Find the unsafe event
Compare the mock encryption log. Enter the event ID that reused a nonce with the same key.
evt-101 key=k7 nonce=8af2
evt-102 key=k7 nonce=c410
evt-103 key=k7 nonce=8af2
Verify before use
Not startedEncrypted or signed data is safe only when verification happens before the application acts on its contents.
Authenticated encryption produces ciphertext and an authentication tag. If tag verification fails, treat the whole message as untrusted and return one consistent error.
The same order matters for tokens and signed payloads. Pin the accepted algorithm and key source, verify the signature, validate expiry and audience, then use claims for authorization.
payload = verify_and_decrypt(message, expected_key)
if payload is invalid: reject
if payload.audience != this_service: reject
process(payload)
Parsing unverified claims for a security decision puts trust before proof.
Before moving on, find the exact line in your own application where verification finishes and business logic begins.
Course challenge
Rebuild the trust boundary
Put the token-processing steps in the only order that avoids using untrusted claims.
Select the steps in the order they should happen.
Rotate with key identities
Not startedRotation works when every ciphertext or signature can be connected to the key version that created it.
Give each key version a stable identifier and store that identifier beside the protected data. During rotation, write with the new key while retaining the old key only for the reads that still require it.
Removing the old key before data is migrated creates an outage. Keeping it active for new writes means rotation never really finishes.
Course challenge
Select the right key version
The record was created before rotation and carries its key identifier. Enter the key the service must use to decrypt it.
{
"created": "2026-06-04T10:16:00Z",
"key_id": "key-v2",
"ciphertext": "b7a91f..."
}
Do not build an oracle
Not startedDetailed cryptographic errors can reveal which part of a protected message was valid.
Return one consistent failure to the caller when signature, padding, tag, or format validation fails. Keep the detailed reason in restricted internal logs.
Also keep the rejection path reasonably consistent. An attacker should not be able to distinguish a valid signature from a valid account by comparing messages or timing.
Course challenge
Choose the safe failure
A token fails verification. Which response gives the caller the least useful oracle?
Continue learning
Choose your next path
You have reached the end of this path. Pick another topic from the course catalog when you are ready.