1

I've been stuck on a problem for 2 days regarding certificates where some clients connect and some other fails the certificate validation.

I'm working on CentOS 7.3 and Nginx 1.15.4. I have 3 levels of certificates that I create:

  • Root CA, ECDSA on secp384r1 + sha256
    • Intermediate CA, RSA 2048 + sha256
      • Server certificate, RSA 2048 + sha256

The Root CA has been trusted on the system, the intermediate CA and the server certificates are bundled in one file and Nginx configured to used them:

ssl_certificate /etc/nginx/ssl_certs/server-chain.crt;
ssl_certificate_key /etc/nginx/ssl_certs/server.key;
ssl_protocols TLSv1.2 ;
ssl_ciphers EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:!SSLv3;
ssl_prefer_server_ciphers on;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;

Now, Nginx is happy to start with this configuration and I can troubleshoot with openssl:

$ openssl s_client -connect www.example.com:39200 < /dev/null 2>&1
CONNECTED(00000003)
depth=2 C = GB, ST = London, L = London, O = Example Ltd, OU = Example Hosting Team, CN = Example Root Certificate Authority, emailAddress = info@Example.com
verify return:1
depth=1 C = GB, ST = London, L = London, O = Example Ltd, OU = Example Hosting Team, CN = Example Intermediate CA for project example, emailAddress = info@Example.com
verify return:1
depth=0 C = GB, ST = London, L = London, O = Example Ltd, OU = Example Hosting Team, CN = www.example.com, emailAddress = info@Example.com
verify return:1
---
Certificate chain
 0 s:/C=GB/ST=London/L=London/O=Example Ltd/OU=Example Hosting Team/CN=www.example.com/emailAddress=info@Example.com
   i:/C=GB/ST=London/L=London/O=Example Ltd/OU=Example Hosting Team/CN=Example Intermediate CA for project example/emailAddress=info@Example.com
 1 s:/C=GB/ST=London/L=London/O=Example Ltd/OU=Example Hosting Team/CN=Example Intermediate CA for project example/emailAddress=info@Example.com
   i:/C=GB/ST=London/L=London/O=Example Ltd/OU=Example Hosting Team/CN=Example Root Certificate Authority/emailAddress=info@Example.com
---
Server certificate
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
subject=/C=GB/ST=London/L=London/O=Example Ltd/OU=Example Hosting Team/CN=www.example.com/emailAddress=info@Example.com
issuer=/C=GB/ST=London/L=London/O=Example Ltd/OU=Example Hosting Team/CN=Example Intermediate CA for project example/emailAddress=info@Example.com
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 2630 bytes and written 415 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: EEE677FB7C38DF8AEBEF68D22E67499D8D05710D819D5AB92BEF1B0A009DD818
    Session-ID-ctx:
    Master-Key: E423843C77F304B5D1D384DEA115AA2320128BCC824EC2B1C2F02D63A4F7B1671602F96C50DFFB9B7674C25466CF6162
    Key-Arg   : None
    Krb5 Principal: None
    PSK identity: None
    PSK identity hint: None
    Start Time: 1538060858
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---
DONE

Note the Verify return code: 0 (ok) and the fact that openssl is using the system trust store to validate my chain.

I tested the certificates also in Microsoft Edge and it works, no problems with the certificate.

But other clients complain heavily like curl and Firefox and I can't make it work. Here is curl:

curl -v --cacert Example_root_ca.crt https://example-elastic-data-vm.Example.com:39200
* About to connect() to example-elastic-data-vm.Example.com port 39200 (#0)
*   Trying 192.168.0.123...
* Connected to example-elastic-data-vm.Example.com (192.168.0.123) port 39200 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: Example_root_ca.crt
  CApath: none
* Server certificate:
*       subject: E=info@Example.com,CN=example-elastic-data-vm.Example.com,OU=Example Hosting Team,O=Example Ltd,L=London,ST=London,C=GB
*       start date: Sep 27 14:53:14 2018 GMT
*       expire date: Sep 26 14:53:14 2020 GMT
*       common name: example-elastic-data-vm.Example.com
*       issuer: E=info@Example.com,CN=Example Intermediate CA for project example,OU=Example Hosting Team,O=Example Ltd,L=London,ST=London,C=GB
* NSS error -8182 (SEC_ERROR_BAD_SIGNATURE)
* Peer's certificate has an invalid signature.
* Closing connection 0
curl: (60) Peer's certificate has an invalid signature.
...

while Firefox entertains me with this error:

An error occurred during a connection to example-elastic-data-vm.example.com:39200. security library: improperly formatted DER-encoded message. Error code: SEC_ERROR_BAD_DER 

To me this looks like a compatibility problems. I did a lot of research and tests:

  • MS Windows happily verifies the whole chain
  • OpenSSL happily verifies the whole chain
  • Tested using all RSA certificates: works! But in my company we prefer ECDSA
  • Tested on different browsers/clients: as already mentioned it works with openssl and Edge, it doesn't with curl and Firefox
  • Researched compatibility guidelines and bugs
  • Tested different curves like secp256r1, secp384r1 and prime256v1 but no chance
  • Tried different signature algorithms, key usage, extended key usage, basic constraings on the certificate. Same results.
  • Explicitly pointing to the CA when using curl

I don't know where to hit my head anymore, anyone that can point me in the right direction?

UPDATE: I created test sanitized certificates so that I can upload them here:

NOTE: The output of the commands have been sanitized with "Example", so some names might be off

  • Just thought: do all the certificates in the chain need to be of the same type? – ColOfAbRiX Sep 27 '18 at 18:49
  • I wonder if it could be your root self-signature; I don't know if whichever NSS you have checks the root, but CentOS7 has OpenSSL1.0.2 which from commandline definitely does not. Can you post a hexdump of your root or at least its signature value (whose offset can be located with openssl asn1parse)? PS: secp256r1 and prime256v1 are (different names for) the same curve (and so is P-256). added PPS: no, certs definitely don't have to be the same type, especially in TLS1.2 (before that it was ambiguous in the RFC, but everyone did support it). – dave_thompson_085 Sep 27 '18 at 19:18
  • @dave_thompson_085 I'm not sure I can find the signature in the output. Anyway I created some more sample certificates and I linked them in the question. Feel free to have a look – ColOfAbRiX Sep 28 '18 at 13:11
  • 1
    The signature is the content of the BIT STRING which is the last component of the outer SEQUENCE. That website agressively decodes it, making it hard to see the encoding, but I checked from the base64's and they are valid. But I do notice your root cert contains the EC 'curve' (parameters) in explicit form; although this form was standardized by X9/SECG and adopted by rfc3279 2002, as of rfc5480 2009 it is no longer allowed for PKIX (Internet) certs. OpenSSL does not enforce this restriction (because it is not limited to Internet) but NSS might. Try using named form, which is better anyway. – dave_thompson_085 Sep 29 '18 at 4:14
  • Apparently the explicit parameters was the issue. In fact it makes sense that it's not good to have explicit parameters and a named curve together. I didn't know all the ins and outs of these parameters and I didn't make the connection. If you transform your comment in a full and complete answer I'll choose it. – ColOfAbRiX Oct 1 '18 at 9:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.