NodeJS无法验证叶子证书签名。

27
我们正试图使用Node.js(和Mocha)作为测试框架,对内部服务器上的API调用进行https测试。我们正在使用以下Node模块:Mocha、Restify和Should来执行这些测试。
当我们运行mocha testFileName.js时,我们得到的主要错误是:
[2013-06-19 14:16:28.105] [ERROR] console - FAIL:  Received error!  [Error:        UNABLE_TO_VERIFY_LEAF_SIGNATURE]
Error: UNABLE_TO_VERIFY_LEAF_SIGNATURE
at SecurePair.<anonymous> (tls.js:1283:32)
at SecurePair.EventEmitter.emit (events.js:92:17)
at SecurePair.maybeInitFinished (tls.js:896:10)
at CleartextStream.read [as _read] (tls.js:430:15)
at CleartextStream.Readable.read (_stream_readable.js:320:10)
at EncryptedStream.write [as _write] (tls.js:344:25)
at doWrite (_stream_writable.js:219:10)
at writeOrBuffer (_stream_writable.js:209:5)
at EncryptedStream.Writable.write (_stream_writable.js:180:11)
at write (_stream_readable.js:573:24)
at flow (_stream_readable.js:582:7)
at Socket.pipeOnReadable (_stream_readable.js:614:5)
at Socket.EventEmitter.emit (events.js:92:17)
at emitReadable_ (_stream_readable.js:408:10)
at emitReadable (_stream_readable.js:404:5)
at readableAddChunk (_stream_readable.js:165:9)
at Socket.Readable.push (_stream_readable.js:127:10)
at TCP.onread (net.js:511:21)

经过在谷歌和 stackexchange 上的搜索,我们发现了一个证书问题。因此,我们安装了内部 CA“public”证书,以及我们的应用程序正在使用的特定实例证书(需要多个重定向)。

  /usr/local/etc/openssl/certs, legacy: /System/Library/Keychains/X509Anchors, /Library/Keychains/System.keychain, as well as in Keychain through the gui to our login and System keychains. However, we're still not getting anywhere. 
在将证书安装到这些位置之前,我们无法在命令行上“curl”访问我们的网站而不出现证书错误;但是,现在安装了它们后,我们没有错误,但是Node仍然会崩溃。
我们尝试过多个版本的Node、OpenSSL以及不同的安装方法,包括下载软件包与使用homebrew。
计算机信息:
- Mac OS X 10.8.4(也尝试过10.8.3) - Node v0.8.18(也尝试过:Node v0.10.11、v0.10.12) - OpenSSL v1.0.1e(也尝试过0.9.8)
头脑风暴问题:
Node.js是否使用其自己打包的OpenSSL版本,而非本地安装的版本? 如果是这样,它在哪里查找证书? TLS.js是否告诉Node在其他地方查找证书? 有没有实用的方法来覆盖使用的证书?看起来可能有我们可以使用的选项,就像这样:
var options = {
    ca: fs.readFileSync("[path to our CA cert file]"),
    requestCert: true,
    rejectUnauthorized: true
};

var req = https.request(options, function(res) {
    ...
});
但这会产生相同的错误。
7个回答

21

无法验证叶子证书签名

问题在于您试图从带有糟糕或不受信任的SSL证书的存储库中安装模块。如果您在使用为内部存储库自签名的证书的公司工作,则这并不罕见。

要解决此问题,您可以运行以下命令:

npm config set strict-ssl false

作为最佳实践,明智的做法是在此之后将其设置回true,这样您就不会意外安装一个您实际上不信任的不受信任的模块。


这个对我有用!感谢提供信息。之后我将其设置为true。 - Steve J
他们应该安装连接到的特定仓库的证书,而不是这样做是不安全的。 - ADJenks
尝试在代理后安装Electron。这帮助我使其正常工作。 非常感谢! - Tolbxela

