NodeJS STARTTLS使用SNI

我正在Node.JS中构build一个简单的,具有STARTTLSfunction的POP3代理服务器,而且我还有相当长的一段时间。

代理服务器作为许多后端服务器的前端,所以它必须dynamic加载他们的证书,这取决于客户端的连接。

我试图使用SNICallback,它带给我客户端使用的服务器名称,但之后我无法设置正确的证书,因为在创build安全上下文之前,我需要一个证书。

代码如下:

// 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中获得的证书。

为了testing它,我正在运行服务器并使用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的答案很简单,尽pipe听众有一个疑问。

你必须从正常的net.Socket中删除所有的监听器,使用你的net.Socket实例化一个新的tls.TLSSocket,并把监听器放回tls.TLSSocket。

为了实现这一点,可以使用像Haraka的tls_socket pluggableStream这样的包装器来覆盖常规的net.Socket,并将“升级”functionreplace为如下所示:

 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)); // ... } }