Nodejs 惊艳了我们 sni
NodeJS STARTTLS Use SNI
我正在 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");
});
}
握手正确,但我使用的证书是 'details' 中的静态证书,而不是我在 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 .
要轻松实现此目的,请在常规 net.Socket 上使用 Haraka's tls_socket pluggableStream 之类的包装器并替换 "upgrade"
功能类似于:
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 中构建一个简单的、支持 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");
});
}
握手正确,但我使用的证书是 'details' 中的静态证书,而不是我在 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 .
要轻松实现此目的,请在常规 net.Socket 上使用 Haraka's tls_socket pluggableStream 之类的包装器并替换 "upgrade" 功能类似于:
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));
// ...
}
}