Проверка сертификата клиента на стороне сервера, ошибка DEPTH_ZERO_SELF_SIGNED_CERT

Я использую node 0.10.26 и пытаюсь установить соединение https с проверкой клиента.

Код сервера:

var https = require('https');
var fs = require('fs');

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

var options = {
    key: fs.readFileSync('ssl/server1.key'),
    cert: fs.readFileSync('ssl/server1.pem'),
    requestCert: true,
    rejectUnauthorized: false,
};

var server = https.createServer(options, function (req, res) {
    if (req.client.authorized) {
        res.writeHead(200, {"Content-Type":"application/json"});
        res.end('{"status":"approved"}');
        console.log("Approved Client ", req.client.socket.remoteAddress);
    } else {
        console.log("res.connection.authroizationError:  " + res.connection.authorizationError);
        res.writeHead(403, {"Content-Type":"application/json"});
        res.end('{"status":"denied"}');
        console.log("Denied Client " , req.client.socket.remoteAddress);
    }
});

server.on('error', function(err) {
    console.log("server.error: "  + err);
});

server.on("listening", function () {
    console.log("Server listeining");
});

server.listen(5678);

Клиентский код:

var https = require('https');
var fs = require('fs');

var options = {
    host: 'localhost',
    port: 5678,
    method: 'GET',
    path: '/',
    headers: {},
    agent: false,
    key: fs.readFileSync('ssl/client2.key'),
    cert: fs.readFileSync('ssl/client2.pem'),
    ca: fs.readFileSync('ssl/ca.pem')
};

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

var req = https.request(options, function(res) {
    console.log(res.req.connection.authorizationError);
});

req.on("error", function (err) {
    console.log('error: ' + err);
});

req.end();

Я создал сертификаты со следующими командами, каждый раз предоставляя результат "uname -n" как "Common Name":

openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -days 999 -out ca.pem

openssl genrsa -out server1.key  1024
openssl req -new -key server1.key -out server1.csr
openssl x509 -req -days 999 -in server1.csr -CA ca.pem  -CAkey ca.key -set_serial 01 -out server1.pem

openssl genrsa  -out client1.key 1024
openssl req -new -key client1.key  -out client1.csr
openssl  x509  -req -days 999 -in client1.csr  -CA ca.pem  -CAkey ca.key  -set_serial 01     -out client1.pem

openssl genrsa  -out server2.key 1024
openssl req -new -key server2.key  -out server2.csr
openssl  x509  -req -days 999 -in server2.csr -CA server1.pem -CAkey server1.key -     set_serial 02 -out server2.pem

openssl  genrsa -out client2.key 1024
openssl req -new -key client2.key -out client2.csr
openssl x509 -req -days 999 -in client2.csr -CA client1.pem -CAkey client1.key  -set_serial 02 -out client2.pem

