Client certificate validation on server side, DEPT

2019-02-08 04:38发布

I'm using node 0.10.26 and trying to establish https connection with client validation.

Server's code:

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);

Client's code:

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();

I've created certificates with following commands, each time providing result of "uname -n" as "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

I've run client and server with all compbinations of client's and server's certificates (that is: [(server1, client1), (server1, client2), (server2, client1), (server2, client2)] and for each combination of those server was tested both with default value of "agent" field and with "agent" set to "false".

Each time I ran client.js, res.req.connection.authorizationError was set to DEPTH_ZERO_SELF_SIGNED_CERT.

How can I establish secure connection in node with client's certificate authentication?

7条回答
Bombasti
2楼-- · 2019-02-08 04:53

This one worked for me:

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

Note: Posting answer so it can help others in future.

查看更多
不美不萌又怎样
3楼-- · 2019-02-08 04:55

Just add strictSSL: false to your options.

查看更多
乱世女痞
4楼-- · 2019-02-08 05:01

If you have only a .pem self-signed certificate (e.g. /tmp/ca-keyAndCert.pem) the following options will work:

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
};
查看更多
欢心
5楼-- · 2019-02-08 05:10

I believe you have two problems, one with your code and one with your certificates.

The code issue is in your server. You are not specifying the CA to check client certificates with an options property like you have in your client code:

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

The second problem is the one that really causes that DEPTH_ZERO_SELF_SIGNED_CERT error. You are giving all your certificates - CA, server, and client - the same Distinguished Name. When the server extracts the issuer information from the client certificate, it sees that the issuer DN is the same as the client certificate DN and concludes that the client certificate is self-signed.

Try regenerating your certificates, giving each one a unique Common Name (to make the DN also unique). For example, name your CA certificate "Foo CA", your server certificate the name of your host ("localhost" in this case), and your client something else (e.g. "Foo Client 1").

查看更多
地球回转人心会变
6楼-- · 2019-02-08 05:12

For those of that want to use a self-signed certificate, the answer is to add rejectUnauthorized: false to the https.request options.

查看更多
三岁会撩人
7楼-- · 2019-02-08 05:12

As mentioned above, there is a sledgehammer to hammer in your nail, using rejectUnauthorized: false.

A more sensible option, from a security point of view, would be to ask the user if (s)he would like to accept and store the self-signed server certificate, just like a browser (or SSH) does.

That would require:

(1) That NodeJS throws an exception that contains the server certificate, and

(2) that the application calls https.request with the stored certificate in the ca: property (see above for description of ca) after the certificate has been manually accepted.

It seems that NodeJS does not do (1), making (2) impossible?

Even better from a security point of view would be to use EFF's SSL observatory to make a crowd-sourced judgement on the validity of a self-signed certificate. Again, that requires NodeJS to do (1).

I think a developer needs to improve NodeJS with respect to (1)...

查看更多
登录 后发表回答