11
为了回答关于使用捆绑版本的OpenSSL的第一个问题:
node在编译期间根据加密api的需求生成静态对象。
例如,加密api需要以下OpenSSL函数调用:
$ nm /usr/local/bin/node |  grep "openssl\|x509\|pkey\|tls"
0000000000953d40 T CMS_RecipientInfo_set0_pkey
000000000095c580 T DSO_METHOD_openssl
00000000008dc790 T ENGINE_get_pkey_asn1_meth
00000000008dc7e0 T ENGINE_get_pkey_asn1_meth_engine
00000000008dc590 T ENGINE_get_pkey_asn1_meth_str
00000000008dc4e0 T ENGINE_get_pkey_asn1_meths
00000000008dcea0 T ENGINE_get_pkey_meth
00000000008dcef0 T ENGINE_get_pkey_meth_engine
00000000008dcdf0 T ENGINE_get_pkey_meths
00000000008dc500 T ENGINE_pkey_asn1_find_str
00000000008dc8d0 T ENGINE_register_all_pkey_asn1_meths
00000000008dcfe0 T ENGINE_register_all_pkey_meths
00000000008dc860 T ENGINE_register_pkey_asn1_meths
00000000008dcf70 T ENGINE_register_pkey_meths
00000000008dc800 T ENGINE_set_default_pkey_asn1_meths
00000000008dcf10 T ENGINE_set_default_pkey_meths
00000000008dc4f0 T ENGINE_set_pkey_asn1_meths
00000000008dce00 T ENGINE_set_pkey_meths
00000000008dc910 T ENGINE_unregister_pkey_asn1_meths
00000000008dd020 T ENGINE_unregister_pkey_meths
00000000008e7a40 T EVP_MD_pkey_type
00000000008eaae0 T EVP_PKEY_CTX_get0_pkey
00000000008fbad0 T PKCS12_certbag2x509
00000000008fba70 T PKCS12_certbag2x509crl
00000000008fbb50 T PKCS12_x5092certbag
00000000008fbb30 T PKCS12_x509crl2certbag
00000000008ba910 T PKCS8_pkey_get0
00000000008ba9d0 T PKCS8_pkey_set0
000000000093d860 T SSL_CTX_set_tlsext_use_srtp
000000000093d840 T SSL_set_tlsext_use_srtp
00000000007bd110 T _ZN2v88internal9Assembler9cvtlsi2sdENS0_11XMMRegisterENS0_8RegisterE
00000000007bd1b0 T _ZN2v88internal9Assembler9cvtlsi2sdENS0_11XMMRegisterERKNS0_7OperandE
00000000007bd070 T _ZN2v88internal9Assembler9cvtlsi2ssENS0_11XMMRegisterENS0_8RegisterE
000000000087d220 t _ZN4node6cryptoL14VerifyCallbackEiP17x509_store_ctx_st
0000000000a821a0 r _ZN4nodeL10tls_nativeE
0000000000eac550 B app_pkey_methods
0000000000b16320 R cmac_pkey_meth
0000000000b183a0 R dh_pkey_meth
000000000093b210 T do_dtls1_write
0000000000962a60 t do_pk8pkey
0000000000962cb0 t do_pk8pkey_fp
00000000008cb340 t dsa_pkey_ctrl
0000000000b18660 R dsa_pkey_meth
0000000000938f40 t dtls1_add_cert_to_buf
0000000000938730 T dtls1_buffer_message
000000000093b9f0 t dtls1_buffer_record
000000000093a7d0 T dtls1_check_timeout_num
000000000093ad90 T dtls1_clear
000000000093ac20 t dtls1_clear_queues
00000000009385f0 T dtls1_clear_record_buffer
000000000093b8b0 t dtls1_copy_record
000000000093abc0 T dtls1_ctrl
000000000093a760 T dtls1_default_timeout
000000000093b610 T dtls1_dispatch_alert
0000000000939b00 T dtls1_do_write
000000000093aae0 T dtls1_double_timeout
000000000096d500 T dtls1_enc
000000000093aee0 T dtls1_free
00000000009384f0 T dtls1_get_ccs_header
000000000093aba0 T dtls1_get_cipher
00000000009398b0 T dtls1_get_message
0000000000939260 t dtls1_get_message_fragment
0000000000938510 T dtls1_get_message_header
0000000000938440 T dtls1_get_queue_priority
000000000093c050 T dtls1_get_record
000000000093a910 T dtls1_get_timeout
000000000093ab20 T dtls1_handle_timeout
00000000009385b0 t dtls1_hm_fragment_free
0000000000938640 t dtls1_hm_fragment_new
000000000093aa10 T dtls1_is_timer_expired
000000000093a770 T dtls1_listen
00000000009384d0 T dtls1_min_mtu
000000000093af70 T dtls1_new
0000000000939010 T dtls1_output_cert_chain
0000000000938e10 t dtls1_preprocess_fragment
000000000093bbf0 t dtls1_process_record
000000000093c480 T dtls1_read_bytes
0000000000938d70 T dtls1_read_failed
0000000000938950 t dtls1_reassemble_fragment
000000000093b160 T dtls1_reset_seq_numbers
000000000093a390 T dtls1_retransmit_buffered_messages
000000000093a0f0 T dtls1_retransmit_message
000000000093b990 t dtls1_retrieve_buffered_record
000000000093a450 T dtls1_send_change_cipher_spec
000000000093a560 T dtls1_send_finished
0000000000938450 T dtls1_set_message_header
00000000009384e0 T dtls1_shutdown
000000000093aa50 T dtls1_start_timer
000000000093a880 T dtls1_stop_timer
0000000000b0bca0 R dtls1_version_str
000000000093b7d0 T dtls1_write_app_data_bytes
000000000093b760 T dtls1_write_bytes
00000000008cd650 t ec_pkey_ctrl
0000000000b19e20 R ec_pkey_meth
00000000008dc700 T engine_pkey_asn1_meths_free
00000000008dce10 T engine_pkey_meths_free
00000000008dc8c0 t engine_unregister_all_pkey_asn1_meths
00000000008dcfd0 t engine_unregister_all_pkey_meths
0000000000962220 T evp_pkey_set_cb_translate
00000000008a9200 t get_optional_pkey_id
00000000008ec1f0 t hmac_pkey_ctrl
0000000000aed360 R hmac_pkey_meth
0000000000e99f40 d openssl_dsa_meth
0000000000e99fa0 d openssl_ecdh_meth
0000000000e99fc0 d openssl_ecdsa_meth
0000000000eaa960 b pkey_asn1_meth_table
00000000008ba990 t pkey_cb
0000000000953b90 t pkey_cmac_cleanup
0000000000953b40 t pkey_cmac_copy
00000000009538f0 t pkey_cmac_ctrl
0000000000953990 t pkey_cmac_ctrl_str
0000000000953b10 t pkey_cmac_init
0000000000953ba0 t pkey_cmac_keygen
000000000095b2f0 t pkey_dh_cleanup
000000000095b370 t pkey_dh_copy
000000000095b020 t pkey_dh_ctrl
000000000095b080 t pkey_dh_ctrl_str
000000000095b120 t pkey_dh_derive
000000000095b310 t pkey_dh_init
000000000095b1a0 t pkey_dh_keygen
000000000095b240 t pkey_dh_paramgen
000000000095b940 t pkey_dsa_cleanup
000000000095b9d0 t pkey_dsa_copy
000000000095b4c0 t pkey_dsa_ctrl
000000000095b400 t pkey_dsa_ctrl_str
000000000095b960 t pkey_dsa_init
000000000095b7d0 t pkey_dsa_keygen
000000000095b870 t pkey_dsa_paramgen
000000000095b740 t pkey_dsa_sign
000000000095b6c0 t pkey_dsa_verify
000000000095ea00 t pkey_ec_cleanup
000000000095ea90 t pkey_ec_copy
000000000095e510 t pkey_ec_ctrl
000000000095e460 t pkey_ec_ctrl_str
000000000095e660 t pkey_ec_derive
000000000095ea40 t pkey_ec_init
000000000095e8b0 t pkey_ec_keygen
000000000095e950 t pkey_ec_paramgen
000000000095e7c0 t pkey_ec_sign
000000000095e740 t pkey_ec_verify
00000000008f84d0 t pkey_get_dsa
00000000008f8400 t pkey_get_eckey
00000000008f85a0 t pkey_get_rsa
00000000008ec610 t pkey_hmac_cleanup
00000000008ec700 t pkey_hmac_copy
00000000008ec340 t pkey_hmac_ctrl
00000000008ec3d0 t pkey_hmac_ctrl_str
00000000008ec670 t pkey_hmac_init
00000000008ec5b0 t pkey_hmac_keygen
0000000000eaa988 b pkey_meth_table
0000000000904ed0 t pkey_rsa_cleanup
0000000000904f20 t pkey_rsa_copy
0000000000904330 t pkey_rsa_ctrl
0000000000904010 t pkey_rsa_ctrl_str
00000000009046f0 t pkey_rsa_decrypt
0000000000904740 t pkey_rsa_encrypt
00000000009049e0 t pkey_rsa_init
0000000000904e00 t pkey_rsa_keygen
0000000000904be0 t pkey_rsa_sign
0000000000904a60 t pkey_rsa_verify
00000000009047e0 t pkey_rsa_verifyrecover
00000000008e9690 t pkey_set_type
0000000000902a70 t rsa_pkey_ctrl
0000000000b04800 R rsa_pkey_meth
00000000008b4220 T ssl_add_clienthello_tlsext
00000000008b3cc0 T ssl_add_serverhello_tlsext
00000000008b29b0 T ssl_check_clienthello_tlsext_early
00000000008b2a70 T ssl_check_clienthello_tlsext_late
00000000008b2770 T ssl_check_serverhello_tlsext
00000000008ac800 T ssl_get_server_send_pkey
00000000008ab300 T ssl_get_sign_pkey
0000000000e99a40 d ssl_mac_pkey_id
00000000008b3260 T ssl_parse_clienthello_tlsext
00000000008b2cc0 T ssl_parse_serverhello_tlsext
00000000008b2b50 T ssl_prepare_clienthello_tlsext
00000000008b2690 T ssl_prepare_serverhello_tlsext
00000000008aee80 t ssl_set_pkey
0000000000e99a30 d ssl_x509_store_ctx_idx.16612
00000000008b1f30 T tls12_get_hash
00000000008b21d0 T tls12_get_req_sig_algs
00000000008b2130 T tls12_get_sigandhash
00000000008b1ef0 T tls12_get_sigid
0000000000e99c00 d tls12_md
0000000000e99bd0 d tls12_sig
0000000000e99c40 d tls12_sigalgs
000000000093e230 t tls1_PRF.clone.0
000000000093d8c0 T tls1_alert_code
00000000008a4c40 T tls1_cbc_remove_padding
000000000093dc20 T tls1_cert_verify_mac
000000000093e7b0 T tls1_change_cipher_state
00000000008b4b80 T tls1_clear
00000000008b1d10 T tls1_default_timeout
00000000008b1d20 T tls1_ec_curve_id2nid
00000000008b1d40 T tls1_ec_nid2curve_id
000000000093dd30 T tls1_enc
000000000093f610 T tls1_export_keying_material
000000000093f330 T tls1_final_finish_mac
00000000008b4ba0 T tls1_free
000000000093f560 T tls1_generate_master_secret
00000000008b1ca0 t tls1_get_client_method
00000000008b4c00 t tls1_get_method
00000000008b50a0 t tls1_get_server_method
000000000093d8e0 T tls1_mac
00000000008a3df0 t tls1_md5_final_raw
00000000008b4bd0 T tls1_new
00000000008b1fb0 T tls1_process_sigalgs
00000000008b24e0 T tls1_process_ticket
000000000093f040 T tls1_setup_key_block
00000000008a3e70 t tls1_sha1_final_raw
00000000008a3f10 t tls1_sha256_final_raw
00000000008a3f50 t tls1_sha512_final_raw
0000000000adeae0 R tls1_version_str
00000000008b2210 t tls_decrypt_ticket
0000000000e9e0c0 d ui_openssl
0000000000b09e00 R v3_pkey_usage_period
00000000008c13d0 t x509_cb
0000000000ea5600 D x509_dir_lookup
0000000000ea5660 D x509_file_lookup
00000000008c0c40 t x509_name_canon
00000000008c11f0 t x509_name_ex_d2i
00000000008c0a10 t x509_name_ex_free
00000000008c1030 t x509_name_ex_i2d
00000000008c0a70 t x509_name_ex_new
00000000008c0950 t x509_name_ex_print
0000000000ae10a0 R x509_name_ff
0000000000914fd0 t x509_object_cmp
0000000000915110 t x509_object_idx_cnt
0000000000921bc0 t x509v3_cache_extensions

