Menu
×
   ❮     
HTML CSS JAVASCRIPT SQL PYTHON JAVA PHP HOW TO W3.CSS C C++ C# BOOTSTRAP REACT MYSQL JQUERY EXCEL XML DJANGO NUMPY PANDAS NODEJS DSA TYPESCRIPT ANGULAR GIT POSTGRESQL MONGODB ASP AI R GO KOTLIN SASS VUE GEN AI SCIPY CYBERSECURITY DATA SCIENCE INTRO TO PROGRAMMING BASH RUST

Node.js Tutorial

Node HOME Node Intro Node Get Started Node JS Requirements Node.js vs Browser Node Cmd Line Node V8 Engine Node Architecture Node Event Loop

Asynchronous

Node Async Node Promises Node Async/Await Node Errors Handling

Module Basics

Node Modules Node ES Modules Node NPM Node package.json Node NPM Scripts Node Manage Dep Node Publish Packages

Core Modules

HTTP Module HTTPS Module File System (fs) Path Module OS Module URL Module Events Module Stream Module Buffer Module Crypto Module Timers Module DNS Module Assert Module Util Module Readline Module

JS & TS Features

Node ES6+ Node Process Node TypeScript Node Adv. TypeScript Node Lint & Formatting

Building Applications

Node Frameworks Express.js Middleware Concept REST API Design API Authentication Node.js with Frontend

Database Integration

MySQL Get Started MySQL Create Database MySQL Create Table MySQL Insert Into MySQL Select From MySQL Where MySQL Order By MySQL Delete MySQL Drop Table MySQL Update MySQL Limit MySQL Join
MongoDB Get Started MongoDB Create DB MongoDB Collection MongoDB Insert MongoDB Find MongoDB Query MongoDB Sort MongoDB Delete MongoDB Drop Collection MongoDB Update MongoDB Limit MongoDB Join

Advanced Communication

GraphQL Socket.IO WebSockets

Testing & Debugging

Node Adv. Debugging Node Testing Apps Node Test Frameworks Node Test Runner

Node.js Deployment

Node Env Variables Node Dev vs Prod Node CI/CD Node Security Node Deployment

Perfomance & Scaling

Node Logging Node Monitoring Node Performance Child Process Module Cluster Module Worker Threads

Node.js Advanced

Microservices Node WebAssembly HTTP2 Module Perf_hooks Module VM Module TLS/SSL Module Net Module Zlib Module Real-World Examples

Hardware & IoT

RasPi Get Started RasPi GPIO Introduction RasPi Blinking LED RasPi LED & Pushbutton RasPi Flowing LEDs RasPi WebSocket RasPi RGB LED WebSocket RasPi Components

Node.js Reference

Built-in Modules EventEmitter (events) Worker (cluster) Cipher (crypto) Decipher (crypto) DiffieHellman (crypto) ECDH (crypto) Hash (crypto) Hmac (crypto) Sign (crypto) Verify (crypto) Socket (dgram, net, tls) ReadStream (fs, stream) WriteStream (fs, stream) Server (http, https, net, tls) Agent (http, https) Request (http) Response (http) Message (http) Interface (readline)

Resources & Tools

Node.js Compiler Node.js Server Node.js Quiz Node.js Exercises Node.js Syllabus Node.js Study Plan Node.js Certificate

Node.js TLS/SSL Module


What is TLS/SSL?

Transport Layer Security (TLS) and its predecessor, Secure Socket Layer (SSL), are protocols that provide secure communication over a computer network. They ensure:

  • Privacy: Communications are encrypted to prevent eavesdropping
  • Data integrity: Message contents cannot be modified without detection
  • Authentication: The identities of the communicating parties can be verified

TLS/SSL is commonly used for securing:

  • Web browsing (HTTPS)
  • Email transmissions (SMTP, IMAP, POP3)
  • Instant messaging
  • Voice over IP (VoIP)
  • API communication

Using the TLS Module

To use the TLS module in Node.js, you need to require it:

const tls = require('tls');

TLS Server

Here's how to create a basic TLS server:

const tls = require('tls');
const fs = require('fs');
const path = require('path');

