bigonotes
System DesignSecurity

Encryption, Hashing and Digital Signatures

Symmetric vs asymmetric encryption, how TLS combines them, salted password hashing, and how digital signatures prove who sent a message.

7 min readLast updated

Three tools from cryptography do most of the work in everyday security. Encryption keeps data secret. Hashing detects changes and lets you store passwords safely. Digital signatures prove who created a message. This note explains what each one guarantees, how HTTPS combines them, and the mistakes that break them. It uses the terms from security fundamentals.

Encryption#

Readable data is called plaintext. Encryption turns it into ciphertext, which is unreadable without the right key. Encryption protects confidentiality. With modern modes, it also detects tampering. There are two families.

Symmetric encryption#

Symmetric encryption uses one shared key to encrypt and to decrypt. AES is the standard example.

  • Fast. It suits large amounts of data, such as disk volumes or a stream of web traffic.
  • Hard to share. Both sides need the same secret key, and you need a safe way to give it to them.
  • No proof of who wrote it. If several parties hold the key, any of them could have made the message. This means symmetric encryption gives no non-repudiation: a sender can deny sending a message.

Asymmetric encryption#

Asymmetric (public key) encryption solves the key-sharing problem by using a key pair. The private key stays with its owner. The public key can go to anyone. RSA and elliptic-curve cryptography (ECC) are the common families.

  • Data encrypted with the public key can only be decrypted with the matching private key. Anyone can send the owner a secret.
  • The private key can also sign data, and anyone with the public key can check the signature. This is the basis of digital signatures, covered below.
  • Slow. The math behind it costs far more than symmetric encryption, so you don't use it for bulk data.

Each family is strong where the other is weak, as the table shows.

Symmetric Asymmetric
Keys One shared secret Public and private pair
Speed Fast Much slower
Key sharing Hard: needs a secure channel Easy: publish the public key
Typical use Bulk data, storage, sessions Key exchange, signatures, certificates
Examples AES, ChaCha20 RSA, ECDSA, Ed25519

How TLS uses both#

HTTPS is HTTP over TLS. TLS replaced SSL, and every SSL version is now deprecated, though people still say "SSL". TLS gets the best of both families: asymmetric crypto to agree on keys and prove identity, then symmetric crypto for the data. Here's how the handshake goes:

  1. The client says hello and lists the cipher suites it supports.
  2. The server sends its certificate. The certificate holds the server's public key and is signed by a certificate authority (CA) the client trusts.
  3. The client checks the CA's signature and that the certificate matches the hostname. This proves the server is who it claims to be.
  4. Both sides run a key exchange. In TLS 1.3 this is always ephemeral Diffie-Hellman (often the elliptic-curve form, ECDHE). The server signs its part with its private key.
  5. Each side derives the same session key from the exchange. The key never crosses the network.
  6. All further traffic is encrypted with a fast symmetric cipher, such as AES-GCM or ChaCha20-Poly1305, under the session key.

Because the session keys are ephemeral, stealing the server's private key later does not decrypt old recorded traffic. This property is called forward secrecy.

Older TLS versions let the client encrypt the session secret with the server's RSA public key. TLS 1.3 removed that option because it has no forward secrecy. Old ciphers such as Triple-DES are also deprecated.

Where you decrypt TLS in your own system (at the load balancer or at each server) is a design choice. See SSL/TLS termination, passthrough and bridging.

Hashing#

A hash function turns any input into a short, fixed-length value called a digest. SHA-256 is the common example. Hashing is one-way: you cannot get the input back from the digest.

A good cryptographic hash has three properties.

  • Deterministic: the same input always gives the same digest.
  • Avalanche effect: a one-bit change in the input gives a completely different digest.
  • Collision resistant: it is not practical to find two inputs with the same digest.

Integrity checks#

Determinism is what makes integrity checks work. A sender can publish a digest next to a file or message. The receiver hashes what it received and compares the two values. A mismatch shows that the data changed.

A plain hash only catches accidents. An attacker who can change the message can also change the digest next to it. To stop that, you need a secret: an HMAC (a hash keyed with a shared secret) or a digital signature.

Storing passwords#

Hashing is also how you store passwords. Never store them in plain text, and never encrypt them, because a stolen key reveals them all. Store a salted, slow hash instead.

  • A salt is a random value, unique to each user, that you mix into the hash. Two users with the same password then get different hashes. Salts also defeat rainbow tables (precomputed lists of hashes for common passwords).
  • A slow hash makes each guess expensive. General hashes like SHA-256 are built to be fast, which helps an attacker try billions of guesses. Use a password hashing function such as Argon2id, scrypt or bcrypt.
  • The salt is not secret. Store it next to the hash.

This example uses hashlib.scrypt from the Python standard library.

import hashlib
import hmac
import os

