用 Node.js 啟一個 TLS server

隨著網路安全的日益重要,使用 TLS 加密資料傳輸已成為必不可少的措施。TLS 是一種安全協議,可確保資料在傳輸過程中不被竊聽或篡改。本文章將以 Node.js 來起一個 TLS server ,並使用我們自己產生的憑證來進行 demo

產生 key 與 certificate

在使用 TLS 協議時,server 需要一個 key 進行資料加密,還需要一個 certificate (憑證) 確保 server 是被認證的。通常 certificate 是由機構認證的,如: Let’s Encrypt、Comodo、DigiCer … 等,在此範例中,我們使用自簽 certificate 來進行演示

首先要先確認設備上已經有 openssl

openssl -v

生成 key

openssl genrsa -out server-key.pem 2048

  • 這個產生出來的 key 實際上是 private key

openssl rsa -pubout -in server-key.pem -out server-key.pub

  • 透過 private key 也可以取得 public key,而 public key 會一起放在 certificate 中,所以我們無需特別去產 public key 檔案

自簽 certificate

openssl req -new -key server-key.pem -x509 -days 365 -out server-cert.pem

  • 在 certificate 中,會透過 private key 產生 public key 放到 certificate 中,client side 可以從 certificate 取得 public key 對資料進行加密,後續到 server 後再進行解密

使用 Node.js 作為 client 連到 TLS server

Server

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

// 建立 tls server
const server = tls.createServer({
  // 使用 private key
  key: fs.readFileSync('server-key.pem'),
  // 使用 certificate
  cert: fs.readFileSync('server-cert.pem')
});

server.on('secureConnection', socket => {
  console.log('Client connected');

  socket.on('data', data => {
    console.log('Received data from client:', data.toString());

    socket.write('Hello, client!');

    // 斷開 client 連線
    socket.end();
  });

  socket.on('end', () => {
    console.log('Client disconnected');
  });
});

server.listen(8080, () => {
  console.log('Server listening on port 8080');
});

當有 client 連上此 server 後,server 的終端機會印出 ‘Client connected’,並在 client 傳資料到 server 時,印出 client 傳來的資料,並返回 ‘Hello, client’ 到 client side

Client

  • 因為我們用自簽憑證,需要加入NODE_TLS_REJECT_UNAUTHORIZED = 0 環境變數,允許連線到不被信任的 server
const tls = require('tls');
const fs = require('fs');

// 允許連線到不信任的 server (自簽憑證)
process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0;

// 進行 tls 連線
const client = tls.connect({
  host: 'localhost',
  port: 8080,
});

client.on('connect', () => {
  console.log('Client connected to server');

  client.write('Hello, server!');
});

client.on('data', data => {
  console.log('Received data from server:', data.toString());
});

client.on('end', () => {
  console.log('Client disconnected from server');
});

當連上 server 後,終端機印出 ‘Client connected to server’,在收到 server 傳來的資料時,印出 server 來的資料

使用瀏覽器作為 client 連到 TLS server

  • server 我們做了一些調整,改成依照 HTTP response 格式回傳給瀏覽器,讓瀏覽器可以正確解析
const tls = require('tls');
const fs = require('fs');

// 建立 tls server
const server = tls.createServer({
  // 使用 private key
  key: fs.readFileSync('server-key.pem'),
  // 使用 certificate
  cert: fs.readFileSync('server-cert.pem')
});

server.on('secureConnection', socket => {
  console.log('Client connected');

  socket.on('data', data => {
    console.log('Received data from client:', data.toString());

    const response =
      'HTTP/1.1 200 OK\r\n' +
      'Content-Type: text/plain\r\n' +
      '\r\n' +
      'Hello, client!\r\n';

    socket.write(response);
  });

  socket.on('end', () => {
    console.log('Client disconnected');
  });
});

server.listen(8080, () => {
  console.log('Server listening on port 8080');
});

Client

用瀏覽器連上 https://localhost:8080

注意這裡是使用 https 而不是 http

會先看到是否連上不信任的 server,可以按 advance 連上此 server

就可以成功在畫面上看到 Hello, client!