当Node编译时,它会创建一个静态对象,其中包含它所需的那些函数。这是来自核心Node.js团队的设计决策,可以实现最小化的膨胀。
如果您希望将Node与OpenSSL的链接行为从静态更改为共享,则可以按以下方式从源代码编译Node.js:
$ curl -k http://nodejs.org/dist/v0.10.26/node-v0.10.26.tar.gz | zcat | tar -xf -; cd node-v0.10.26; ./configure --shared-openssl

为了回答第二个问题:
TLS模块使用由您指定的证书。您可以使用OpenSSL测试服务器及其相应的证书的功能,操作如下:
openssl s_client -connect server:port

这将为您提供有关证书链的详细信息,这些信息在诸如curl等工具中不易获得。

简单的解决方法应该是将此选项设置为false,因为您的测试客户端未配置发送任何客户端证书。

var options = {
    /* ... */
    rejectUnauthorized: false
    /* ... */
}

关于“实际问题”,它源于客户端证书存储、其信任的CA来源(浏览器SSL证书管理器)以及服务器证书和其CA未被客户端信任的组合。

希望这样可以澄清问题。


这个(rejectUnauthorized: false)解决了我的问题,也是我一直在寻找的。谢谢。但我只会在特定情况下这么做。在我这种情况下,我有一个我无法控制且需要爬取一些数据的内部调制解调器。由于某些原因它使用https并带有自签名证书。我可以获取它的证书并安装它们,但为什么要费这个事呢?用例不需要这样做。 - Andrew Philips