// Server options with TLS certificates
const options = {
  key: fs.readFileSync(path.join(__dirname, 'server-key.pem')),
  cert: fs.readFileSync(path.join(__dirname, 'server-cert.pem')),
  // Request client certificate (optional)
  requestCert: true,
  // Reject connections without authorized certificates (optional)
  rejectUnauthorized: false
};

// Create TLS server
const server = tls.createServer(options, (socket) => {
  console.log('Server connected',
    socket.authorized ? 'authorized' : 'unauthorized');
  
  // Set encoding for data
  socket.setEncoding('utf8');
  
  // Handle incoming data
  socket.on('data', (data) => {
    console.log('Received:', data);
    // Echo back the data
    socket.write(`You said: ${data}`);
  });
  
  // Handle socket closure
  socket.on('end', () => {
    console.log('Socket ended');
  });
  
  // Write welcome message
  socket.write('Welcome to the TLS server!\n');
});

// Start TLS server
const port = 8000;
server.listen(port, () => {
  console.log(`TLS server running on port ${port}`);
});

This example requires certificate files (server-key.pem and server-cert.pem). For development purposes, you can generate self-signed certificates using OpenSSL.

Generating Self-Signed Certificates for Development

You can use OpenSSL to generate self-signed certificates for development and testing:

# Generate CA certificate
openssl genrsa -out ca-key.pem 2048
openssl req -new -x509 -key ca-key.pem -out ca-cert.pem -days 365

# Generate server certificate
openssl genrsa -out server-key.pem 2048
openssl req -new -key server-key.pem -out server-csr.pem
openssl x509 -req -in server-csr.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 365

# Generate client certificate (optional, for mutual authentication)
openssl genrsa -out client-key.pem 2048
openssl req -new -key client-key.pem -out client-csr.pem
openssl x509 -req -in client-csr.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 365

TLS Client

Creating a client that connects to a TLS server:

const tls = require('tls');
const fs = require('fs');
const path = require('path');

// Client options
const options = {
  // For mutual authentication (optional)
  key: fs.readFileSync(path.join(__dirname, 'client-key.pem')),
  cert: fs.readFileSync(path.join(__dirname, 'client-cert.pem')),
  // Server name for Server Name Indication (SNI)
  servername: 'localhost',
  // CA certificate to verify the server (optional)
  ca: fs.readFileSync(path.join(__dirname, 'ca-cert.pem')),
  // Reject unauthorized certificates
  rejectUnauthorized: true
};

// Connect to server
const client = tls.connect(8000, 'localhost', options, () => {
  // Check if authorized
  console.log('Client connected',
    client.authorized ? 'authorized' : 'unauthorized');
    
  if (!client.authorized) {
    console.log('Reason:', client.authorizationError);
  }
  
  // Send data to server
  client.write('Hello from TLS client!');
});

// Set encoding for received data
client.setEncoding('utf8');

// Handle received data
client.on('data', (data) => {
  console.log('Received from server:', data);
  
  // Send another message
  client.write('How are you?');
});

// Handle errors
client.on('error', (error) => {
  console.error('Connection error:', error);
});

// Handle connection end
client.on('end', () => {
  console.log('Server ended connection');
});

// Close connection after 5 seconds
setTimeout(() => {
  console.log('Closing connection');
  client.end();
}, 5000);

Server and Client Options

Both tls.createServer() and tls.connect() accept various options to configure the TLS connection:

Common Options

  • key: Private key in PEM format
  • cert: Certificate in PEM format
  • ca: Trusted CA certificates
  • ciphers: Cipher suite specification string
  • minVersion: Minimum TLS version to allow
  • maxVersion: Maximum TLS version to allow

Server-specific Options

  • requestCert: Whether to request a certificate from clients
  • rejectUnauthorized: Whether to reject clients with invalid certificates
  • SNICallback: Function to handle SNI from the client

Client-specific Options

  • servername: Server name for SNI
  • checkServerIdentity: Function to verify server hostname
  • session: A Buffer instance containing TLS session
const tls = require('tls');
const fs = require('fs');

