【问题标题】:How to add custom certificate authority (CA) to nodejs如何将自定义证书颁发机构 (CA) 添加到 nodejs
【发布时间】:2015-05-30 17:45:12
【问题描述】:

我正在使用 CLI 工具构建具有很酷的上传功能的混合移动应用程序,因此我可以在设备上测试应用程序而无需通过应用程序商店(它是 ionic-cli)。但是,在我的公司中,与许多其他公司一样,TLS 请求是使用公司自己的自定义 CA 证书重新签名的,该证书在我的机器上的钥匙串 (OS X) 中。但是,nodejs 不使用钥匙串来获取其 CA 列表以信任。我不控制 ionic-cli 应用程序,所以我不能简单地将 { ca: } 属性传递给 https 模块。对于我无法控制的任何节点应用程序,我也可以看到这是一个问题。是否可以告诉 nodejs 信任 CA?

我不确定这是否属于信息安全或任何其他交易所...

【问题讨论】:

    标签: node.js npm


    【解决方案1】:

    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]
    

    【讨论】:

    • cafile configuration property 的工作方式类似:npm config set cafile [your CA certificate file path]
    • NODE_EXTRA_CA_CERTScafile 配置属性的主要区别在于前者添加一个证书,而cafile 配置属性替换 i> 证书。对于那些只想在链中添加公司证书的人,NODE_EXTRA_CA_CERTS 是更简单的选择。
    • ca 证书是否意味着 .pem/.cer 文件位置?
    • @ChrisDaMour 它可以与 sudo 一起使用,您只需使用 visudo 编辑 sudoers 文件并允许 NODE_EXTRA_CA_CERTS 环境变量跨 sudo 边界工作。
    • 当应用程序在线时,这可以用于以编程方式添加证书吗?还是仅在使用一些预先提供的证书的初始启动期间才有效?
    【解决方案2】:

    我知道有两个 npm 模块可以在您控制应用时处理此问题:

    1. https://github.com/capriza/syswide-cas(我是这个作者)
    2. https://github.com/coolaj86/node-ssl-root-cas

    node-ssl-root-cas 捆绑了它自己的节点根 CA 副本,并且还允许添加您自己的 CA 以进行信任。它将证书放在 https 全局代理上,因此它将仅用于 https 模块,而不是纯 tls 连接。此外,如果您使用自定义代理而不是全局代理,您将需要额外的步骤。

    syswide-cas 从预定义的目录(例如 /etc/ssl/certs)加载证书,并使用节点内部 API 将它们与捆绑的根 CA 一起添加到受信任的 CA 列表中。无需使用 ca 选项,因为它会在全局范围内进行更改,这会自动影响所有以后的 TLS 调用。 如果需要,也可以从其他目录/文件添加 CA。 已验证可与节点 0.10、节点 5 和节点 6 一起使用。

    由于您不控制应用程序,您可以创建一个包装脚本来启用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
    

    【讨论】:

    • 这个方法可以用来monkey patch npm吗?
    • 如果您问是否可以使用 syswide-cas 模块运行“npm”,那么答案是:不能直接运行。但是,您可以创建一个“npm-wrapper”文件来执行 require("syswide-cas") 然后 require("npm") 以编程方式调用 npm。
    • 我尝试了包装器,npm 似乎忽略了全局 http ca 系统证书修复,并且似乎也忽略了 syswide-cas 要求 /shrug
    【解决方案3】:

    除非您使用自定义 CA 证书编译自定义版本的 nodejs,否则目前这是不可能的。在有人提交 PR 并将其合并之前,硬核 CA 证书是 nodejs 的当前限制。对其他人来说也是个问题。

    下面我有一些解决方法的副本,它们可能对某些人有所帮助,但可能对 OP 没有帮助。

    据我所知 OP 可以:

    • 自定义编译nodejs
    • 为 nodejs 提交 PR 以解决问题
    • 向 ionic-cli 提交问题或 PR 以支持自定义 CA 证书:https://github.com/driftyco/ionic-cli(由 @Nate 建议)
    • 强制降低安全性(@Nate 也建议不进行 TLS 或静默验证)

    其他,如果您控制有问题的 nodejs 应用程序,您有更多选择。您当然可以在每个请求中指定 ca 证书。一些聪明的人在 github 问题https://github.com/nodejs/node/issues/4175 中分享了一些解决方法。我自己还没有尝试过这些,所以没有任何承诺,我只是分享我读过的内容。

    DuBistKomisch 解释了如何让 nodejs 使用操作系统的 CA 证书:

    我的解决方法是手动加载和解析系统 CA 证书。然后, 按照请求文档的建议,使用 ca 选项传递它们 我们在任何地方提出请求。我想你也可以设置 ca on 全局代理(如果适用于您的用例)。

    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 解释了如何在全局范围内而不是在每个 https 请求上设置 CA 证书:

    有类似的问题,有内部应用程序使用内部 签署的证书。选择使用 https.globalAgent 并设置 CA 的数组 它们在配置中定义并在环境基础上更新。

    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));
    }
    

    【讨论】:

      【解决方案4】:

      有一个未记录的、看似稳定的 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 });
      

      有关更多信息,请查看有关该主题的未解决问题:https://github.com/nodejs/node/issues/27079

      【讨论】:

      • 有没有办法在全球范围内应用这个?
      • @SeánHayes 我假设您已经在这里看到了引用 NODE_EXTRA_CA_CERTScafile 的其他答案,所以我假设您的意思是您可以在 JavaScript 中做到这一点吗?稍微研究一下,看起来NODE_EXTRA_CA_CERTS 完全在节点的 C++ 源代码中实现,其中的方法不适用于 JavaScript 层。在节点源中搜索UseExtraCaCerts 自己查看。不确定 cafile 方法使用的机制究竟是什么,因此您可能会对此进行调查,但我怀疑它是否存在严重不同。
      【解决方案5】:

      您可以指定命令行选项来告诉节点使用系统 CA 存储:

      node --use-openssl-ca
      

      如果您自己不直接运行节点 CLI,也可以将其指定为环境变量:

      NODE_OPTIONS=--use-openssl-ca
      

      【讨论】:

        【解决方案6】:

        这个答案更侧重于包维护者/构建者。

        如果您希望最终用户依赖额外的环境变量,可以使用此方法。

        当 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
        

        【讨论】:

          猜你喜欢
          • 2019-11-01
          • 1970-01-01
          • 2014-12-26
          • 1970-01-01
          • 2016-08-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多