NodeJS with ECDHE ECC Certificate

824 views
Skip to first unread message

Eugene Williams

unread,
Apr 21, 2016, 4:22:49 PM4/21/16
to nodejs
I've been struggling with this for a few days.

We've obtained an ECC certificate using the following openssl routines:

openssl ecparam -genkey -name secp521r1 | openssl ec -out ec.key
openssl req -new -key ec.key -out ec.csr

Upon receiving the certs, we used the following routine to generate the Diffie Hellman (DH) parameters for the keyfile:

openssl dhparam -rand - 1024 >> ec.key

When complete, we confirmed that the certificate could be used with openssl:

openssl s_server -accept 8443 -cert ssl/ec.pem -key ssl/ec.key -CAfile ssl/ec_chain.pem 

and 

openssl s_client -tls1 -connect hostname:8443 -cipher 'ECDHE-RSA-RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH' -msg

This works flawlessly.

When attempting to use the same certs on the same server with nodejs, there's no joy.


Here's the general config:

var options = {
  cert: fs.readFileSync('ssl/ec.pem'),
  key: fs.readFileSync('ssl/ec.key'),
  ca: fs.readFileSync('ssl/ec_chain.pem'),
  ciphers: 'ECDHE-RSA-RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH',
  ecdhCurve: 'secp521r1',
  honorCipherOrder: true
};


tls.createServer(options, function() {
}).listen(common.PORT, function() {
    console.log('Server started on port: ' + common.PORT);
}).on('clientError', function(err){
    console.log('A failed client connection attempt occurred.');
    console.error(err);
    console.log();
});
This starts a server,

sudo NODE_DEBUG=tls,fs,net,crypto node test-server-2.js
NET: 21217 listen2 0.0.0.0 8443 4 false
NET: 21217 _listen2: create a handle
NET: 21217 bind to 0.0.0.0
Server started on port: 8443

but when trying to connect to this server using openssl, I'm seeing the following in the server logs:
s_client -tls1 -connect hostname:8443 -cipher 'ECDHE-RSA-RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH' -msg

NET: 21217 onconnection
NET: 21217 _read
NET: 21217 Socket._read readStart
TLS: encrypted.read called with 16384 bytes
TLS: encrypted.read succeed with 0 bytes
TLS: onhandshakestart
TLS: encrypted.read called with 16384 bytes
TLS: encrypted.read succeed with 0 bytes
NET: 21217 onread undefined 0 115 115
NET: 21217 got data
NET: 21217 _read
TLS: encrypted.write called with 115 bytes
TLS: cleartext.read called with 16384 bytes
TLS: SecurePair.destroy
TLS: cleartext.destroy
TLS: encrypted.destroy
A failed client connection attempt occurred.
[Error: 140230049531904:error:1408A10B:SSL routines:SSL3_GET_CLIENT_HELLO:wrong version number:s3_srvr.c:993:
]
...

and this in the client:
CONNECTED(00000003)
>>> ??? [length 0005]
    16 03 01 00 a1
>>> TLS 1.0 Handshake [length 00a1], ClientHello
    01 00 00 9d 03 01 59 bd 15 7a 3a 4b fe fc b2 41
    36 9b cd ca 38 7f 5f af de 36 53 1d ec a4 02 d2
    9e a2 8e 6a 10 3f 00 00 42 c0 11 c0 07 c0 0c c0
    02 00 05 c0 14 c0 0a 00 37 00 36 00 86 00 85 c0
    0f c0 05 00 35 00 84 c0 13 c0 09 00 31 00 30 00
    43 00 42 c0 0e c0 04 00 2f 00 41 c0 12 c0 08 00
    10 00 0d c0 0d c0 03 00 0a 00 ff 02 01 00 00 31
    00 0b 00 04 03 00 01 02 00 0a 00 1c 00 1a 00 17
    00 19 00 1c 00 1b 00 18 00 1a 00 16 00 0e 00 0d
    00 0b 00 0c 00 09 00 0a 00 23 00 00 00 0f 00 01
    01
140735256072272:error:1409E0E5:SSL routines:ssl3_write_bytes:ssl handshake failure:s3_pkt.c:656:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 0 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1
    Cipher    : 0000
...

and the connection is never made. 

Can anyone provide guidance with this that will help us in getting it loaded?

Eugene Williams

unread,
Apr 21, 2016, 5:01:41 PM4/21/16
to nodejs
I start the server with the following:
Can anyone provide guidance with this?

johnny somethingerman

unread,
Apr 22, 2016, 12:46:24 AM4/22/16
to nodejs
I'm not an SSL expert, and probably only a few people on this list are, but my hunch is you are doing something wrong with your config and the "honorCipherOrder: true" property of your opetions objet is causing it to show up in node.js when it didn't in openssl s_server.

That hex blob the client dumped probably has the answer in it, but wireshark can make it easy to see what ciphers the client actually presents, and I suspect there is a surprise in there that either doesn't require auth when you use the RSA client.  Because I don't expect that you can do RSA authentication with an EC certificate.

The cipher suite you might want to try is:

ECDHE-ECDSA-RC4-SHA

Cheers,
-johnny

Eugene Williams

unread,
Apr 22, 2016, 7:15:39 PM4/22/16
to nodejs
Thanks for the comment Johnny. It's a good idea to install wireshark. I'll do that and run some tests. Changing the "honorCipherOrder" parameter had no effect.

