node.js - NodeJS STARTTLS 使用 SNI

标签 node.js ssl pop3 sni starttls

我正在 Node.JS 中构建一个简单的、支持 STARTTLS 的 POP3 代理,但我遇到了相当困难。

代理充当许多后端服务器的前端,因此它必须根据客户端的连接动态加载其证书。

我正在尝试使用 SNICallback,它为我提供客户端使用的服务器名称,但在此之后我无法设置正确的证书,因为当我创建安全上下文时,在进行此调用之前我需要一个证书.

代码如下:

// Load libraries
var net = require('net');
var tls = require('tls');
var fs = require('fs');

// Load certificates (created with openssl)
var certs = [];
for (var i = 1; i <= 8; i++) {
   var hostName = 'localhost' + i;
   certs[hostName] = {
      key : fs.readFileSync('./private-key.pem'),
      cert : fs.readFileSync('./public-cert' + i + '.pem'),
   }
}

var server = net.createServer(function(socket) {
   socket.write('+OK localhost POP3 Proxy Ready\r\n');

   socket.on('data', function(data) {
      if (data == "STLS\r\n") {
         socket.write("+OK begin TLS negotiation\r\n");
         upgradeSocket(socket);
      } else if (data == "QUIT\r\n") {
         socket.write("+OK Logging out.\r\n");
         socket.end();
      } else {
         socket.write("-ERR unknown command.\r\n");
      }
   });

}).listen(10110);

和upgradeSocket()如下:

function upgradeSocket(socket) {
   // I need this 'details' or handshake will fail with a message: 
   // SSL routines:ssl3_get_client_hello:no shared cipher
   var details = {
       key : fs.readFileSync('./private-key.pem'),
       cert : fs.readFileSync('./public-cert1.pem'),
   }

   var options = {
      isServer : true,
      server : server,
      SNICallback : function(serverName) {
         return tls.createSecureContext(certs[serverName]);
      },
   }

   sslcontext = tls.createSecureContext(details);
   pair = tls.createSecurePair(sslcontext, true, false, false, options);

   pair.encrypted.pipe(socket);
   socket.pipe(pair.encrypted);

   pair.fd = socket.fd;
   pair.on("secure", function() {
      console.log("TLS connection secured");
   });
}

它握手正确,但我使用的证书是“详细信息”中的静态证书,而不是我在 SNICallback 中获得的证书。

为了测试它,我正在运行服务器并使用 gnutls-cli 作为客户端:

~$ gnutls-cli -V -s -p 10110 --crlf --insecure -d 5 localhost3
STLS
^D (Control+D)

上面的命令应该为我获取“localhost3”证书,但它获取的是“localhost1”,因为它是在“details”var 中定义的;

互联网上有太多使用 HTTPS 或 TLS 客户端的示例,这与我这里的示例有很大不同,甚至还有服务器,但它们没有使用 SNI。任何帮助将不胜感激。

提前致谢。

最佳答案

使用 tls.TLSSocket 答案非常简单,尽管监听器存在一个问题。

您必须从常规 net.Socket 中删除所有监听器,使用 net.Socket 实例化新的 tls.TLSSocket 并将监听器放回到 tls.TLSSocket 上。

要轻松实现此目的,请使用类似 Haraka's tls_socket pluggableStream 的包装器通过常规net.Socket并替换“升级” 函数类似于:

pluggableStream.prototype.upgrade = function(options) {
    var self = this;
    var socket = self;
    var netSocket = self.targetsocket;

    socket.clean();

    var secureContext = tls.createSecureContext(options)
    var tlsSocket = new tls.TLSSocket(netSocket, {
        // ...
        secureContext : secureContext,
        SNICallback : options.SNICallback
        // ...
    });

    self.attach(tlsSocket);
}

并且您的选项对象将 SNICallback 定义为:

var options {
    // ...
    SNICallback : function(serverName, callback){
        callback(null, tls.createSecureContext(getCertificateFor(serverName));
    // ...
    }
}

关于node.js - NodeJS STARTTLS 使用 SNI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35586957/

相关文章:

javascript - 如何在 Pug 中渲染详细 View 而不是其所有对象?

ssl - Asio 流式传输 - 使用 SSL/TLS 加密的速度较慢

google-app-engine - 在 Google App Engine 上使用 HTTPS SSL 时裸域重定向失败

c# - 使用 smtp 对 Intranet 的用户进行身份验证

php - 用 PHP 实现的 IMAP 或 POP3 服务器

delphi - TNMPOP3.Connect 生成两次异常

node.js - Sails 服务器未启动

node.js - Node js V4.2.2 LTS 和 V5.0.0 Stable 有什么区别

javascript - 如何防止用户使用 express 访问客户端 html 文件?

c - 启用记录内部 openssl 消息