Digital signatures and certificates

Why signing means hashing first, how RSA and ECDSA signatures differ, what a certificate chain proves, and how code signing and reproducible builds use hashes together.

You sign a digest, not a document

Public-key operations are slow and only work on small inputs, so a signature covers a hash of the message. That is why a broken hash breaks the signature even when the signature algorithm is sound.

sign:    message -> hash -> sign(hash, private key) -> signature
verify:  message -> hash -+
                            +-> verify(signature, public key) -> valid?
         signature ---------+
# generate a key pair and sign a digest
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out key.pem
openssl pkey -in key.pem -pubout -out pub.pem

openssl dgst -sha256 -sign key.pem -out file.sig file.bin
openssl dgst -sha256 -verify pub.pem -signature file.sig file.bin
# Verified OK

RSA and ECDSA compared

AspectRSA (PKCS#1 v1.5 or PSS)ECDSA (P-256)Ed25519
Key size for 128-bit security3072 bit256 bit256 bit
Signature size384 bytes at 3072About 72 bytes (DER)64 bytes
SpeedSlow signing, fast verifyFast both waysVery fast both ways
Deterministic signatureYes with PKCS#1 v1.5No, unless RFC 6979 is usedYes by design
Nonce riskNoneA repeated nonce leaks the private keyNone
Best forLegacy interoperabilityExisting web PKINew protocols and tokens
from cryptography.hazmat.primitives.asymmetric import ed25519

private = ed25519.Ed25519PrivateKey.generate()
public = private.public_key()

msg = b"release 1.2.3"
sig = private.sign(msg)
public.verify(sig, msg)          # raises InvalidSignature if it does not match

# a signature over a digest is equally valid
import hashlib
digest = hashlib.sha256(msg).digest()
sig2 = private.sign(digest)
public.verify(sig2, digest)
  • Ed25519 is deterministic: no per-signature randomness, so no nonce-reuse catastrophe.
  • ECDSA requires a unique nonce per signature; reuse has leaked private keys in real products.
  • RSA-PSS is the modern RSA padding; the older PKCS#1 v1.5 remains in use for compatibility.
  • Post-quantum signature schemes are being standardised now; plan migration for anything with a long trust lifetime.

Chains, code signing and reproducible builds

  • A certificate binds a public key to a name, and is itself signed by a certificate authority whose key is in a trust store.
  • Verification walks the chain to a trusted root, checks validity dates, and confirms the name matches.
  • A signature over a software release ties the artifact to a publisher identity, not just to integrity.
  • Reproducible builds let anyone recompute the artifact hash from source and compare it with the published one.
  • Sign the hash of the release manifest, not only each file, so the set of artifacts is covered too.
# verify a release: signature, then digest, then the artifact
gpg --verify release.tar.gz.sig release.tar.gz
sha256sum -c SHA256SUMS

# inspect a certificate chain
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates
⚠️
A signature is only as trustworthy as the key behind it. Publishing the public key next to the signature on the same server proves nothing about authorship — an attacker who can replace the file can replace both. Distribute keys through a separate channel.

FAQ

Why does my signature break when I re-encode the file?
Any change to the bytes changes the digest. Text editors, line-ending conversion or a re-zip step will invalidate it. Sign the exact artifact you distribute, and never edit it afterwards.
Is a hash alone enough to trust a download?
No. A hash detects accidental corruption, but an attacker who controls the download can also control the published hash. A signature from a key you obtained independently is the missing piece.

HMAC and keyed hashing SHA-3, BLAKE2 and BLAKE3

Last refreshed 2026-09-18.