-Eugene

johnny somethingerman

unread,
Apr 23, 2016, 12:12:01 AM4/23/16
to nodejs
Yes, sorry for my ramblings, it was late and there was information I totally left off that would have made the last part of the comment make actual sense..

The end of my comment is the important part about changing your cipher suite.

You see, you used the option "-cipher 'ECDHE-RSA-RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH" in your openssl s_client command.  Problem is, your certificate is an EC, not RSA, and your cipher suite you're putting up front is ECDHE-RSA-RC4-SHA.  The cipher suite specification for openssl can be unintuitive and I certainly haven't read the docs to know enough to determine precisely what is meant by your argument string. I would expect this string to include everything RSA and everything considered "HIGH" while discounting all aNULL EDH and MD5 suites... but I'm not sure what effect putting ECDHE-RSA-RC4-SHA right up front has.

So my suggestion was to change ECDHE-RSA-RC4-SHA to ECDHE-ECDSA-RC4-SHA, because that's probably what you wanted in the first place.  ECDHE is the key-exchange algorithm, NOT the certificate public key's signing algorithm (for auth).  Your certificate probably has the value  "Elliptic Curve Public Key" in the Algorithm section of your "Public Key Info" section (based on how you created the private key).  The EC key can't do RSA, but it can do ECDSA.  That's why you need a cipher suite with ECDHE-ECDSA-*

Cheers,
-johnny

Eugene Williams

unread,
Apr 25, 2016, 5:27:37 PM4/25/16
to nodejs
Thanks again for the suggestion Johnny.

I updated the server configuration with the new cipher suite. The final setup is:

var ciphersall = 'ECDHE-ECDSA-RC4-SHA:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK';

var options = {
  cert
: [fs.readFileSync('ssl/GEOTRUST_rsa_full.pem'),fs.readFileSync('ssl/COMODO_ecc_full.pem')],
  key
: [fs.readFileSync('ssl/GEOTRUST_rsa.key'),fs.readFileSync('ssl/COMODO_ecc.key')],
  ca
: fs.readFileSync('ssl/COMODO-GEOTRUST_chain.pem')
 
, ciphers: ciphersall
 
, ecdhCurve: 'secp521r1'
 
, honorCipherOrder: true
};



But I'm still getting errors:
  

SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher


Not sure how to handle it from here. I feel as though I've tried everything I know to do. It's as though nodejs doesn't understand how to begin the conversation - initial handshake is fine, beyond that, no luck. I tested again using the same certs with the openssl on the same server, works fine, so I'm trusting nothing is wrong with the actual certs or the keys.


-Eugene

johnny somethingerman

unread,
Apr 26, 2016, 9:32:03 AM4/26/16
to nodejs
Did you also update the -ciphers option in your openssl s_client command?

Ben Noordhuis

unread,
Apr 26, 2016, 9:32:17 AM4/26/16
to nod...@googlegroups.com
On Mon, Apr 25, 2016 at 11:34 AM, Eugene Williams
<eugenew...@gmail.com> wrote:
> Thanks again for the suggestion Johnny.
>
> I updated the server configuration with the new cipher suite. The final
> setup is:
>
> var ciphersall =
> 'ECDHE-ECDSA-RC4-SHA:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK';
>
> var options = {
> cert:
> [fs.readFileSync('ssl/GEOTRUST_rsa_full.pem'),fs.readFileSync('ssl/COMODO_ecc_full.pem')],
> key:
> [fs.readFileSync('ssl/GEOTRUST_rsa.key'),fs.readFileSync('ssl/COMODO_ecc.key')],
> ca: fs.readFileSync('ssl/COMODO-GEOTRUST_chain.pem')
> , ciphers: ciphersall
> , ecdhCurve: 'secp521r1'
> , honorCipherOrder: true
> };
>
>
>
> But I'm still getting errors:
>
>
> SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher
>
>
> Not sure how to handle it from here. I feel as though I've tried everything
> I know to do. It's as though nodejs doesn't understand how to begin the
> conversation - initial handshake is fine, beyond that, no luck. I tested
> again using the same certs with the openssl on the same server, works fine,
> so I'm trusting nothing is wrong with the actual certs or the keys.

I don't believe you've mentioned the version of node.js that you're
using. If it's v0.10 or v0.12, you need to upgrade; the DHE/ECDHE
family of ciphers only work reliably in v4 and newer.

Eugene Williams

unread,
Apr 26, 2016, 4:16:43 PM4/26/16
to nodejs
[SOLVED]
Sometimes the shortest responses are the most profound. Thanks to everyone who contributed.

Ben, you saved my sanity, my hours, in some ways my life.

Although I'd upgraded nodejs to version 5.10 under the current user (AWS ec2-user), this latest version was installed to /usr/local/bin/node. All attempts to run the server were with "sudo" since ec2-user didn't have access to reserve the ports. The root user was still pulling the /usr/bin/node version (which was at 0.10.33).

Once I updated the link using the following:

sudo mv /usr/bin/node /usr/bin/node_0.10.33
sudo ln -s /usr/local/bin/node /usr/bin/node

The server started perfectly, loading the DH params and presenting both the RSA and ECC certs when viewing through Symantec's cert checker (https://cryptoreport.websecurity.symantec.com/checker/views/certCheck.jsp)

Again, thanks to everyone who contributed.

Eugene
Reply all
Reply to author
Forward
0 new messages