Sign data and verify signatures

A digital signature proves that a message was created by the owner of a private key, and that it was not modified afterwards. Anybody with the matching public key can verify the signature.

Signatures do not hide the message: if it must also be confidential, encrypt it too (see Encrypt data for a recipient’s public key).

If the two parties share a secret key, a MAC is simpler and faster.

Alternatives

Choose a different algorithm if a protocol or a standard requires it:

  • ECDSA with the NIST P-256 curve, often required by standards (like FIPS), and used in JWT (ES256) and in TLS certificates. You sign a hash of the message, created with SHA-256:

    from Crypto.Hash import SHA256
    from Crypto.Signature import DSS
    
    ecdsa_key = ECC.generate(curve="p256")
    signature = DSS.new(ecdsa_key, "fips-186-3").sign(SHA256.new(message))
    
    verifier = DSS.new(ecdsa_key.public_key(), "fips-186-3")
    verifier.verify(SHA256.new(message), signature)       # ValueError if invalid
    

    See Digital Signature Algorithm (DSA and ECDSA).

  • RSA keys, when the existing infrastructure uses them. Prefer the PSS scheme; use PKCS#1 v1.5 only for compatibility:

    from Crypto.PublicKey import RSA
    from Crypto.Signature import pss
    
    rsa_key = RSA.generate(3072)
    signature = pss.new(rsa_key).sign(SHA256.new(message))
    
    verifier = pss.new(rsa_key.public_key())
    verifier.verify(SHA256.new(message), signature)       # ValueError if invalid
    
  • ML-DSA, a post-quantum signature scheme standardized in FIPS 204, if signatures must remain secure against future quantum computers. Keys and signatures are much larger (several kilobytes):

    from Crypto.PublicKey import MLDSA
    from Crypto.Signature import mldsa
    
    mldsa_key = MLDSA.generate(param_set=MLDSA.ML_DSA_65)
    signature = mldsa.new(mldsa_key).sign(message)
    
    mldsa.new(mldsa_key.public_key()).verify(message, signature)  # ValueError if invalid
    

    See Module-Lattice-Based Digital Signature Algorithm (ML-DSA).

Sign a large file

ECDSA and RSA sign a hash object, so you can hash a large file in chunks first:

with open("large_file.bin", "rb") as f:
    h = SHA256.new()
    while chunk := f.read(1024 * 1024):
        h.update(chunk)

signature = pss.new(rsa_key).sign(h)

With Ed25519, pass a SHA-512 object instead of the message (this variant is called HashEdDSA, and it produces a different signature, so the verifier must do the same).