24

We're trying to use Node.js (and Mocha) as a testing framework to test API calls against an internal server over https. We're using the following node modules: Mocha, Restify, and Should to perform these tests.

When we run mocha testFileName.js, the major error we get back is:

[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)

After searching google and stackexchange it would seem that we have a certificate problem. From there we installed internal CA 'public' cert, as well as the instance specific certifications that our app is using (there are multiple redirects to get through), to

  /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. 

Before installing the certs in these places, we couldn't 'curl' our site without certificate errors on command-line; however, with them installed now we get no errors, but node still explodes.

We've tried multiple versions of Node, OpenSSL, as well as varying installation methods including downloading the package vs. using homebrew.

Computer Information:

  • Mac OS X 10.8.4 (Also tried with 10.8.3)
  • Node v0.8.18 (Also tried with: Node v0.10.11, v0.10.12)
  • OpenSSL v1.0.1e (Also tried with 0.9.8)

Brainstorming Questions:

Does Node.js use its own (bundled) version of OpenSSL instead of what's installed on the local machine? If that's the case, where is it looking for certificates? Could the TLS.js be telling Node to look elsewhere for certs? Is there a pragmatic approach to overwriting the the certificates used; it appears there might be options we can use like this:

var options = {
    ca: fs.readFileSync("[path to our CA cert file]"),
    requestCert: true,
    rejectUnauthorized: true
};

var req = https.request(options, function(res) {
    ...
});

But this generates our same error.

Morten Siebuhr
  • 5,812
  • 4
  • 27
  • 41
user1984638
  • 241
  • 1
  • 2
  • 3

7 Answers7

16

UNABLE_TO_VERIFY_LEAF_SIGNATURE

The problem is that you are attempting to install a module from a repository with a bad or untrusted SSL[Secure Sockets Layer] certificate. That is not uncommon if you work at a company that self signs its certificates for internal repositories.

To work around this issue, you can run the following command:

npm config set strict-ssl false

As a best practice, it is wise to set it back to true afterwords so you do not accidentally install an untrusted module that you actually do not trust.

Tharif
  • 13,172
  • 9
  • 51
  • 73
11

To answer the first question about using a bundled version of OpenSSL:

node generates static objects during compile based on the needs of the crypto api.

For example the crypto API requires the following OpenSSL function calls:

$ 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

So when node is compiled it creates a static object which includes only those functions which it needs. This is a design decision from the core node.js team which allows for minimal bloat.

If you wish to change the behavior of node's linking to OpenSSL from static to shared you can compile node.js from source like so:

$ 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

To answer the second question:

The TLS module uses the certificates specified by you. You can test the functionality of your server and its corresponding certificates using OpenSSL like so:

openssl s_client -connect server:port

That will provide you with details regarding your certificate chain that is not easily available with tools such as curl etc.

The simple fix should be to set this option to false as your testing client is not configured to send any client certificates.

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

In regards to the 'actual problem' it stems from a combination of the client certificate store, its trusted CA sources (browser SSL certificate manager) and the server certificate and its CA not being trusted by the client.

Hope that clear it up.

jas-
  • 1,724
  • 1
  • 17
  • 28
  • This (`rejectUnauthorized: false`) solved my problem and what I was looking for. Thank you. I'd only do this on a case by case basis. In my case, I have an internal modem I don't control and need to scrape some data. It uses https for some reason and has a self-signed cert. I could grab its credentials and install them, but why bother here. Use case doesn't need it. – Andrew Philips May 13 '20 at 16:58
4

Try something like:

cas = [fs.readFileSync("[path to our CA cert file]"],
https.globalAgent.options.ca = cas;

Taken from http://permalink.gmane.org/gmane.comp.mozilla.identity.devel/3762

It worked for me where all other solutions failed, it seems that this needs to be applied on the global agent.

Mastfish
  • 78
  • 4
  • 9
    @Yadheendran: Congratulations! You just disabled most of your outgoing security! **Don't do that**. – SLaks Dec 19 '13 at 22:57
  • Sometimes you may want to supply additional CAs instead of replacing the bundled ones. There's a [ssl-root-cas](https://github.com/coolaj86/node-ssl-root-cas) module simplifying that, as mentioned in [other post here](http://stackoverflow.com/a/22263280/623816). Trusting intermediary CAs is the correct solution; not compromising your security like disabling the checks by `rejectUnauthorized` or `NODE_TLS_REJECT_UNAUTHORIZED`. – Ferdinand Prantl Aug 31 '14 at 10:11
3

sometimes, if you are using network with some proxy configurations, npm fails to verify package signature.

Try this,

npm set strict-ssl false

Taken from https://github.com/Leaflet/Leaflet/issues/2860

After that,

npm cache clean --force

It will work. I tried this.

Vikas Keskar
  • 935
  • 5
  • 15
2

To resolve the issue, please install the module node_extra_ca_certs_mozilla_bundle using

npm install --save node_extra_ca_certs_mozilla_bundle

And then use it in you code with :

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

This approach works for me.

fcdt
  • 2,151
  • 5
  • 9
  • 24
0

Can you try adding the --insecure option, while running the command?

Soham Navadiya
  • 353
  • 4
  • 15
0

I had got the same problem of rest of mine done by https. When I connect with my node clients I obtain those error. The solution for me is to use intermediate certificate.

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);
dlopezgonzalez
  • 3,987
  • 3
  • 26
  • 40