// Comprehensive server options
const serverOptions = {
  // Key and certificate
  key: fs.readFileSync('server-key.pem'),
  cert: fs.readFileSync('server-cert.pem'),
  
  // Certificate Authority
  ca: [fs.readFileSync('ca-cert.pem')],
  
  // Protocol version control
  minVersion: 'TLSv1.2',
  maxVersion: 'TLSv1.3',
  
  // Cipher control
  ciphers: 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384',
  
  // Client authentication
  requestCert: true,
  rejectUnauthorized: true,
  
  // Server Name Indication handling
  SNICallback: (servername, cb) => {
    // Different certificates for different servernames
    if (servername === 'example.com') {
      cb(null, tls.createSecureContext({
        key: fs.readFileSync('example-key.pem'),
        cert: fs.readFileSync('example-cert.pem')
      }));
    } else {
      // Default certificate
      cb(null, tls.createSecureContext({
        key: fs.readFileSync('default-key.pem'),
        cert: fs.readFileSync('default-cert.pem')
      }));
    }
  }
};

// Example client options
const clientOptions = {
  key: fs.readFileSync('client-key.pem'),
  cert: fs.readFileSync('client-cert.pem'),
  ca: [fs.readFileSync('ca-cert.pem')],
  
  servername: 'example.com',
  minVersion: 'TLSv1.2',
  
  // Custom identity check function
  checkServerIdentity: (hostname, cert) => {
    // Custom validation logic
    if (hostname !== cert.subject.CN) {
      return new Error(`Certificate CN does not match hostname: ${hostname}`);
    }
    return undefined; // No error
  },
  
  // Session reuse
  session: savedTlsSession, // Previously saved session
};

Secure HTTP Server (HTTPS)

While the TLS module can be used directly, for HTTPS servers, Node.js provides a higher-level https module built on top of TLS:

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

// HTTPS server options
const options = {
  key: fs.readFileSync(path.join(__dirname, 'server-key.pem')),
  cert: fs.readFileSync(path.join(__dirname, 'server-cert.pem'))
};

// Create HTTPS server
https.createServer(options, (req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/html' });
  res.end('<h1>Secure HTTPS Server</h1><p>This connection is encrypted using TLS.</p>');
}).listen(443, () => {
  console.log('HTTPS server running on port 443');
});

The HTTPS module provides a more convenient way to create secure HTTP servers, but it uses the TLS module under the hood.


TLS with Express

You can also create an HTTPS server with Express:

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

// Create Express app
const app = express();

// Define routes
app.get('/', (req, res) => {
  res.send('<h1>Secure Express App</h1><p>This connection is encrypted using TLS.</p>');
});

app.get('/api/data', (req, res) => {
  res.json({
    message: 'This is sensitive data',
    timestamp: new Date()
  });
});

// HTTPS server options
const options = {
  key: fs.readFileSync(path.join(__dirname, 'server-key.pem')),
  cert: fs.readFileSync(path.join(__dirname, 'server-cert.pem'))
};

// Create HTTPS server with Express app
const port = 443;
https.createServer(options, app).listen(port, () => {
  console.log(`Secure Express app running on port ${port}`);
});

Certificate Verification

TLS uses certificates to verify the identity of servers and optionally clients. Here's an example showing how to implement custom certificate verification:

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

// Custom verification function
function validateCertificate(cert) {
  // Basic certificate info
  console.log('Certificate subject:', cert.subject);
  console.log('Certificate issuer:', cert.issuer);
  console.log('Valid from:', cert.valid_from);
  console.log('Valid to:', cert.valid_to);
  
  // Check certificate validity period
  const now = new Date();
  const validFrom = new Date(cert.valid_from);
  const validTo = new Date(cert.valid_to);
  
  if (now < validFrom || now > validTo) {
    return { valid: false, reason: 'Certificate is not within its validity period' };
  }
  
  // Additional checks could include:
  // - Certificate revocation status
  // - Certificate chain validation
  // - Public key strength
  
  return { valid: true };
}

