Skip to content
← Networking · intermediate · 15 min · 05 / 08

TLS & HTTPS

Certificates, handshakes, and encryption — how TLS keeps your data private and verifies you're talking to the right server.

TLSHTTPScertificatesencryptionhandshake

What TLS Does

TLS (Transport Layer Security) provides three guarantees:

  1. Confidentiality — data is encrypted; eavesdroppers see gibberish
  2. Integrity — data can’t be modified in transit without detection
  3. Authentication — you’re talking to the real server, not an impersonator

HTTPS is just HTTP over TLS. The S means your browser established a TLS connection before sending any HTTP data.

Real-World Analogy

Like the sealed envelope system at a bank — before sending sensitive documents, both parties verify identity (certificates), agree on a secret code (key exchange), and then all messages travel in sealed envelopes (encryption).

The TLS 1.3 Handshake

TLS 1.3 reduced the handshake from 2 round-trips (TLS 1.2) to just 1:

// TLS 1.3 Handshake (simplified)

// 1. Client Hello
//    Client → Server:
//    - Supported cipher suites
//    - Client's key share (Diffie-Hellman public key)
//    - SNI (Server Name Indication — which domain)

// 2. Server Hello + Encrypted Extensions
//    Server → Client:
//    - Chosen cipher suite
//    - Server's key share
//    - Certificate + signature
//    - Finished (verification)

// 3. Client Finished
//    Client → Server:
//    - Finished (verification)
//    - Application data can now flow!

interface TLSClientHello {
  supportedVersions: ["TLS 1.3"];
  cipherSuites: string[];
  keyShare: {
    group: "x25519" | "secp256r1";
    publicKey: Uint8Array;
  };
  sni: string; // "api.example.com"
}

Certificates and Trust

How does your browser know it’s really talking to google.com?

Certificate chain:

  1. Google’s server presents a certificate signed by a Certificate Authority (CA)
  2. The CA’s certificate is signed by a root CA
  3. Root CA certificates are pre-installed in your browser/OS (the trust store)
interface X509Certificate {
  subject: string;          // "*.google.com"
  issuer: string;           // "Google Trust Services"
  validFrom: Date;
  validTo: Date;
  publicKey: Uint8Array;
  signature: Uint8Array;    // signed by issuer's private key
  extensions: {
    subjectAltNames: string[]; // domains this cert covers
    keyUsage: string[];
  };
}

function verifyCertChain(certs: X509Certificate[]): boolean {
  for (let i = 0; i < certs.length - 1; i++) {
    const cert = certs[i];
    const issuer = certs[i + 1];

    // Verify signature
    if (!verifySignature(cert, issuer.publicKey)) return false;

    // Check expiration
    if (cert.validTo < new Date()) return false;

    // Check issuer matches
    if (cert.issuer !== issuer.subject) return false;
  }

  // Last cert must be in trust store
  return trustStore.has(certs[certs.length - 1]);
}

Key Exchange: Diffie-Hellman

TLS uses Diffie-Hellman to create a shared secret without ever sending it over the wire. Even if someone records all the traffic, they can’t derive the key.

// Simplified Diffie-Hellman concept
// Both sides generate a key pair, exchange public keys,
// and independently derive the same shared secret

// Client: privateA, publicA = generate()
// Server: privateB, publicB = generate()

// Client sends publicA → Server
// Server sends publicB → Client

// Client computes: sharedSecret = combine(privateA, publicB)
// Server computes: sharedSecret = combine(privateB, publicA)
// Both get the same sharedSecret!

// An eavesdropper sees publicA and publicB
// but can't derive sharedSecret without a private key

Perfect Forward Secrecy (PFS): TLS 1.3 generates new key pairs for every connection. Even if someone steals the server’s long-term private key, they can’t decrypt past conversations — because each session used a unique ephemeral key.

Common TLS Issues

// Certificate pinning — extra security for mobile apps
const PINNED_HASHES = [
  "sha256/YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=",
];

function verifyPin(cert: X509Certificate): boolean {
  const hash = sha256(cert.publicKey);
  return PINNED_HASHES.includes(`sha256/${base64(hash)}`);
}

// Mixed content — loading HTTP resources on HTTPS page
// Browser blocks this to prevent downgrade attacks
// Always use HTTPS for everything

// HSTS — tell browsers to always use HTTPS
// Strict-Transport-Security: max-age=31536000; includeSubDomains

Never disable certificate verification in production code (rejectUnauthorized: false in Node.js). It defeats the entire purpose of TLS — an attacker can present any certificate and you’ll accept it.

Key Takeaways

  1. TLS provides encryption, integrity, and authentication — all three matter
  2. TLS 1.3 is 1 round-trip — significantly faster than TLS 1.2
  3. Certificates form a chain of trust back to pre-installed root CAs
  4. Diffie-Hellman key exchange creates shared secrets without sending them over the wire
  5. Perfect Forward Secrecy means compromising today’s key doesn’t reveal yesterday’s traffic