无法在Node.js中使用IP作为自签名证书

时间:2015-11-06 01:42:41

标签: javascript node.js https openssl self-signed

我正在尝试在Node.js中使用HTTP自签名证书。

我使用我编写的以下脚本生成了证书:

generate.sh

#!/bin/bash
read -p "FQDN: " FQDN

# for easy testing
rm ROOT.*
rm SERVER.*

openssl genrsa 4096 > ROOT.key
openssl req -x509 -nodes -sha256 -new -key ROOT.key -days 365 -subj "/C=AU/CN=example" > ROOT.crt

openssl req -newkey rsa:4096 -nodes -sha256 -keyout SERVER.key -subj "/C=AU/CN=${FQDN}" > SERVER.csr
openssl x509 -days 365 -req -in SERVER.csr -CA ROOT.crt -CAkey ROOT.key -CAcreateserial > SERVER.crt

我正在尝试使用以下方法测试证书:

test.js

let fs = require('fs')
let https = require('https')

// HTTP server
https.createServer({
  key: fs.readFileSync('SERVER.key'),
  cert: fs.readFileSync('SERVER.crt'),
  ca: fs.readFileSync('ROOT.crt')
}, function (req, res) {
  res.writeHead(200)
  res.end('Hello world')
}).listen(4433)

// HTTP request
let req = https.request({
  hostname: '127.0.0.1',
  port: 4433,
  path: '/',
  method: 'GET',
  ca: fs.readFileSync('ROOT.crt')
}, function (res) {
  res.on('data', function (data) {
    console.log(data.toString())
  })
})
req.end()

然而,经过测试:

> node test.js
Error: Hostname/IP doesn't match certificate's altnames: "IP: 127.0.0.1 is not in the cert's list: "

这看起来很奇怪,因为如果我打印证书证书列表,它会显示IP在哪里?

如果我使用localhost作为FQDN和host(在请求中),它确实有效。

我错过了什么?

修改

curl --cacert ROOT.crt https://127.0.0.1:4433完成没有错误,那么我在Node.js代码中缺少什么?

2 个答案:

答案 0 :(得分:3)

感谢Kris Reeves指出我正确的方向。

问题如上所述,证书缺少0x40 s,并且以兼容的格式获取它们并非如此简单(必须与X509v3兼容)。

最终subjectAltName最终结束:

Makefile

希望这可以帮助其他想要使用仅具有IP的自签名证书的人。

.PHONY: clean default FQDN ?= 127.0.0.1 default: SERVER.crt clean: rm -f openssl.conf rm -f ROOT.* rm -f SERVER.* openssl.conf: cat /etc/ssl/openssl.cnf > openssl.conf echo "[ san_env ]" >> openssl.conf echo "subjectAltName=$$""{ENV::SAN}" >> openssl.conf ROOT.key: openssl genrsa 4096 > ROOT.key ROOT.crt: ROOT.key openssl req \ -new \ -x509 \ -nodes \ -sha256 \ -key ROOT.key \ -days 365 \ -subj "/C=AU/CN=example" \ -out ROOT.crt SERVER.csr: openssl.conf SAN=IP:$(FQDN) openssl req \ -reqexts san_env \ -config openssl.conf \ -newkey rsa:4096 \ -nodes -sha256 \ -keyout SERVER.key \ -subj "/C=AU/CN=$(FQDN)" \ -out SERVER.csr SERVER.crt: openssl.conf ROOT.key ROOT.crt SERVER.csr SAN=IP:$(FQDN) openssl x509 \ -req \ -extfile openssl.conf \ -extensions san_env \ -days 365 \ -in SERVER.csr \ -CA ROOT.crt \ -CAkey ROOT.key \ -CAcreateserial \ -out SERVER.crt

答案 1 :(得分:1)

我在IRC上与你交谈,但我会留下这些信息,以防其他人帮忙。

通过node's tls模块的源代码,我们可以看到它希望altnames采用非常特定的格式:

https://github.com/nodejs/node/blob/v5.0.0/lib/tls.js#L108-L145

特别是线路: var option = altname.match(/^(DNS|IP Address|URI):(.*)$/);

稍后,它决定要检查的主机所处的格式,如果它是IP地址,它会在从altnames中提取的IP地址中查找它。因此,证书将失败,除非它具有格式为“IP地址:127.0.0.1”的altname,因为这是Node将其置于它检查的IP列表中的唯一方式,当它稍后识别出它正在测试IP地址时

我不知道这是否是证书的altnames字段格式的正典,但它应该为您提供生成Node将接受的证书的路径。

或者,您可以在套接字或http请求的连接选项中提供自己的checkServerIdentity函数(应该传递给套接字):

https://github.com/nodejs/node/blob/82022a79b035c25f8a41df1f2a20793d356c1511/lib/_tls_wrap.js#L962-L969

这是我以前必须这样做的方式,但我相信altname匹配可能比我上次遇到此问题时更新。