主机名/ IP与证书的altname不匹配

Gol*_*den 19 ssl openssl csr ssl-certificate node.js

我正在尝试使用带有自签名证书的Node.js 0.8.8创建TLS服务器/客户端设置.

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

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

现在,当我尝试连接到此服务器时,我使用以下代码:

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());
});
Run Code Online (Sandbox Code Playgroud)

如果我删除该行

ca: [ fs.readFileSync('server-cert.pem') ]
Run Code Online (Sandbox Code Playgroud)

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

如果我删除

rejectUnauthorized: true,
Run Code Online (Sandbox Code Playgroud)

同样,错误消失了 - 但是connection.authorized等于false有效意味着我的连接没有加密.无论如何,使用getPeerCertificate()我可以访问服务器发送的证书.由于我想强制执行加密连接,我知道我可能不会删除此行.

现在我读到我可以使用该ca属性来指定我希望Node.js信任的任何CA. TLS模块文档意味着它足以将服务器证书添加到ca阵列,然后一切都应该没问题.

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

Hostname/IP doesn't match certificate's altnames
Run Code Online (Sandbox Code Playgroud)

对我而言,这意味着CA现在基本上是可信的,因此现在没问题,但证书是为我使用的主机之外的其他主机制作的.

我使用创建了证书

$ 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
Run Code Online (Sandbox Code Playgroud)

正如文件暗示的那样.在创建CSR时,我会被问到常见问题,例如国家,州,......和公用名(CN).当您在"网上"获知SSL证书时,您不会将您的名称视为CN,而是您要使用的主机名.

这可能是我失败的地方.

我试过了

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

最后两个是我的机器的本地名称和完全限定的本地名称.

知道我在这里做错了什么吗?

Mit*_*tar 16

最近有一个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) {
  //...
});
Run Code Online (Sandbox Code Playgroud)

现在,它将接受任何服务器标识,但仍会加密连接和验证密钥.

请注意,在以前的版本(例如v0.11.14)中,checkServerIdentity是返回boolean指示服务器有效性的.如果存在问题并且存在问题,那么它已经(之前v4.3.1)更改为函数return(没有throw)错误undefined.

  • 只是为了警告人们:`NODE_TLS_REJECT_UNAUTHORIZED`允许您连接到任何人,无论它是否是您想要的服务器.连接是加密的,但它不是*授权*,这通常是非常关键的事情.并且,将其设置为全局变量是一种非常繁重的方法.只需在`tls`连接选项中将`rejectUnauthorized`设置为false即可. (8认同)
  • 设置NODE_TLS_REJECT_UNAUTHORIZED = 0 env var (6认同)

Gol*_*den 9

tls.js第112-141行中,您可以看到,如果在调用时使用的主机名connect是IP地址,则忽略证书的CN,并且仅使用SAN.

由于我的证书不使用SAN,验证失败.


Bru*_*uno 7

如果您使用主机名进行连接,则将根据DNS类型的主题备用名称(如果有)检查主机名,否则将返回主题可分辨名称中的CN.

如果您使用IP地址进行连接,则将根据IP地址类型的SAN检查IP地址,而不会回退到CN.

这至少是符合HTTP over TLS规范(即HTTPS)的实现.有些浏览器更宽容一些.

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

一般来说,除非是测试CA,否则很难管理依赖IP地址的证书.使用主机名连接更好.