// Create TLS client with custom validation
const options = {
  ca: [fs.readFileSync('ca-cert.pem')],
  checkServerIdentity: (hostname, cert) => {
    // First check the certificate against our custom rules
    const validationResult = validateCertificate(cert);
    
    if (!validationResult.valid) {
      return new Error(validationResult.reason);
    }
    
    // Then verify the hostname matches the certificate
    const certCN = cert.subject.CN;
    
    if (hostname !== certCN &&
        !cert.subjectaltname ||
        !cert.subjectaltname.includes(hostname)) {
      return new Error(`Certificate name mismatch: ${hostname} !== ${certCN}`);
    }
    
    // Certificate is valid
    return undefined;
  }
};

// Connect to server with custom verification
const client = tls.connect(8000, 'example.com', options, () => {
  if (client.authorized) {
    console.log('Connection authorized');
    client.write('Secure message');
  } else {
    console.log('Connection not authorized:', client.authorizationError);
  }
});

// Handle connection events
client.on('error', (error) => {
  console.error('TLS error:', error);
});

client.on('end', () => {
  console.log('Connection ended');
});

TLS Session Resumption

Session resumption allows clients to reconnect to a server without performing a full TLS handshake, improving performance:

const tls = require('tls');
const fs = require('fs');
const path = require('path');

// Server options
const serverOptions = {
  key: fs.readFileSync(path.join(__dirname, 'server-key.pem')),
  cert: fs.readFileSync(path.join(__dirname, 'server-cert.pem')),
  // Enable session resumption
  sessionTimeout: 300, // Session timeout in seconds
  ticketKeys: Buffer.from('0123456789abcdef0123456789abcdef'), // 32 bytes for key encryption
};

// Create TLS server
const server = tls.createServer(serverOptions, (socket) => {
  console.log('Client connected');
  
  // Check if this is a resumed session
  if (socket.isSessionReused()) {
    console.log('Session reused!');
  } else {
    console.log('New session');
  }
  
  socket.on('data', (data) => {
    console.log('Received:', data.toString());
    socket.write('Hello back!');
  });
  
  socket.on('end', () => {
    console.log('Client disconnected');
  });
});

server.listen(8443, () => {
  console.log('TLS server listening on port 8443');
  
  // First client connection
  connectClient(() => {
    // Second client connection - should use session resumption
    connectClient();
  });
});

// Function to create a client with session resumption
let savedSession = null;

function connectClient(callback) {
  const clientOptions = {
    rejectUnauthorized: false, // For self-signed certificates
    session: savedSession // Use saved session if available
  };
  
  const client = tls.connect(8443, 'localhost', clientOptions, () => {
    console.log('Client connected. Authorized:', client.authorized);
    console.log('Using session resumption:', client.isSessionReused());
    
    // Save the session for future connections
    savedSession = client.getSession();
    
    // Send data
    client.write('Hello server!');
    
    // Close after a short delay
    setTimeout(() => {
      client.end();
      if (callback) setTimeout(callback, 100);
    }, 100);
  });
  
  client.on('data', (data) => {
    console.log('Client received:', data.toString());
  });
  
  client.on('error', (err) => {
    console.error('Client error:', err);
  });
}

Server Name Indication (SNI)

SNI allows a server to present different certificates for different hostnames on the same IP address and port:

const tls = require('tls');
const fs = require('fs');
const path = require('path');

// Load different certificates for different domains
const serverOptions = {
  SNICallback: (servername, cb) => {
    console.log(`SNI request for: ${servername}`);
    
    // Different certificate contexts based on hostname
    if (servername === 'example.com') {
      const context = tls.createSecureContext({
        key: fs.readFileSync(path.join(__dirname, 'example.com-key.pem')),
        cert: fs.readFileSync(path.join(__dirname, 'example.com-cert.pem'))
      });
      cb(null, context);
    }
    else if (servername === 'another.com') {
      const context = tls.createSecureContext({
        key: fs.readFileSync(path.join(__dirname, 'another.com-key.pem')),
        cert: fs.readFileSync(path.join(__dirname, 'another.com-cert.pem'))
      });
      cb(null, context);
    }
    else {
      // Default certificate
      const context = tls.createSecureContext({
        key: fs.readFileSync(path.join(__dirname, 'default-key.pem')),
        cert: fs.readFileSync(path.join(__dirname, 'default-cert.pem'))
      });
      cb(null, context);
    }
  },
  // Default keys and certificates (used as a fallback)
  key: fs.readFileSync(path.join(__dirname, 'default-key.pem')),
  cert: fs.readFileSync(path.join(__dirname, 'default-cert.pem'))
};