# scrypt cost settings: higher n means slower hashing per guess.
N, R, P = 2**14, 8, 1


def hash_password(password: str) -> tuple[bytes, bytes]:
    salt = os.urandom(16)  # new random salt for every user
    digest = hashlib.scrypt(
        password.encode(), salt=salt, n=N, r=R, p=P
    )
    return salt, digest


def verify_password(password: str, salt: bytes, digest: bytes) -> bool:
    candidate = hashlib.scrypt(
        password.encode(), salt=salt, n=N, r=R, p=P
    )
    # compare_digest takes the same time whether or not bytes match,
    # so timing does not leak how much of the hash was correct.
    return hmac.compare_digest(candidate, digest)


salt, digest = hash_password("correct horse battery staple")
print(verify_password("correct horse battery staple", salt, digest))  # True
print(verify_password("guess", salt, digest))  # False

Digital signatures#

A digital signature proves two things about a message: it has not changed (integrity), and it came from the holder of a specific private key (authenticity). Because only one party holds that key, the sender cannot later deny sending it. This is non-repudiation, which symmetric keys cannot give.

It does this by combining the two tools above: hashing and asymmetric keys.

  1. The sender hashes the message.
  2. The sender signs the digest with its private key. The result is the signature.
  3. The sender sends the message and the signature together.
  4. The receiver hashes the message it received.
  5. The receiver uses the sender's public key to check the signature against that digest.
  6. If the check passes, the message is unchanged and came from the key's owner. Any change to the message makes the check fail.

Textbooks often describe step 2 as "encrypting the hash with the private key". That picture fits classic RSA, but modern schemes such as ECDSA and Ed25519 are not encryption. Think of signing and verifying as their own operations.

You meet signatures every day: TLS certificates, signed software updates, Git commit signing, and JWTs signed with RS256. The API security note covers JWTs.

Which tool for which job#

Each tool gives a different guarantee, so pick by the property you need.

You need Use
Keep data secret Encryption (symmetric for bulk data)
Detect accidental corruption A hash such as SHA-256
Detect tampering between two parties who share a secret HMAC
Prove who sent it, to anyone Digital signature
Store passwords Salted slow hash: Argon2id, scrypt or bcrypt

Key takeaways#

  • Symmetric encryption uses one shared key and is fast. Asymmetric encryption uses a key pair and is slow.
  • TLS uses asymmetric crypto to authenticate the server and agree on a session key, then symmetric crypto for the data.
  • A hash is one-way and fixed-length. Any change to the input changes the digest completely.
  • Store passwords as salted, slow hashes (Argon2id, scrypt, bcrypt), never as plain text or encrypted values.
  • A digital signature is a digest signed with a private key. It gives integrity, authenticity and non-repudiation.
Revision notes · 2 min

Core idea#

Encryption keeps data secret. Hashing detects changes and stores passwords safely. Digital signatures prove who created a message.

Symmetric vs asymmetric#

Symmetric Asymmetric
Keys One shared secret Public and private pair
Speed Fast Much slower
Key sharing Hard Easy: publish the public key
Use Bulk data, sessions Key exchange, signatures, certificates
Examples AES, ChaCha20 RSA, ECDSA, Ed25519
  • Symmetric encryption gives no non-repudiation. Anyone with the key could have made the message.

How TLS uses both#

  • The server sends a certificate signed by a trusted CA. The client checks it and the hostname.
  • TLS 1.3 uses ephemeral Diffie-Hellman (often ECDHE) to agree on a session key. The key never crosses the network.
  • Bulk traffic then uses a symmetric cipher such as AES-GCM.
  • Ephemeral keys give forward secrecy: a stolen private key does not decrypt old traffic.
  • TLS 1.3 removed RSA key exchange. SSL and Triple-DES are deprecated.

Hashing#

  • One-way, fixed-length digest. SHA-256 is common.
  • Properties: deterministic, avalanche effect, collision resistant.
  • A plain hash catches accidents only. Use an HMAC or a signature against attackers.

Passwords#

  • Store a salted, slow hash: Argon2id, scrypt or bcrypt.
  • A unique random salt per user defeats rainbow tables. The salt is not secret.
  • Compare hashes with hmac.compare_digest().

Digital signatures#

  • The sender hashes the message and signs the digest with its private key.
  • The receiver hashes the message and checks the signature with the public key.
  • Gives integrity, authenticity and non-repudiation.

Pitfalls#

  • Fast hashes such as SHA-256 for passwords let attackers try billions of guesses.
  • Encrypting passwords: one stolen key reveals them all.
  • Thinking a plain hash next to a message stops tampering.

Say it in an interview#

  • "TLS uses asymmetric crypto to authenticate and agree on a key, then symmetric crypto for the data."
  • "Passwords get a salted, slow hash such as Argon2id, never encryption."

Found a mistake? Email ask@bigonotes.com

Further reading#