Я запускаю клиент и сервер со всеми комбинациями сертификатов клиентов и серверов (то есть: [(server1, client1), (server1, client2), (server2, client1), (server2, client2)] и для каждого комбинация этого сервера была протестирована как со значением по умолчанию для поля "агент", так и с "агентом", установленным на "false".

Каждый раз, когда я запускал client.js, res.req.connection.authorizationError был установлен в DEPTH_ZERO_SELF_SIGNED_CERT.

Как установить безопасное соединение в node с проверкой сертификата клиента?

Ответы

Ответ 1

Я считаю, что у вас две проблемы: одна с кодом и одна с сертификатами.

Проблема с кодом на вашем сервере. Вы не указываете CA для проверки клиентских сертификатов с помощью свойства options, которое у вас есть в вашем клиентском коде:

ca: fs.readFileSync('ssl/ca.pem'),

Вторая проблема - та, которая действительно вызывает ошибку DEPTH_ZERO_SELF_SIGNED_CERT. Вы предоставляете все свои сертификаты - CA, сервер и клиент - такое же Distinguished Name. Когда сервер извлекает информацию эмитента из сертификата клиента, он видит, что DN эмитента совпадает с DN сертификата клиента и заключает, что сертификат клиента самоподписан.

Попробуйте восстановить сертификаты, предоставив каждому уникальное общее имя (чтобы сделать DN уникальным). Например, назовите свой сертификат CA "Foo CA", ваш сертификат сервера - имя вашего хоста (в данном случае - "localhost" ), а ваш клиент - что-то еще (например, "Foo Client 1" ).

Ответ 2

Для тех, кто хочет использовать самозаверяющий сертификат, ответ заключается в том, чтобы добавить rejectUnauthorized: false в параметры https.request.

Ответ 3

Это работало для меня:

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

Примечание. Отправка ответа, чтобы он мог помочь другим в будущем.

Ответ 4

Несмотря на длинные строки описания на этой странице, я все еще получил ошибку "UNABLE_TO_VERIFY_LEAF_SIGNATURE" на стороне клиента с помощью этого рецепта. Может быть, у меня что-то не так, следуя комментарию о рашимото.

Отсылая книгу Professional Node.js ', я нашел еще один способ успешно протестировать HTTPS с проверкой сертификата клиента. < ш > Вот моя история.
Установив requestCert: true на стороне сервера, сервер пытается проверить сертификат клиента. Но ЦС по умолчанию не проверяет самозаверяющий сертификат клиента. Я могу успешно пройти тест с помощью простого трюка - скопируйте клиентский сертификат и скажите, что это центр сертификации.

Я повторно использовал оригинальный код и немного изменил его, чтобы он работал. Большая разница заключается в создании файлов сертификатов.

Создание файлов сертификата:

# create client private key
openssl genrsa -out client2.key
openssl req -new -key client2.key -out client2.csr
# create client certificate
openssl x509 -req -in client2.csr -signkey client2.key -out client2.pem

# create server private key and certificate
openssl genrsa -out server1.key
openssl req -new -key server1.key -out server1.csr
openssl x509 -req -in server1.csr -signkey server1.key -out server1.pem

# * Important *: create fake CA with client certificate for test purpose
cp client2.pem fake_ca.pem

Код сервера:

var options = {
    key: fs.readFileSync('ssl/server1.key'),
    cert: fs.readFileSync('ssl/server1.pem'),
    ca: [fs.readFileSync('ssl/fake_ca.pem')], // Line added
    requestCert: true,
    rejectUnauthorized: false,
};

Клиентский код:

    key: fs.readFileSync('ssl/client2.key'),
    cert: fs.readFileSync('ssl/client2.pem'),
    //ca: fs.readFileSync('ssl/ca.pem') // Line commented
};

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var req = https.request(options, function(res) {
    //console.log(res.req.connection.authorizationError);
    console.log("statusCode:", res.statusCode);
    res.on('data', function(d) {
      console.log('data:', d.toString());
    });
});
req.on("error", function (err) {
    console.log('error: ' + err);
});
req.end();

Ответ 5

Как упоминалось выше, есть кувалда, чтобы забить ваш гвоздь, используя rejectUnauthorized: false.

Более разумным вариантом, с точки зрения безопасности, было бы спросить пользователя, если он хочет принять и сохранить самоподписанный серверный сертификат, как это делает браузер (или SSH).

Это потребует:

(1) Что NodeJS выбрасывает исключение, которое содержит сертификат сервера и

(2), что приложение вызывает https.request с сохраненным сертификатом в свойстве ca: (см. выше для описания ca) после того, как сертификат был принят вручную.

Кажется, что NodeJS не делает (1), делая невозможным (2)?

Еще лучше с точки зрения безопасности было бы использовать обсерваторию EFF SSL, чтобы сделать суждение, основанное на толпе, на достоверность самозаверяющего сертификата. Опять же, для этого требуется NodeJS (1).

Я думаю, что разработчику необходимо улучшить NodeJS по отношению к (1)...

Ответ 6

Просто добавьте strictSSL: false к вашему options.

Ответ 7

Если у вас есть только самоподписанный сертификат .pem(например,/tmp/ca-keyAndCert.pem), будут работать следующие параметры:

var options = {
      url: 'https://<MYHOST>:<MY_PORT>/',
      key: fs.readFileSync('/tmp/ca-keyAndCert.pem'),
      cert: fs.readFileSync('/tmp/ca-keyAndCert.pem'),
      requestCert: false,
      rejectUnauthorized: false
};