2016-02-23 8 views
2

Я строю простой, способный к STARTTLS POP3-прокси в Node.JS, и мне очень тяжело.NodeJS STARTTLS Использовать SNI

Прокси-сервер служит интерфейсом для многих серверных серверов, поэтому он должен динамически загружать свои сертификаты в зависимости от подключения Клиента.

Я пытаюсь использовать 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. Любая помощь будет оценена.

Заранее спасибо.

+0

Я только что видел эту открытую ошибку в Node.js GitHub, мне интересно, если это, по сути, проблема: https://github.com/nodejs/node/issues/4878 –

ответ

1

Ответ довольно прост с использованием tls.TLSSocket, хотя есть и есть прием с слушателями.

Вы должны удалить всех слушателей из обычного net.Socket, который у вас есть, создать новый tls.TLSSocket с помощью вашего net.Socket и вернуть слушателей на 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)); 
    // ... 
    } 
}