Elliptic Curve Cryptography
Elliptic curve cryptography reaches the same security as RSA or Diffie-Hellman with far smaller keys: an ECDSA-P256 signature matches a 3072-bit RSA signature, and an X25519 key exchange matches 3072-bit DH, at a fraction of the size and compute cost. That is why curves are the default in TLS 1.3, SSH, and mobile protocols, and why the cryptography library treats them as first-class citizens.
Standard Curves
| Curve | Type | Security | Typical use |
|---|---|---|---|
| SECP256R1 (P-256) | NIST, Weierstrass | ~128 bits | certificates, ECDSA, TLS |
| SECP384R1 (P-384) | NIST | ~192 bits | long-lived high-value keys |
| X25519 | Montgomery | ~128 bits | key exchange (ECDHE) |
| Ed25519 | twisted Edwards | ~128 bits | signatures (SSH, TLS 1.3) |
P-256 is the universal interoperability choice; Ed25519/X25519 are the speed and safety favorites for new systems (deterministic signatures, no padding pitfalls).
Generating EC Keys
from cryptography.hazmat.primitives.asymmetric import ec
# key exchange
x25519 = ec.generate_private_key(ec.X25519())
# signing / certificates
p256 = ec.generate_private_key(ec.SECP256R1())
ed = ec.generate_private_key(ec.Ed25519())
print(p256.curve.name) # 'secp256r1'
print(ed.public_key()) # Ed25519PublicKey
ECDSA Signatures
from cryptography.hazmat.primitives.asymmetric import ec, utils
from cryptography.hazmat.primitives import hashes
signature = p256.sign(
b"release-2.4.1 manifest",
ec.ECDSA(hashes.SHA256()),
)
p256.public_key().verify(signature, b"release-2.4.1 manifest",
ec.ECDSA(hashes.SHA256()))
ECDSA signatures are variable-length DER blobs by default; the utils.Prehashed and SIG_ENCODE helpers produce the fixed 64-byte form (r || s) used by many protocols.
Ed25519 Signatures
signature = ed.sign(b"payload")
ed.public_key().verify(signature, b"payload") # no hash argument - it hashes internally
Ed25519 is simpler to use correctly: no padding parameter, deterministic signatures, and a fixed 64-byte output. SSH keys, TLS 1.3, and Git's signed commits increasingly use it.
Serializing Curve Keys
from cryptography.hazmat.primitives import serialization
pem = x25519.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.PKCS8,
serialization.NoEncryption(),
)
loaded = serialization.load_pem_private_key(pem, password=None)
The same PKCS#8/SPKI encodings as RSA - the curve is identified inside the key data, so tools that read "a private key" work with any of them.
Summary
Default to X25519 for key exchange, Ed25519 for new signatures, and P-256 where interoperability demands NIST curves. Small keys, fast operations, fewer padding pitfalls - curves are the right first choice in anything you design today.