Skip to content

SSL/TLS: Securing Data in Transit with Certificates and Encryption

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about SSL/TLS: Securing Data in Transit with Certificates and Encryption. We cover key concepts, practical examples, and best practices to help you master this topic.

SSL/TLS encrypts data transmitted between clients and servers, preventing eavesdropping, tampering, and man-in-the-middle attacks. For backend services, TLS is essential not just for external APIs but also for internal service-to-service communication.

flowchart TB
    Client[Client] -->|TCP Handshake| Server
    Server -->|Certificate with Public Key| Client
    Client -->|Verify Certificate| CA[Certificate Authority]
    CA -->|Trusted| Client
    Client -->|Generate Pre-Master Secret| Server
    Server -->|Decrypt with Private Key| Session[Session Keys Established]
    Session -->|Encrypted Data Exchange| Client
    Session -->|Encrypted Data Exchange| Server
    
    subgraph TLS 1.3
        Fast[1-RTT Handshake]
        FS[Forward Secrecy]
        AEAD[AEAD Ciphers Only]
    end

What You'll Learn

  • TLS handshake and certificate chain validation
  • Configuring TLS 1.3 with strong cipher suites
  • Mutual TLS (mTLS) for service-to-service authentication
  • Certificate management with Let's Encrypt

Why It Matters

Without TLS, all data transmitted between client and server is in plaintext and can be intercepted by anyone on the same network. TLS is not optional — it is required for security, privacy, and regulatory compliance.

Real-World Use

A Microservices architecture uses mTLS for all internal communication. Each service has a certificate signed by an internal CA. mTLS ensures that only authenticated services can communicate and all traffic is encrypted. Certificates are rotated automatically every 30 days.

TLS Configuration

Node.js HTTPS Server with TLS 1.3

const https = require('https');
const fs = require('fs');

const options = {
  key: fs.readFileSync('/etc/ssl/private/key.pem'),
  cert: fs.readFileSync('/etc/ssl/certs/cert.pem'),
  ca: fs.readFileSync('/etc/ssl/certs/ca.pem'),
  minVersion: 'TLSv1.3',
  ciphers: [
    'TLS_AES_256_GCM_SHA384',
    'TLS_AES_128_GCM_SHA256',
    'TLS_CHACHA20_POLY1305_SHA256'
  ].join(':'),
  honorCipherOrder: true,
  rejectUnauthorized: true,
  requestCert: false
};

const server = https.createServer(options, app);
server.listen(443, () => {
  console.log('HTTPS server with TLS 1.3 listening on port 443');
});

Expected output:

Server only accepts TLS 1.3 connections. Only AEAD cipher suites are supported. SSLv3, TLS 1.0, 1.1, 1.2 are rejected.

Mutual TLS (mTLS) for Service-to-Service

const https = require('https');
const fs = require('fs');

// Server requires client certificate
const serverOptions = {
  key: fs.readFileSync('/etc/ssl/private/service-a-key.pem'),
  cert: fs.readFileSync('/etc/ssl/certs/service-a-cert.pem'),
  ca: fs.readFileSync('/etc/ssl/certs/internal-ca.pem'),
  minVersion: 'TLSv1.3',
  requestCert: true,
  rejectUnauthorized: true
};

// Client presents certificate
const clientOptions = {
  key: fs.readFileSync('/etc/ssl/private/service-b-key.pem'),
  cert: fs.readFileSync('/etc/ssl/certs/service-b-cert.pem'),
  ca: fs.readFileSync('/etc/ssl/certs/internal-ca.pem'),
  minVersion: 'TLSv1.3'
};

// Server validates client certificate
app.use((req, res, next) => {
  const cert = req.socket.getPeerCertificate();
  if (!cert.subject || !cert.subject.CN) {
    return res.status(401).json({ error: 'Client certificate required' });
  }
  req.clientService = cert.subject.CN;
  next();
});

Expected output:

Service B's HTTPS request to Service A includes B's client certificate. Service A validates the certificate against the internal CA. Only services with valid certificates can communicate.

Automatic Certificate Renewal with Let's Encrypt

const acme = require('acme-client');
const fs = require('fs/promises');

async function obtainCertificate(domains) {
  const client = new acme.Client({
    directoryUrl: acme.directory.letsencrypt.production,
    accountKey: await fs.readFile('./account-key.pem', 'utf8')
  });

  const [cert, key] = await client.auto({
    domains,
    csr: new acme.csr.Csr({
      commonName: domains[0],
      altNames: domains
    }),
    challengePriority: ['http-01'],
    challengeCreate: async (authz, challenge, keyAuthorization) => {
      // Store challenge for HTTP-01 validation
      await fs.writeFile(`/var/www/.well-known/acme-challenge/${challenge.token}`, keyAuthorization);
    },
    challengeRemove: async (authz, challenge, keyAuthorization) => {
      await fs.unlink(`/var/www/.well-known/acme-challenge/${challenge.token}`);
    }
  });

  await fs.writeFile('/etc/ssl/certs/fullchain.pem', cert.toString());
  await fs.writeFile('/etc/ssl/private/key.pem', key.toString());
  console.log('Certificate obtained and saved');
}

Expected output:

Let's Encrypt validates domain ownership via HTTP-01 challenge. Certificate and key are saved to files. Certificate auto-renews every 60 days.

Common Mistakes

  • Using weak cipher suites or outdated TLS versions (TLS 1.0, 1.1, SSLv3).
  • Not enabling HSTS, allowing SSL stripping attacks on first visit.
  • Ignoring certificate expiration — expired certificates cause connection failures for all users.
  • Using self-signed certificates in production without proper CA trust chain.
  • Not implementing certificate pinning or CA verification for internal mTLS.

Practice Questions

  1. What is the difference between TLS 1.2 and TLS 1.3?
  2. How does mutual TLS (mTLS) authenticate services?
  3. Why is HSTS important for TLS security?
  4. What is certificate transparency?
  5. How does Let's Encrypt automate certificate renewal?

Challenge

Set up a Node.js HTTPS server with TLS 1.3 only. Configure mTLS for a second service that acts as a client. Use Let's Encrypt staging environment to obtain certificates. Test with curl and openssl s_client. Verify that non-mTLS requests are rejected.

FAQ

What is the difference between SSL and TLS?

SSL (Secure Sockets Layer) is the deprecated predecessor. TLS (Transport Layer Security) is the modern protocol. Use TLS 1.3; disable SSL and TLS 1.0/1.1.

What is a cipher suite?

A cipher suite is a combination of key exchange, authentication, encryption, and MAC algorithms. Example: TLS_AES_256_GCM_SHA384. TLS 1.3 simplified cipher suites to AEAD only.

What is forward secrecy?

Forward secrecy ensures that if a server's private key is compromised, past sessions cannot be decrypted. TLS 1.3 mandates forward secrecy; TLS 1.2 made it optional.

What is HSTS?

HTTP Strict Transport Security (HSTS) tells browsers to always connect via HTTPS. It prevents SSL stripping attacks and is set via the Strict-Transport-Security header.

Should I use mTLS for internal services?

Yes. mTLS provides strong authentication and encryption for service-to-service communication. It eliminates the need for API keys or JWTs for internal service auth.

Mini Project

Create a two-service architecture with mTLS. Service A (Express, port 3001) requires client certificates. Service B (Express, port 3002) calls Service A with its certificate. Generate certificates with a custom CA. Configure TLS 1.3 only. Write a test that verifies mTLS authentication.

What's Next

Continue to Security Logging to learn about security event logging and monitoring.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro