Engineer's toolkit
Public-key crypto and signatures
Key pairs let strangers agree on a secret and let anyone verify a signature. Why elliptic curves are the default, what ECDHE gives you, and when a signature beats an HMAC.
Symmetric crypto needs both sides to hold the same secret, and you can't email that secret safely. Public-key cryptography solves this with a key pair. The public key can be shared with anyone. The private key never leaves its owner. Two uses matter most in daily work: agreeing on a shared secret, and signing.
The big idea
A signature is made with the private key and checked with the public key. Anyone can verify it, but only the key holder can make it. That gives authenticity, integrity and non-repudiation. An HMAC can't give the last one, because the verifier holds the same key and could have forged the tag.
Use a signature when verifiers should not be able to sign. Examples:
- A TLS certificate is a certificate authority's signature over a domain name and a public key.
- An identity provider signs OIDC ID tokens with algorithms such as
RS256,ES256orEdDSA. Thousands of services verify them from a published key set, but none of them can mint tokens. - Package managers, container registries and Git commits (signed with SSH or GPG keys) carry signatures.
Public-key crypto rarely encrypts data directly. You exchange a symmetric key with it, and encrypt the data with an AEAD. That combination is called hybrid encryption.
Walking through it
Elliptic curves give the same strength with much smaller keys than RSA:
| Security level | RSA key | ECC key | Example curve |
|---|---|---|---|
| About 128 bits | 3072 bits | 256 bits | P-256, Curve25519 |
| About 192 bits | 7680 bits | 384 bits | P-384 |
An Ed25519 public key is 32 bytes, and its signature is 64 bytes. An RSA-3072 signature is 384 bytes. Use elliptic curves for anything new. Use RSA only where old clients or hardware need it.
Here is a signature round trip:
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.exceptions import InvalidSignature
priv = Ed25519PrivateKey.generate()
pub = priv.public_key()
msg = b'{"order_id": 991, "amount_cents": 4200}'
sig = priv.sign(msg) # 64 bytes
pub.verify(sig, msg) # returns None when valid
try:
pub.verify(sig, msg.replace(b"4200", b"1"))
except InvalidSignature:
print("tampered message rejected")
print(len(sig), "bytes")Output:
tampered message rejected
64 bytesKey exchange and forward secrecy
Diffie–Hellman lets two parties agree on a secret over a public channel. Each side combines its private key with the other's public key and gets the same result. An eavesdropper who sees both public keys can't compute it.
ECDHE runs Diffie–Hellman on elliptic curves with ephemeral keys, generated fresh for each connection. Ephemeral keys give forward secrecy. If a server's long-term key is stolen next year, traffic recorded today still can't be decrypted, because the session keys were thrown away. TLS 1.3 dropped every key exchange that lacks this property. See TLS for how ECDHE and signatures combine in a handshake.
Note
Large quantum computers would break RSA and elliptic-curve crypto. NIST standardized the post-quantum key exchange ML-KEM in 2024, and many TLS stacks now offer hybrid X25519 and ML-KEM key exchange. Keeping TLS libraries current gets you this.
Signing has its own trap. ECDSA needs a fresh random value for every signature. Reuse one, and the private key leaks. That's how the PlayStation 3 code-signing key was extracted in 2010. Ed25519 avoids the problem by deriving its per-signature value deterministically.
Common mistakes
- Choosing RSA by default for a new system. Ed25519, X25519 and P-256 are the usual defaults.
- Assuming an HMAC proves who signed a message.
- Reusing the random value in ECDSA signatures, which leaks the private key.
Check yourself
- Why does a TLS certificate use a signature rather than an HMAC? Answer: anyone must verify it, but only the authority should be able to create it. An HMAC verifier could forge it.
- Why does ECDHE with ephemeral keys protect recorded traffic if the server's key leaks later? Answer: the per-session keys were discarded, so the stolen long-term key can't decrypt old sessions.
Key takeaways
- A signature is made only with the private key and verified with the public key. That's what makes it non-repudiable.
- Default to elliptic curves. Ed25519 and X25519 are small and fast.
- Ephemeral key exchange, ECDHE, gives forward secrecy.
- Use a signature when others must verify but not sign.
Further learning
Next, see password hashing, the one place where you must not use a fast hash.
Related
Cryptography for engineers
The few building blocks you need for everyday security decisions, which question each one answers, and where teams go wrong. Start here for the map.
TLS and certificates
How HTTPS keeps data private and proves the server's identity, how the TLS 1.3 handshake works, and why expired certificates keep causing outages. Start here for TLS.
HMAC and webhook signing
A keyed hash proves a message came from a key holder and was not changed. It protects webhooks, signed URLs and TOTP codes.