如何将自定义证书颁发机构 (CA) 添加到 nodejs
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29283040/
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
How to add custom certificate authority (CA) to nodejs
提问by Mike Haas
I'm using a CLI tool to build hybrid mobile apps which has a cool upload feature so I can test the app on a device without going through the app store (it's ionic-cli). However, in my company like so many other companies TLS requests are re-signed with the company's own custom CA certificate which I have on my machine in the keychain (OS X). However, nodejs does not use the keychain to get its list of CA's to trust. I don't control the ionic-cli app so I can't simply pass in a { ca: } property to the https module. I could also see this being a problem for any node app which I do not control. Is it possible to tell nodejs to trust a CA?
我正在使用 CLI 工具来构建混合移动应用程序,该应用程序具有很酷的上传功能,因此我可以在设备上测试该应用程序,而无需通过应用程序商店(它是 ionic-cli)。但是,在我的公司中,像许多其他公司一样,TLS 请求是使用公司自己的自定义 CA 证书重新签名的,该证书位于我的机器上的钥匙串 (OS X) 中。但是,nodejs 不使用钥匙串来获取其要信任的 CA 列表。我不控制 ionic-cli 应用程序,所以我不能简单地将 { ca: } 属性传递给 https 模块。我也可以看到这是我无法控制的任何节点应用程序的问题。是否可以告诉 nodejs 信任 CA?
I wasn't sure if this belonged in Information Security or any of the other exchanges...
我不确定这是否属于信息安全或任何其他交易所......
回答by Cheng-Lin Tsao
Node.js 7.3.0 (and the LTS versions 6.10.0 and 4.8.0) added NODE_EXTRA_CA_CERTSenvironment variable for you to pass the CA certificate file. It will be safer than disabling certificate verification using NODE_TLS_REJECT_UNAUTHORIZED.
Node.js 7.3.0(以及 LTS 版本 6.10.0 和 4.8.0)添加了NODE_EXTRA_CA_CERTS环境变量供您传递 CA 证书文件。这比使用 禁用证书验证更安全NODE_TLS_REJECT_UNAUTHORIZED。
$ export NODE_EXTRA_CA_CERTS=[your CA certificate file path]
回答by fujifish
I'm aware of two npm modules that handle this problem when you control the app:
我知道有两个 npm 模块可以在您控制应用程序时处理此问题:
- https://github.com/capriza/syswide-cas(I'm the author of this one)
- https://github.com/coolaj86/node-ssl-root-cas
node-ssl-root-casbundles it's own copies of nodes root CAs and also enables adding your own CAs to trust. It places the certs on the https global agent, so it will only be used for https module, not pure tls connections. Also, you will need extra steps if you use a custom Agent instead of the global agent.
node-ssl-root-cas捆绑它自己的节点根 CA 副本,还可以添加您自己的 CA 以信任。它将证书放在 https 全局代理上,因此它将仅用于 https 模块,而不是纯 tls 连接。此外,如果您使用自定义代理而不是全局代理,您将需要额外的步骤。
syswide-casloads certificates from pre-defined directories (such as /etc/ssl/certs) and uses node internal API to add them to the trusted list of CAs in conjunction to the bundled root CAs. There is no need to use the caoption since it makes the change globally which affects all later TLS calls automatically.
It's also possible to add CAs from other directories/files if needed.
It was verified to work with node 0.10, node 5 and node 6.
syswide-cas从预定义的目录(例如 /etc/ssl/certs)加载证书,并使用节点内部 API 将它们添加到 CA 的可信列表中,并与捆绑的根 CA 一起使用。不需要使用该ca选项,因为它会在全局范围内进行更改,这会自动影响所有以后的 TLS 调用。如果需要,也可以从其他目录/文件添加 CA。经验证可与节点 0.10、节点 5 和节点 6 一起使用。
Since you do not control the app you can create a wrapper script to enable syswide-cas(or node-ssl-root-cas) and then require the ionic-cli script:
由于您不控制应用程序,您可以创建一个包装脚本来启用syswide-cas(或node-ssl-root-cas),然后需要 ionic-cli 脚本:
require('syswide-cas'); // this adds your custom CAs in addition to bundled CAs
require('./path/to/real/script'); // this runs the actual script
回答by chrishiestand
This is not currently possible unless you compile a custom version of nodejs with custom CA certs. Hard-baked CA certs is a current limitation of nodejs until someone submits a PR and it's merged. It's a problem for others as well.
除非您使用自定义 CA 证书编译自定义版本的 nodejs,否则目前这是不可能的。在有人提交 PR 并将其合并之前,严格的 CA 证书是 nodejs 的当前限制。这对其他人来说也是一个问题。
Below I have some copies of workarounds which might help some people but probably not the OP.
下面我有一些变通方法的副本,它们可能对某些人有帮助,但可能对 OP 没有帮助。
As far as I know OP can:
据我所知 OP 可以:
- Custom compile nodejs
- submit a PR for nodejs to fix the issue
- file an issue or PR with ionic-cli to support custom CA certs: https://github.com/driftyco/ionic-cli(as suggested by @Nate)
- Force less security (no TLS or silence verification also suggested by @Nate)
- 自定义编译nodejs
- 为 nodejs 提交 PR 以解决问题
- 使用 ionic-cli 提交问题或 PR 以支持自定义 CA 证书:https: //github.com/driftyco/ionic-cli(如@Nate 所建议)
- 强制降低安全性(@Nate 也建议不使用 TLS 或静音验证)
Others, if you control the nodejs app in question you have more options. You can of course specify the ca cert in each request. Some clever people have shared some workarounds in the github issue https://github.com/nodejs/node/issues/4175. I haven't tried any of these myself yet so no promises, I'm just sharing what I've read.
其他人,如果您控制有问题的 nodejs 应用程序,您将有更多选择。您当然可以在每个请求中指定 ca 证书。一些聪明的人在 github 问题https://github.com/nodejs/node/issues/4175 中分享了一些解决方法。我自己还没有尝试过任何这些,所以没有承诺,我只是分享我读过的东西。
DuBistKomisch explains how to get nodejs to use the operating system's CA certs:
DuBistKomisch 解释了如何让 nodejs 使用操作系统的 CA 证书:
My workaround is to load and parse the system CA certs manually. Then, as recommended by the request docs, pass them in with the ca option everywhere we make a request. I presume you could also just set ca on the global agent if that works for your use case.
fs.readFileSync('/etc/ssl/certs/ca-certificates.crt') .toString() .split(/-----END CERTIFICATE-----\n?/) // may include an extra empty string at the end .filter(function (cert) { return cert !== ''; }) // effectively split after delimiter by adding it back .map(function (cert) { return cert + '-----END CERTIFICATE-----\n'; })
我的解决方法是手动加载和解析系统 CA 证书。然后,按照请求文档的建议,在我们发出请求的任何地方使用 ca 选项传递它们。如果这适用于您的用例,我认为您也可以在全局代理上设置 ca 。
fs.readFileSync('/etc/ssl/certs/ca-certificates.crt') .toString() .split(/-----END CERTIFICATE-----\n?/) // may include an extra empty string at the end .filter(function (cert) { return cert !== ''; }) // effectively split after delimiter by adding it back .map(function (cert) { return cert + '-----END CERTIFICATE-----\n'; })
mwain explains how to set the CA certs globally and not on each https request:
mwain 解释了如何全局设置 CA 证书,而不是针对每个 https 请求:
Had similar issues with this, have internal apps using an internally signed cert. Opted to use https.globalAgent and set an array of CA's which are defined in a config and updated on an env basis.
const trustedCa = [ '/etc/pki/tls/certs/ca-bundle.crt', '/path/to/custom/cert.crt' ]; https.globalAgent.options.ca = []; for (const ca of trustedCa) { https.globalAgent.options.ca.push(fs.readFileSync(ca)); }
有类似的问题,有使用内部签名证书的内部应用程序。选择使用 https.globalAgent 并设置一组 CA,这些 CA 在配置中定义并在 env 基础上更新。
const trustedCa = [ '/etc/pki/tls/certs/ca-bundle.crt', '/path/to/custom/cert.crt' ]; https.globalAgent.options.ca = []; for (const ca of trustedCa) { https.globalAgent.options.ca.push(fs.readFileSync(ca)); }
回答by Cameron Tacklind
There is an undocumented, seemingly stable, API for appendinga certificate to the default list:
有一个未记录的、看似稳定的 API 用于将证书附加到默认列表:
const tls = require('tls');
const secureContext = tls.createSecureContext();
// https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt
secureContext.context.addCACert(`-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
-----END CERTIFICATE-----`);
const sock = tls.connect(443, 'host', { secureContext });
For more information, checkout the open issue on the subject: https://github.com/nodejs/node/issues/27079
有关更多信息,请查看该主题的未解决问题:https: //github.com/nodejs/node/issues/27079
回答by Nehal J Wani
This answer is more focused towards package maintainers/builders.
这个答案更侧重于包维护者/构建者。
One can use this method if you do notwant end users to rely on additional environment variables.
如果您不希望最终用户依赖其他环境变量,则可以使用此方法。
When nodejs is built from source, it (by default, can be overridden) embeds the Mozilla CA certificate database into the binary itself. One can add more certificates to this database using the following commands:
当 nodejs 从源代码构建时,它(默认情况下,可以被覆盖)将 Mozilla CA 证书数据库嵌入到二进制文件本身中。可以使用以下命令向该数据库添加更多证书:
# Convert your PEM certificate to DER
openssl x509 -in /path/to/your/CA.pem -outform der -out CA.der
# Add converted certificate to certdata
nss-addbuiltin -n "MyCompany-CA" -t "CT,C,C" < CA.der >> tools/certdata.txt
# Regenerate src/node_root_certs.h header file
perl tools/mk-ca-bundle.pl
# Finally, compile
make install

