TLS/SSL moduli


ULASHISH

TLS/SSL nima?

Transport Layer Security (TLS) va undan oldingi Secure Socket Layer (SSL) bu kompyuter tarmog‘i orqali xavfsiz aloqani ta’minlovchi protokollardir. Ular quyidagilarni ta’minlaydi:

  • Maxfiylik: tinglashning oldini olish uchun aloqalar shifrlangan
  • Ma’lumotlar yaxlitligi: xabar mazmunini aniqlanmasdan o‘zgartirib bo‘lmaydi
  • Autentifikatsiya: Muloqot qilayotgan tomonlarning shaxsini tekshirish mumkin

TLS/SSL odatda xavfsizlik uchun ishlatiladi:

  • Web browsing (HTTPS)
  • Email transmissions (SMTP, IMAP, POP3)
  • Tezkor xabar almashish
  • Voice over IP (VoIP)
  • API aloqasi

TLS modulidan foydalanish

Node.js da TLS modulidan foydalanish uchun siz uni talab qilishingiz kerak:

const tls = require('tls');

TLS serveri

Asosiy TLS serverini qanday yaratish mumkin:

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}`);
});

Bu misol uchun sertifikat fayllari (server-key.pem va server-cert.pem) kerak. Ishlab chiqish maqsadlarida OpenSSL yordamida o‘z-o‘zidan imzolangan (self-signed) sertifikatlar yaratishingiz mumkin.

Rivojlanish uchun o‘z-o‘zidan imzolangan sertifikatlarni yaratish

Rivojlanish va sinov uchun o‘z-o‘zidan imzolangan sertifikatlarni yaratish uchun OpenSSL dan foydalanishingiz mumkin:

# 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 mijozi

TLS serveriga ulanadigan mijoz yaratish:

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 va mijoz opsiyalari

tls.createServer() va tls.connect() ikkalasi ham TLS ulanishini sozlash uchun turli variantlarni qabul qiladi:

Umumiy variantlar

  • key : PEM formatidagi shaxsiy kalit
  • cert : PEM formatidagi sertifikat
  • ca : Ishonchli CA sertifikatlari
  • ciphers : Shifr to‘plami spetsifikatsiyalar stringi
  • minVersion : Ruxsat berish uchun minimal TLS versiyasi
  • maxVersion : Ruxsat berilgan maksimal TLS versiyasi

Serverga xos parametrlar

  • requestCert : Mijozlardan sertifikat so‘rash kerakmi
  • rejectUnauthorized : Yaroqsiz sertifikatlarga ega mijozlarni rad etish
  • SNICallback : Mijozdan SNI bilan ishlash funksiyasi

Mijoz uchun maxsus variantlar

  • servername : SNI uchun server nomi
  • checkServerIdentity : Server xost nomini tekshirish funksiyasi
  • session : TLS seansini o‘z ichiga olgan buffer namunasi
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
};

Xavfsiz HTTP server (HTTPS)

TLS modulidan to‘g‘ridan-to‘g‘ri foydalanish mumkin bo‘lsa-da, HTTPS serverlari uchun Node.js TLS ustiga qurilgan yuqori darajadagi https modulini taqdim etadi:

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');
});

HTTPS moduli xavfsiz HTTP serverlarini yaratishning qulayroq usulini taqdim etadi, ammo u qopqoq ostidagi TLS modulidan foydalanadi.


Express bilan TLS

Express yordamida HTTPS serverini ham yaratishingiz mumkin:

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}`);
});

Sertifikatni tekshirish

TLS serverlar va ixtiyoriy ravishda mijozlarning identifikatorini tekshirish uchun sertifikatlardan foydalanadi. Shaxsiy sertifikat tekshiruvini qanday amalga oshirishni ko‘rsatadigan misol:

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 sessiyasini davom ettirish

Seansni qayta boshlash mijozlarga to‘liq TLS qo‘l siqishini amalga oshirmasdan serverga qayta ulanish imkonini beradi, bu esa unumdorlikni oshiradi:

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 nomini ko‘rsatish (SNI)

SNI serverga bir xil IP manzil va portda turli xost nomlari uchun turli sertifikatlarni taqdim etish imkonini beradi:

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');
});

Kengaytirilgan sertifikat boshqaruvi

Xavfsiz TLS aloqasi uchun sertifikatni to‘g‘ri boshqarish juda muhimdir. Mana bir necha ilg‘or texnikalar:

1. Sertifikat zanjiri va bir nechta CA

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. CRL bilan sertifikatni bekor qilish

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. Let’s Encrypt bilan sertifikatlarni avtomatik boshqarish

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();

Eslatma: Keling, shifrlaymiz misoli soddalashtirilgan. Ishlab chiqarishda yaxshi saqlangan ACME mijozlar kutubxonasidan foydalaning va Let’s Encrypt tezligi cheklovlari va eng yaxshi amaliyotlariga amal qiling.


Xavfsizlik bo‘yicha eng yaxshi amaliyotlar

Ishlab chiqarish ilovalarida TLS dan foydalanilganda, ushbu xavfsizlik bo‘yicha eng yaxshi amaliyotlarni ko‘rib chiqing:

1. Kuchli TLS versiyalaridan foydalaning

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. Strong Cipher Suitesni sozlang

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. Perfect Forward Secrecy dan foydalaning

// 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. OCSP stapling’ni joriy qiling (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 va SNI qo‘llab-quvvatlashi

Application-Layer Protocol Negotiation (ALPN) va Server Name Indication (SNI) protokol muzokaralari va virtual hostingni ta’minlaydigan muhim TLS kengaytmalaridir:

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. HTTP Strict Transport Security (HSTS) mexanizmidan foydalaning

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


W3Schools Pathfinder

Yutuqlaringizni kuzating – bu bepul!