// Create server
const server = tls.createServer(serverOptions, (socket) => {
  socket.write(`Hello, you connected to ${socket.servername || 'unknown'}!\n`);
  socket.end();
});

server.listen(8443, () => {
  console.log('TLS SNI server running on port 8443');
});

Advanced Certificate Management

Proper certificate management is crucial for secure TLS communications. Here are some advanced techniques:

1. Certificate Chain and Multiple CAs

const tls = require('tls');
const fs = require('fs');
const path = require('path');

// Load multiple CA certificates
const caCerts = [
  fs.readFileSync(path.join(__dirname, 'ca1-cert.pem')),
  fs.readFileSync(path.join(__dirname, 'ca2-cert.pem')),
  fs.readFileSync(path.join(__dirname, 'intermediate-cert.pem'))
];

// Server with multiple CA certificates
const serverOptions = {
  key: fs.readFileSync(path.join(__dirname, 'server-key.pem')),
  cert: fs.readFileSync(path.join(__dirname, 'server-cert.pem')),
  ca: caCerts,  // Array of CA certificates
  requestCert: true,
  rejectUnauthorized: true
};

const server = tls.createServer(serverOptions, (socket) => {
  console.log('Client connected:', socket.authorized ? 'Authorized' : 'Unauthorized');
  
  // Get peer certificate
  const cert = socket.getPeerCertificate();
  console.log('Client certificate subject:', cert.subject);
  console.log('Issuer:', cert.issuer.CN);
  
  socket.write('Welcome to the secure server!\n');
  socket.end();
});

server.listen(8000, () => {
  console.log('TLS server running on port 8000');
});

2. Certificate Revocation with CRL

const tls = require('tls');
const fs = require('fs');
const crypto = require('crypto');

// Load CRL (Certificate Revocation List)
const crl = fs.readFileSync('revoked-certs.pem');

// Parse CRL to check against
const checkRevocation = (cert) => {
  // In a real application, you would parse the CRL and check
  // if the certificate's serial number is in the revocation list
  
  // For demonstration, we'll just check against a known revoked serial
  const revokedSerials = [
    '0123456789ABCDEF', // Example revoked serial
    'FEDCBA9876543210'
  ];
  
  const certInfo = crypto.certificateVerify(
    cert.raw,
    'sha256',
    Buffer.from(''),
    Buffer.from('')
  );
  
  return !revokedSerials.includes(certInfo.serialNumber.toString('hex').toUpperCase());
};

const server = tls.createServer({
  key: fs.readFileSync('server-key.pem'),
  cert: fs.readFileSync('server-cert.pem'),
  requestCert: true,
  rejectUnauthorized: true,
  
  // Custom certificate validation
  checkServerIdentity: (host, cert) => {
    if (!checkRevocation(cert)) {
      return new Error('Certificate has been revoked');
    }
    return undefined; // No error means certificate is valid
  }
}, (socket) => {
  // Handle connection
  console.log('Client connected:', socket.authorized ? 'Authorized' : 'Unauthorized');
  socket.end('Hello secure world!\n');
});

server.listen(8000);

3. Automatic Certificate Management with Let's Encrypt

const tls = require('tls');
const https = require('https');
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');

class TLSCertManager {
  constructor(domain, email) {
    this.domain = domain;
    this.email = email;
    this.certDir = path.join(__dirname, 'certs', domain);
    this.ensureCertDir();
  }
  
  ensureCertDir() {
    if (!fs.existsSync(this.certDir)) {
      fs.mkdirSync(this.certDir, { recursive: true });
    }
  }
  
  async getCertificates() {
    const keyPath = path.join(this.certDir, 'privkey.pem');
    const certPath = path.join(this.certDir, 'cert.pem');
    const chainPath = path.join(this.certDir, 'chain.pem');
    
    // Check if certificates exist and are valid
    if (this.certsValid(keyPath, certPath, chainPath)) {
      return {
        key: fs.readFileSync(keyPath),
        cert: fs.readFileSync(certPath),
        ca: fs.readFileSync(chainPath)
      };
    }
    
    // Use certbot to obtain new certificates
    return await this.obtainCertificates();
  }
  
