2014-05-12 484 views
14

我正在使用节点0.10.26并尝试与客户端验证建立https连接。服务器端的客户端证书验证DEPTH_ZERO_SELF_SIGNED_CERT错误

服务器的代码:

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” 的结果为 “通用名称”:

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 

我已经运行客户端和服务器与客户端和服务器的所有证书组合证书(塔t是:[(server1,client1),(server1,client2),(server2,client1),(server2,client2)],并且对这些服务器的每个组合都进行了测试,默认值为“agent” “设置为”false“。

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

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

+0

+1你马立克为是一类的行为给@rhashimoto一个+50赏金。 –

回答

16

我相信你有两个问题,一个是你的代码,另一个是你的证书。

代码问题出现在您的服务器上。你是不是指定CA检查客户端证书与options属性就像你在你的客户端代码有:

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

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

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

+1

你先生,是个天才!我遇到了确切的问题,即我的CA具有与服务器证书相同的CN。我没有意识到这是一个问题,但现在我读了你的答案,我觉得自己像个大笨蛋。 –

10

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

+2

简单而重要;非常感谢你。 – gustavohenke

+0

对我不起作用:( –

+0

downvote,因为它不安全,因为前面的答案给出了问题的原因 –

0

如上所述,使用rejectUnauthorized: false时,有一把大锤钉在你的指甲上。

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

这就需要:

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

(2)在应用程序调用https.request与在ca:属性存储的证书(证书已被手动接受后,请参阅上面有关ca的说明)。

看来,NodeJS不做(1),使(2)不可能?

从安全角度来看,更好的做法是使用EFF的SSL观察站对自签名证书的有效性做出群众来源的判断。同样,这需要NodeJS完成(1)。

我认为开发者需要改善相对于(1)...

+0

事实上,如果您使用rejectUnauthorized:false进行连接,则无法事件获取服务器证书。您可以在请求,响应,连接等对象上一直使用util.inspect(),并且您将永远无法获得NodeJS收到的证书。现在,您只需通过代码接受所有证书或CA签名证书即可。 –

0

只需添加strictSSL: falseoptions的NodeJS。

1

尽管本页面有很长的描述,但我仍然在客户端使用这个配方得到'UNABLE_TO_VERIFY_LEAF_SIGNATURE'错误。也许我在遵循rhashimoto's comment的时候出了点问题。

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

我重复使用original code并稍微修改它以使其工作。 最大的区别在于创建证书文件。

创建证书文件:

# 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代码:

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

如果您有叶节点错误,这是因为您没有将中间证书包含在服务器证书中。您必须将证书与链中的所有中间证书连接起来。 –

6

这一个工作:

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 

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

+1

虽然有效,但它具有全球效应。根据需要拒绝未授权:错误是安全的。 –

+1

用于我的开发测试。 – sfanjoy

0

如果你只有一个。质子交换膜自签名证书(如/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 
};