Glossary

ECDSA

ECDSA stands for Elliptic Curve Digital Signature Algorithm, a signature scheme that turns a message hash and a private key into two integers, r and s. It is the elliptic curve version of DSA, specified in FIPS 186-5, and it is widely used in TLS certificates, JWT ES256 tokens and SSH. Every signature also needs a fresh secret number called k.

How it works

The signer hashes the message, for example with SHA-256, and picks a random k between 1 and n-1, where n is the order of the curve group. It computes the point k times G and takes the x coordinate modulo n as r. It then calculates s from k, the hash, r and the private key. The signature is the pair (r, s).

The verifier uses the public key, r, s and the hash to rebuild a point and checks that its x coordinate matches r. Each of r and s has the size of the curve order, so a P-256 signature holds 2 times 32 = 64 bytes of data.

The randomness of k is the sensitive part. RFC 6979 describes a deterministic variant that derives k from the private key and the message, and FIPS 186-5 approves it as deterministic ECDSA. The verifier cannot tell the difference. The script below signs the message "sample" with the RFC 6979 P-256 test key and compares with that RFC's published r and s.

from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
x=0xC9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721
k=ec.derive_private_key(x, ec.SECP256R1())
alg=ec.ECDSA(hashes.SHA256(), deterministic_signing=True)
sig=k.sign(b"sample", alg)
r,s=decode_dss_signature(sig)
print("r =", format(r,'064X'))
print("s =", format(s,'064X'))
print("same twice:", k.sign(b"sample", alg)==sig)
rnd=ec.ECDSA(hashes.SHA256())
a,b=k.sign(b"sample", rnd),k.sign(b"sample", rnd)
print("random k, same twice:", a==b)
r = EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716
s = F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8
same twice: True
random k, same twice: False

How long is an ECDSA signature?

A P-256 ECDSA signature is 64 bytes as a raw r and s pair, and 70 to 72 bytes in the DER encoding that OpenSSL and X.509 use. DER wraps r and s as two ASN.1 integers, and each gets a leading zero byte when its top bit is set. That makes 72 bytes the maximum for P-256, and the two random-k signatures in the run above came out at 71 and 72 bytes. JWT ES256 uses the raw 64-byte form, so DER output must be converted.

Common pitfalls

  • Reusing or leaking k: two signatures made with the same k reveal the private key, and RFC 6979 warns that even slight bias in k can be turned into an attack. Use a vetted library, or the deterministic variant.
  • Mixing DER and raw signatures: a JWT library expecting 64 bytes rejects a 71-byte DER blob, and an X.509 verifier rejects raw bytes. Convert explicitly.
  • Expecting the same signature twice: with random k, every signature differs, as the last line above shows. Compare by verifying, never by string equality.
  • Hashing with a weaker hash than the curve: FIPS 186-5 recommends matching hash and curve strength, such as SHA-256 with P-256 and SHA-384 with P-384.
  • Using the key for other purposes: FIPS 186-5 says ECDSA keys must not be used for key establishment. Keep signing keys separate.

Related terms

  • ECC — the curve mathematics ECDSA is built on.
  • RSA — the older signature option with larger keys and signatures.
  • Ed25519 — a deterministic-by-design signature that avoids per-signature randomness.
  • SHA-256 — the hash commonly paired with the P-256 curve.
  • TLS — carries ECDSA signatures such as ecdsa_secp256r1_sha256 in handshakes.
  • JWT — the ES256, ES384 and ES512 algorithms are ECDSA.

See also