  certsValid(keyPath, certPath, chainPath) {
    try {
      if (!fs.existsSync(keyPath) || !fs.existsSync(certPath) || !fs.existsSync(chainPath)) {
        return false;
      }
      
      // Check if certificate is valid for at least 7 more days
      const cert = fs.readFileSync(certPath);
      const notAfter = cert.toString().match(/Not After : (.*?)\n/)[1];
      const expiryDate = new Date(notAfter);
      const now = new Date();
      
      return (expiryDate - now) > 7 * 24 * 60 * 60 * 1000; // 7 days in ms
    } catch (err) {
      console.error('Error checking certificate validity:', err);
      return false;
    }
  }
  
  async obtainCertificates() {
    try {
      // This is a simplified example - in production, use a proper ACME client
      console.log('Obtaining new certificates from Let\'s Encrypt...');
      
      // In a real application, you would use an ACME client like 'greenlock' or 'acme'
      // This is just a placeholder to illustrate the concept
      execSync(`certbot certonly --standalone -d ${this.domain} --email ${this.email} --agree-tos --non-interactive`);
      
      // Copy certificates to our certs directory
      const certs = {
        key: fs.readFileSync(`/etc/letsencrypt/live/${this.domain}/privkey.pem`),
        cert: fs.readFileSync(`/etc/letsencrypt/live/${this.domain}/cert.pem`),
        ca: fs.readFileSync(`/etc/letsencrypt/live/${this.domain}/chain.pem`)
      };
      
      // Save certificates for future use
      fs.writeFileSync(path.join(this.certDir, 'privkey.pem'), certs.key);
      fs.writeFileSync(path.join(this.certDir, 'cert.pem'), certs.cert);
      fs.writeFileSync(path.join(this.certDir, 'chain.pem'), certs.ca);
      
      return certs;
    } catch (err) {
      console.error('Failed to obtain certificates:', err);
      throw err;
    }
  }
}

// Usage example
async function createSecureServer() {
  const certManager = new TLSCertManager('example.com', 'admin@example.com');
  
  try {
    const certs = await certManager.getCertificates();
    
    const server = https.createServer({
      key: certs.key,
      cert: certs.cert,
      ca: certs.ca,
      requestCert: true,
      rejectUnauthorized: true
    }, (req, res) => {
      res.writeHead(200);
      res.end('Hello, secure world!\n');
    });
    
    server.listen(443, () => {
      console.log('HTTPS server running on port 443');
    });
    
    // Schedule certificate renewal check (e.g., daily)
    setInterval(async () => {
      try {
        await certManager.getCertificates();
      } catch (err) {
        console.error('Certificate renewal check failed:', err);
      }
    }, 24 * 60 * 60 * 1000); // Check daily
    
  } catch (err) {
    console.error('Failed to start secure server:', err);
    process.exit(1);
  }
}

createSecureServer();

Note: The Let's Encrypt example is simplified. In production, use a well-maintained ACME client library and follow Let's Encrypt's rate limits and best practices.


Security Best Practices

When using TLS in production applications, consider these security best practices:

1. Use Strong TLS Versions

const options = {
  // Disable older TLS versions
  minVersion: 'TLSv1.2',
  
  // Explicitly disallow TLS 1.0 and 1.1
  secureOptions: crypto.constants.SSL_OP_NO_TLSv1 |
                 crypto.constants.SSL_OP_NO_TLSv1_1
};

2. Configure Strong Cipher Suites

const options = {
  // Prioritize modern, secure cipher suites
  ciphers: [
    'TLS_AES_256_GCM_SHA384',
    'TLS_CHACHA20_POLY1305_SHA256',
    'TLS_AES_128_GCM_SHA256',
    'ECDHE-RSA-AES256-GCM-SHA384',
    'ECDHE-RSA-AES128-GCM-SHA256'
  ].join(':')
};

3. Use Perfect Forward Secrecy

