node.js 服务器端客户端证书验证,DEPTH_ZERO_SELF_SIGNED_CERT 错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23601989/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 17:19:23  来源:igfitidea点击:

Client certificate validation on server side, DEPTH_ZERO_SELF_SIGNED_CERT error

node.jssslcertificate

提问by Marek

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

我正在使用节点 0.10.26 并尝试通过客户端验证建立 https 连接。

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":

我使用以下命令创建了证书,每次都提供“uname -n”的结果作为“通用名称”:

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".

我已经使用客户端和服务器证书的所有组合运行客户端和服务器(即:[(server1, client1), (server1, client2), (server2, client1), (server2, client2)] 以及这些证书的每个组合服务器在“agent”字段的默认值和“agent”设置为“false”的情况下进行了测试。

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

每次我运行 client.js 时,res.req.connection.authorizationError 都被设置为 DEPTH_ZERO_SELF_SIGNED_CERT。

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

如何使用客户端的证书身份验证在节点中建立安全连接?

回答by rhashimoto

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 optionsproperty like you have in your client code:

代码问题出在您的服务器中。您没有指定 CA 来检查客户端证书与options您在客户端代码中的属性:

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

The second problem is the one that really causes that DEPTH_ZERO_SELF_SIGNED_CERTerror. 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.

第二个问题是真正导致该DEPTH_ZERO_SELF_SIGNED_CERT错误的问题。您为所有证书(CA、服务器和客户端)提供了相同的专有名称。当服务器从客户端证书中提取颁发者信息时,它看到颁发者 DN 与客户端证书 DN 相同,并得出客户端证书是自签名的结论。

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").

尝试重新生成证书,给每个证书一个唯一的通用名称(使 DN 也是唯一的)。例如,将您的 CA 证书命名为“Foo CA”,将您的服务器证书命名为您的主机名(在本例中为“localhost”),将您的客户端命名为其他名称(例如“Foo Client 1”)。

回答by B T

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

对于那些想要使用自签名证书的人,答案是添加rejectUnauthorized: false到 https.request 选项。

回答by Manwal

This one worked for me:

这个对我有用:

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

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

注意:发布答案以便将来可以帮助其他人。

回答by Hill

Despite of long lines of description in this page, I still got 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' error in the client side with this recipe. Maybe I had something wrong in following rhashimoto's comment.

尽管此页面中有很长的描述行,但我仍然在使用此配方的客户端中收到“UNABLE_TO_VERIFY_LEAF_SIGNATURE”错误。也许我在遵循rhashimoto 的评论时出了什么问题。

By referencing book of 'Professional Node.js', I found another way to test HTTPS with Client Certificate Verification successfully.
Here is my story.
By setting requestCert: truein server side, the server tries to validate client certificate. But the default CA doesn't validate the self-signed certificate of the client. I can succeed the test with simple trick -- copy the client certificate and say that is a Certificate Authority.

通过参考' Professional Node.js'一书,我找到了另一种使用客户端证书验证成功测试HTTPS的方法。
这是我的故事。
通过requestCert: true在服务器端设置,服务器尝试验证客户端证书。但默认 CA 不会验证客户端的自签名证书。我可以通过简单的技巧成功测试 - 复制客户端证书并说这是一个证书颁发机构。

I reused original codeand modified it slightly to make it work. The big difference is in creating certificate files.

我重用了原始代码并稍微修改了它以使其工作。最大的区别在于创建证书文件。

Creating certificate files:

创建证书文件:

# 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

Server code:

服务器代码:

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,
};

Client code:

客户端代码:

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

回答by taco

Just add strictSSL: falseto your options.

只需添加strictSSL: false到您的options.

回答by Jim Shark

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

如上所述,有一把大锤可以用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.

从安全角度来看,一个更明智的选择是询问用户是否愿意接受并存储自签名服务器证书,就像浏览器(或 SSH)一样。

That would require:

那将需要:

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

(1) NodeJS 抛出一个包含服务器证书的异常,并且

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

(2)在证书被手动接受后,应用程序调用https.request存储在ca:属性中的证书(见上面的描述ca)。

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

似乎NodeJS不做(1),使(2)不可能?

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

从安全的角度来看,更好的是使用 EFF 的 SSL 观察站对自签名证书的有效性进行众包判断。同样,这需要 NodeJS 执行 (1)。

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

我认为开发人员需要在 (1) 方面改进 NodeJS ...

回答by AR1

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

如果您只有一个 .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
};