4

10
恭喜你!你刚刚禁用了大部分的出站安全功能!不要这样做。 - SLaks
有时候你可能想提供额外的CA证书而不是替换捆绑的证书。可以使用ssl-root-cas 模块来简化操作,正如这个帖子中提到的。信任中间CA证书是正确的解决方案,而不是通过rejectUnauthorizedNODE_TLS_REJECT_UNAUTHORIZED关闭安全检查,从而危及你的安全。 - Ferdinand Prantl

4
有时,如果您使用的网络有一些代理配置,npm无法验证包签名。

尝试以下操作:

npm set strict-ssl false

https://github.com/Leaflet/Leaflet/issues/2860 中获取。
之后,
npm cache clean --force

这会起作用的。我试过了。


2
为了解决这个问题,请安装模块 node_extra_ca_certs_mozilla_bundle,使用以下命令:
npm install --save node_extra_ca_certs_mozilla_bundle

然后在你的代码中使用它:

const fs = require('fs');
const https = require('https');
https.globalAgent.options.ca = yourCertificatePEMcontent + fs.readFileSync('node_modules/node_extra_ca_certs_mozilla_bundle/ca_bundle/ca_intermediate_root_bundle.pem');

这种方法对我很有效。

0

我遇到了与我的https相同的问题。当我连接我的节点客户端时,会发生这些错误。对我而言,解决方法是使用中间证书。

var key_content  =     fs.readFileSync('./resources/certificates/foo/foo.es_private_key.key');
var cert_content =  fs.readFileSync('./resources/certificates/foo/foo.es_ssl_certificate.crt');
var ca_content   =  fs.readFileSync('./resources/certificates/foo/_.foo.es_ssl_certificate_INTERMEDIATE.crt');

var options = {
  key:  key_content,
  cert: cert_content,
  ca: ca_content
};

var server = https.createServer(options, app);

0

你可以尝试在运行命令时添加--insecure选项吗?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接