// Cipher suites with ECDHE (Elliptic Curve Diffie-Hellman Ephemeral) support PFS
const options = {
  ciphers: 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'
};

4. Implement OCSP Stapling (Online Certificate Status Protocol)

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

// Server with OCSP stapling
const serverOptions = {
  key: fs.readFileSync(path.join(__dirname, 'server-key.pem')),
  cert: fs.readFileSync(path.join(__dirname, 'server-cert.pem')),
  ca: fs.readFileSync(path.join(__dirname, 'ca-cert.pem')),
  
  // Enable OCSP stapling
  requestOCSP: true,
  
  // OCSP response cache timeout (in milliseconds)
  ocspCache: new tls.OCSPCache({
    max: 1000,  // Maximum number of cached responses
    maxAge: 60 * 60 * 1000  // Cache for 1 hour
  })
};

// Create HTTPS server with OCSP stapling
const server = https.createServer(serverOptions, (req, res) => {
  res.writeHead(200);
  res.end('Hello with OCSP stapling!\n');
});

// Handle OCSP request errors
server.on('OCSPRequest', (cert, issuer, callback) => {
  if (!cert || !issuer) {
    return callback(new Error('No certificate or issuer provided'));
  }
  
  // Get OCSP URL from certificate
  const ocspUrl = tls.getOCSPURL(cert);
  if (!ocspUrl) {
    return callback(new Error('No OCSP URL in certificate'));
  }
  
  console.log('OCSP request for:', cert.subject.CN);
  
  // In a real application, you would make an OCSP request here
  // and return the response via the callback
  
  // For demonstration, we'll just return a dummy response
  const ocspResponse = Buffer.from('OCSP response would go here');
  callback(null, ocspResponse);
});

server.listen(443, () => {
  console.log('HTTPS server with OCSP stapling running on port 443');
});

// Client that verifies OCSP stapling
const clientOptions = {
  host: 'example.com',
  port: 443,
  rejectUnauthorized: true,
  requestOCSP: true  // Request OCSP stapling from server
};

const req = https.request(clientOptions, (res) => {
  console.log('Response status code:', res.statusCode);
  
  // Get the OCSP response from the server
  const ocspResponse = res.socket.getOCSPResponse();
  if (ocspResponse) {
    console.log('Received OCSP response');
    // Verify the OCSP response here
  } else {
    console.log('No OCSP response received');
  }
  
  res.on('data', (chunk) => {
    console.log('Received data:', chunk.toString());
  });
});

req.on('error', (err) => {
  console.error('Request error:', err);
});

req.end();

5. ALPN and SNI Support

Application-Layer Protocol Negotiation (ALPN) and Server Name Indication (SNI) are important TLS extensions that enable protocol negotiation and virtual hosting:

const tls = require('tls');
const http2 = require('http2');
const https = require('https');
const fs = require('fs');
const path = require('path');

// Server with ALPN and SNI support
const serverOptions = {
  // ALPN protocols in order of preference
  ALPNProtocols: ['h2', 'http/1.1'],
  
  // SNI callback for multiple domains
  SNICallback: (servername, cb) => {
    console.log('SNI request for:', servername);
    
    try {
      let context;
      
      // Create different contexts for different domains
      if (servername === 'example.com') {
        context = tls.createSecureContext({
          key: fs.readFileSync(path.join(__dirname, 'example.com-key.pem')),
          cert: fs.readFileSync(path.join(__dirname, 'example.com-cert.pem')),
          // Enable OCSP stapling for this domain
          requestOCSP: true,
          // Custom cipher suites for this domain
          ciphers: [
            'TLS_AES_256_GCM_SHA384',
            'TLS_CHACHA20_POLY1305_SHA256',
            'TLS_AES_128_GCM_SHA256'
          ].join(':')
        });
      } else {
        // Default context for other domains
        context = tls.createSecureContext({
          key: fs.readFileSync(path.join(__dirname, 'default-key.pem')),
          cert: fs.readFileSync(path.join(__dirname, 'default-cert.pem')),
          // Less strict ciphers for legacy clients
          ciphers: [
            'TLS_AES_256_GCM_SHA384',
            'TLS_CHACHA20_POLY1305_SHA256',
            'TLS_AES_128_GCM_SHA256',
            'ECDHE-RSA-AES256-GCM-SHA384',
            'ECDHE-RSA-AES128-GCM-SHA256'
          ].join(':')
        });
      }
      
      // Set ALPN protocols for this context
      context.setALPNProtocols(['h2', 'http/1.1']);
      
      // Return the created context
      if (cb) {
        cb(null, context);
      } else {
        return context;
      }
    } catch (err) {
      console.error('SNI callback error:', err);
      if (cb) {
        cb(err);
      } else {
        throw err;
      }
    }
  },
  
  // Default key and cert (used if SNI is not supported by client)
  key: fs.readFileSync(path.join(__dirname, 'default-key.pem')),
  cert: fs.readFileSync(path.join(__dirname, 'default-cert.pem'))
};

