const tls = require('tls');
const fs = require('fs');
const { generateKeyPairSync, createHash } = require('crypto');
const path = require('path');
// Generate self-signed certificates if they don't exist
function generateSelfSignedCert() {
const certPath = path.join(__dirname, 'selfsigned-cert.pem');
const keyPath = path.join(__dirname, 'selfsigned-key.pem');
// Check if files already exist
if (fs.existsSync(certPath) && fs.existsSync(keyPath)) {
return {
cert: fs.readFileSync(certPath),
key: fs.readFileSync(keyPath)
};
}
console.log('Generating self-signed certificate...');
// Generate a key pair
const { privateKey, publicKey } = generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
cipher: 'aes-256-cbc',
passphrase: 'topsecret'
}
});
// Create a self-signed certificate
const cert = require('crypto').createCertificate({
key: privateKey,
publicKey: publicKey,
serialNumber: '01',
issuer: {
C: 'US',
ST: 'California',
L: 'San Francisco',
O: 'Test',
OU: 'Test',
CN: 'localhost'
},
subject: {
C: 'US',
ST: 'California',
L: 'San Francisco',
O: 'Test',
OU: 'Test',
CN: 'localhost'
},
days: 365,
selfSigned: true
});
// Save the certificate and key
fs.writeFileSync(certPath, cert.toString());
fs.writeFileSync(keyPath, privateKey);
console.log('Self-signed certificate generated');
return {
cert: cert.toString(),
key: privateKey
};
}
// Generate or load self-signed certificate
const { cert, key } = generateSelfSignedCert();
// TLS server options
const options = {
key: key,
cert: cert,
// Request client certificate (optional)
requestCert: true,
// Reject connections without certificates (set to false for testing)
rejectUnauthorized: false,
// Enable all cipher suites for testing (not recommended for production)
ciphers: 'ALL',
// Minimum TLS version
minVersion: 'TLSv1.2',
// Session timeout in seconds
sessionTimeout: 300
};
// Create a TLS server
const server = tls.createServer(options, (socket) => {
console.log('Client connected');
// Get client certificate information
const cert = socket.getPeerCertificate();
const clientAuth = socket.authorized ? 'authorized' : 'unauthorized';
console.log(`Client ${clientAuth}: ${socket.remoteAddress}:${socket.remotePort}`);
if (socket.authorized) {
console.log('Client certificate:');
console.log(` Subject: ${cert.subject.CN}`);
console.log(` Issuer: ${cert.issuer.CN}`);
console.log(` Valid from: ${cert.valid_from}`);
console.log(` Valid to: ${cert.valid_to}`);
} else {
console.log('Client did not present a valid certificate');
console.log('Authorization error:', socket.authorizationError);
}
// Handle data from client
socket.on('data', (data) => {
const message = data.toString().trim();
console.log(`Received from client: ${message}`);
// Echo the message back to the client
socket.write(`Server echo: ${message}\n`);
// Close connection if client sends 'quit'
if (message.toLowerCase() === 'quit') {
console.log('Client requested to close connection');
socket.end('Goodbye!\n');
}
});
// Handle client disconnection
socket.on('end', () => {
console.log(`Client ${socket.remoteAddress}:${socket.remotePort} disconnected`);
});
// Handle socket errors
socket.on('error', (err) => {
console.error(`Socket error: ${err.message}`);
});
// Send welcome message
socket.write('Welcome to the TLS server! Type "quit" to disconnect.\n');
socket.write('> ');
});
// Handle server errors
server.on('tlsClientError', (err, tlsSocket) => {
console.error('TLS client error:', err.message);
if (tlsSocket) {
tlsSocket.destroy();
}
});
// Start the server
const PORT = 8443;
server.listen(PORT, () => {
console.log(`TLS server listening on port ${PORT}`);
console.log('You can test with:');
console.log(` openssl s_client -connect localhost:${PORT} -servername localhost -tls1_2`);
console.log('Or use the test client that will be started next...');
// Start a test client
startTestClient();
});
// Function to start a test client
function startTestClient() {
console.log('\n=== Starting test client ===');
const clientOptions = {
host: 'localhost',
port: PORT,
rejectUnauthorized: false, // For testing with self-signed certificates
servername: 'localhost' // SNI (Server Name Indication)
};
const client = tls.connect(clientOptions, () => {
console.log('Connected to server');
console.log('Client connected:', client.authorized ? 'Authorized' : 'Unauthorized');
// Send a test message
client.write('Hello from test client\n');
// Close connection after a short delay
setTimeout(() => {
client.end('quit\n');
}, 1000);
});
client.on('data', (data) => {
console.log('Server says:', data.toString().trim());
});
client.on('end', () => {
console.log('Disconnected from server');
});
client.on('error', (err) => {
console.error('Client error:', err.message);
});
// Handle process termination
process.on('SIGINT', () => {
console.log('\nShutting down...');
client.end();
server.close();
process.exit(0);
});
}
// Handle process termination
process.on('SIGINT', () => {
console.log('\nShutting down server...');
server.close(() => {
console.log('Server closed');
process.exit(0);
});
});