主机名/ IP不匹配证书的名称

我试图创build一个TLS服务器/客户端设置使用Node.js 0.8.8与自签名证书。

基本的服务器代码看起来像

var tlsServer = tls.createServer({ key: fs.readFileSync('server-key.pem'), cert: fs.readFileSync('server-cert.pem') }, function (connection) { // [...] }); tlsServer.listen(3000); 

现在当我尝试连接到这个服务器时,我使用下面的代码:

 var connection = tls.connect({ host: '192.168.178.31', port: 3000, rejectUnauthorized: true, ca: [ fs.readFileSync('server-cert.pem') ] }, function () { console.log(connection.authorized); console.log(connection.authorizationError); console.log(connection.getPeerCertificate()); }); 

如果我删除线

 ca: [ fs.readFileSync('server-cert.pem') ] 

从客户端代码中,Node.js抛出一个错误告诉我DEPTH_ZERO_SELF_SIGNED_CERT 。 据我了解,这是因为这是一个自签证书,没有其他人信任这个证书。

如果我删除

 rejectUnauthorized: true, 

错误消失了 – 但是connection.authorized等于false ,这实际上意味着我的连接没有被encryption。 无论如何,使用getPeerCertificate()我可以访问服务器发送的证书。 由于我想执行一个encryption的连接,我明白,我可能不会删除这一行。

现在我读了一下,我可以使用ca属性来指定我希望Node.js信任的任何CA. TLS模块的文档意味着将服务器证书添加到ca数组就足够了,然后一切都会好起来的。

如果我这样做,这个错误消失了,但我得到一个新的:

 Hostname/IP doesn't match certificate's altnames 

对我来说,这意味着CA现在基本上是可信的,所以现在没问题,但是证书是为另一个主机而不是我使用的。

我使用创build的证书

 $ openssl genrsa -out server-key.pem 2048 $ openssl req -new -key server-key.pem -out server-csr.pem $ openssl x509 -req -in server-csr.pem -signkey server-key.pem -out server-cert.pem 

正如文件所暗示的那样。 在创build企业社会责任时,我会问到一些常见的问题,例如国家,州,…和通用名称(CN)。 正如您在网上所说的SSL证书一样,您不会提供您的名字作为CN,而是您想要使用的主机名。

而这可能是我失败的地方。

我试过了

  • localhost
  • 192.168.178.31
  • eisbaer
  • eisbaer.fritz.box

最后两个是我的机器的本地名称和完全合格的本地名称。

任何想法我在这里做错了吗?

最近有一个node.js的附加,它允许用自定义函数覆盖主机名检查。 它已添加到v0.11.14,并将在下一个稳定版本(0.12)中提供。 现在你可以做一些事情:

 var options = { host: '192.168.178.31', port: 3000, ca: [ fs.readFileSync('server-cert.pem') ], checkServerIdentity: function (host, cert) { return undefined; } }; options.agent = new https.Agent(options); var req = https.request(options, function (res) { //... }); 

这将现在接受任何服务器身份,但仍然encryption连接和validation密钥。

请注意 ,在以前的版本(例如v0.11.14 )中, checkServerIdentity将返回一个指示服务器有效性的boolean 。 如果存在问题,那么已经改变了(在v4.3.1之前)函数return (不throw )错误,而如果有错误,则return undefined

在tls.js,第112-141行中 ,可以看到,如果调用connect时使用的主机名是IP地址,则证书的CN将被忽略,只有SAN正在使用。

由于我的证书不使用SAN,validation失败。

如果您使用主机名进行连接,则将根据DNStypes的主题备用名称(如果有)来检查主机名,否则返回主题专有名称中的CN。

如果您使用IP地址进行连接,则IP地址将针对IP地址types的SAN进行检查,而不会退回到CN上。

至less有哪些实现符合HTTP over TLS规范(即HTTPS)。 有些浏览器有点宽容。

这与Java中的这个答案完全相同,也提供了一种通过OpenSSL放置自定义SAN的方法(请参阅本文档 )。

一般来说,除非用于testingCA,否则很难pipe理依赖IP地址的证书。 连接主机名称更好。

Mitar有一个错误的假设,即checkServerIdentity应该在成功时返回'true',但实际上它应该返回'undefined'成功。 任何其他值都被视为错误描述。

所以这样的代码是正确的:

 var options = { host: '192.168.178.31', port: 3000, ca: [ fs.readFileSync('server-cert.pem') ], checkServerIdentity: function (host, cert) { // It can be useful to resolve both parts to IP or to Hostname (with some synchronous resolver (I wander why they did not add done() callback as the third parameter)). // Be carefull with SNI (when many names are bound to the same IP). if (host != cert.subject.CN) return 'Incorrect server identity';// Return error in case of failed checking. // Return undefined value in case of successful checking. // Ie you could use empty function body to accept all CN's. } }; options.agent = new https.Agent(options); var req = https.request(options, function (res) { //... }); 

我试图在Mitar的答案中编辑,但编辑被拒绝,所以我创build了一个单独的答案。

你做错了什么是使用一个IP地址,而不是一个域名。 创build一个域名,并将其粘贴到DNS服务器(或者仅仅在hosts文件中),创build一个域名为公用名的自签名证书,并连接到域名而不是IP地址。

Interesting Posts