// Create HTTP/2 server with ALPN and SNI
const http2Server = http2.createSecureServer(serverOptions, (req, res) => {
  const protocol = req.socket.alpnProtocol;
  res.writeHead(200);
  res.end(`Hello from ${req.socket.servername} using ${protocol}\n`);
});

http2Server.on('error', (err) => {
  console.error('HTTP/2 server error:', err);
});

http2Server.on('stream', (stream, headers) => {
  const protocol = stream.session.alpnProtocol;
  const hostname = stream.session.servername || 'unknown';
  
  stream.respond({
    'content-type': 'text/plain; charset=utf-8',
    ':status': 200
  });
  
  stream.end(`HTTP/2 stream from ${hostname} using ${protocol}\n`);
});

// Create HTTPS server with ALPN and SNI
const httpsServer = https.createServer(serverOptions, (req, res) => {
  const protocol = req.socket.alpnProtocol;
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end(`Hello from ${req.socket.servername} using ${protocol || 'HTTP/1.1'}\n`);
});

// Handle upgrade to HTTP/2
httpsServer.on('upgrade', (req, socket, head) => {
  const protocol = req.socket.alpnProtocol;
  if (protocol === 'h2') {
    http2Server.emit('connection', socket);
  } else {
    socket.destroy();
  }
});

// Start servers
const PORT = 443;

httpsServer.listen(PORT, () => {
  console.log(`HTTPS server running on port ${PORT}`);
});

// Client example
function makeRequest(hostname, port = 443) {
  const options = {
    hostname,
    port,
    path: '/',
    method: 'GET',
    // Enable ALPN
    ALPNProtocols: ['h2', 'http/1.1'],
    // Set SNI
    servername: hostname,
    // Verify certificate
    rejectUnauthorized: false, // For testing with self-signed certs
    // Custom check for server identity
    checkServerIdentity: (host, cert) => {
      // Implement custom certificate validation
      return undefined; // No error means success
    }
  };

  const req = https.request(options, (res) => {
    console.log(`Status: ${res.statusCode}`);
    console.log('ALPN Protocol:', res.socket.alpnProtocol);
    console.log('Negotiated Protocol:', res.socket.getProtocol());
    
    let data = '';
    res.on('data', (chunk) => {
      data += chunk;
    });
    
    res.on('end', () => {
      console.log('Response:', data.trim());
    });
  });

  req.on('error', (err) => {
    console.error('Request error:', err);
  });

  req.end();
}

// Example usage
// makeRequest('example.com');
// makeRequest('another-domain.com');

6. Use HTTP Strict Transport Security (HSTS)

// In an Express application
app.use((req, res, next) => {
  res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
  next();
});


×

Contact Sales

If you want to use W3Schools services as an educational institution, team or enterprise, send us an e-mail:
sales@w3schools.com

Report Error

If you want to report an error, or if you want to make a suggestion, send us an e-mail:
help@w3schools.com

W3Schools is optimized for learning and training. Examples might be simplified to improve reading and learning. Tutorials, references, and examples are constantly reviewed to avoid errors, but we cannot warrant full correctness of all content. While using W3Schools, you agree to have read and accepted our terms of use, cookie and privacy policy.

Copyright 1999-2025 by Refsnes Data. All Rights Reserved. W3Schools is Powered by W3.CSS.