4

Do you know why using a Let's Encrypt cert for TLS would result in a client failing at the SSL handshake point with error 19 (self signed cert in chain)? I am developing an app in C that uses the Mosquitto libraries to open the connection (and fails at the handshake) but to simplify this question I'm going to demonstrate the problem using the mosquitto_sub command (which we know works).

I am using Centos 6.2 with Let's Encrypt certificates for my website without any issues (because they're free, automated, and open). I now want to use the same certificate issued by Let's Encrypt to secure a TLS connection between my server and any remote client. TLS is being used for connections to the Mosquitto MQTT broker in fact, on port 8881. The mosquitto.conf file on my server contains:

...
user mosquitto
listener 8883 example.com
cafile /etc/mosquitto/certs/chain-ca.pem  # These 3 from Let's Encrypt
certfile /etc/mosquitto/certs/cert.pem
keyfile /etc/mosquitto/certs/privkey.pem
require_certificate false
...

I have several network interfaces on my CentOS server and my domain, "example.com" resolves with the dig command to the IP of interface eth0:1:

[root@spiff mosquitto]# dig example.com

; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.47.rc1.el6_8.2 <<>> example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 804
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 0

;; QUESTION SECTION:
;example.com.             IN      A

;; ANSWER SECTION:
example.com.      300     IN      A       158.234.234.24

;; AUTHORITY SECTION:
example.com.      300     IN      NS      dns0.ns.co.uk.
example.com.      300     IN      NS      dns1.ns.co.uk.

;; Query time: 41 msec
;; SERVER: 151.236.220.5#535(11.226.220.5)
;; WHEN: Sun Oct 30 19:21:07 2016
;; MSG SIZE  rcvd: 101

I believe this is significant because the certificate for 'example.com' that I've created with Let's Encrypt has a CN which has to resolve with DNS to the IP address of the NIC that the Mosquitto listener is bound to, otherwise from the perspective of OpenSSL I'm serving the wrong certificate to any clients.

The server doesn't have any issues on startup.

After a lot of Googling, I understand (maybe wrongly) that the client needs to be handed as a parameter the certificate of the CA (certification authority) that signed the cert for 'example.com' when it makes a connection to the server, because it will check that the CA used to sign example.com's certificate is trusted, and so we use the --cafile [certificate-authority-cert.crt] parameter to pass this. I appreciate that this is specific to TLS connections and that web clients don't use this feature.

When a client on the same Centos 6.2 server (which could be my program but to simplify things is the out-of-the-box mosquitto_sub command) connects with these parameters I see this error:

 mosquitto_sub -h example.com -p 8883 -t test -u mr-user -P P@55W0rD --cafile /etc/pki/tls/certs/lets-encrypt-x3-cross-signed.pem -d
 Error: A TLS error occurred

Because the Mosquitto broker is not very specific about the value of errno on a failure, I try again using the s_client feature of openSSL instead:

[root@spiff certs]# openssl s_client -connect example.com:8883 -CAfile lets-encrypt-x3-cross-signed.pem
 CONNECTED(00000003)
 depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
 verify error:num=19:self signed certificate in certificate chain
 verify return:0
 ---
 Certificate chain
 0 s:/CN=www.example.com
    i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
    1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
    i:/O=Digital Signature Trust Co./CN=DST Root CA X3
    2 s:/O=Digital Signature Trust Co./CN=DST Root CA X3
    i:/O=Digital Signature Trust Co./CN=DST Root CA X3
 ---
 Server certificate
 -----BEGIN CERTIFICATE-----
 MIIFDjCCA/agAwIBAgISA12o6mO9oS364BF5UVgSAD7TMA0GCSqGSIb3DQEBCwUA
 [... lines removed for brevity ...]
 A+q6hf00nJJsEvGmhVzQG5zAn6ojcWgT3EhurPien7Y16+kIS5tdz9xbeCgLTOrJ
 BXA=
 -----END CERTIFICATE-----
subject=/CN=www.example.com
 issuer=/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
 ---
 No client certificate CA names sent
 Server Temp Key: ECDH, prime256v1, 256 bits
 ---
 SSL handshake has read 3999 bytes and written 373 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
 SSL-Session:
   Protocol  : TLSv1.2
   Cipher    : ECDHE-RSA-AES256-GCM-SHA384
   Session-ID:   E8D32590BAB26FF5811D39D775B3F455CAC3E8747866FA251DDA2032FA88E349
   Session-ID-ctx:
   Master-Key:       1B45DF54D11BC44D96AEAC940291B4D3BBAE56D6431E746873DC4F15DC1219F02019F4D903CAA6E8B23AF83CE291F4A6
   Key-Arg   : None
   Krb5 Principal: None
   PSK identity: None
   PSK identity hint: None
   TLS session ticket lifetime hint: 300 (seconds)
   TLS session ticket:
   0000 - d9 fb 28 9f c9 7c ba b3-26 ff dd 75 53 d1 12 65   ..(..|..&..uS..e
   0010 - 91 76 91 2b f8 a2 b4 4b-0a e2 97 eb ce 8e a1 af   .v.+...K........
   [ ... lines ommitted for brevity ... ]
   00a0 - 71 c3 a9 f3 16 c4 04 17-d1 e8 b0 75 e8 80 e9 fb   q..........u....

   Start Time: 1477857075
   Timeout   : 300 (sec)
   Verify return code: 19 (self signed certificate in certificate chain)
   ---

Why does Verify think that I'm using a self-signed certificate? Is there something else that I need to do, for instance does OpenSSL check the certificate store in /etc/pki/tls for a certificate for the root authority that signed Let's Encrypt's certificate and not find it perhaps when you link in the OpenSSL libs? Is it possible that the trust store under /etc/pki doesn't know about the root authority used by Let's Encrypt already? I got lets-encrypt-x3-cross-signed.pem after reading instructions on the Let's Encrypt Chain Of Trust page, is this the wrong file completely?

cagdas
  • 1,634
  • 2
  • 14
  • 27
webbje
  • 71
  • 6
  • Stack Overflow is a site for programming and development questions. This question appears to be off-topic because it is not about programming or development. See [What topics can I ask about here](http://stackoverflow.com/help/on-topic) in the Help Center. Perhaps [Unix & Linux Stack Exchange](http://unix.stackexchange.com/) or [Information Security Stack Exchange](http://security.stackexchange.com/) would be a better place to ask. – jww Oct 31 '16 at 05:55
  • But to answer your question, its due to the cross-signed CA and the LE Root CA being sent in the chain. If you root your trust in the LE Root ***Let's Encrypt Authority X3***, then you should be OK. From the [Chain of Trust](https://letsencrypt.org/certificates/) page, download the ***PEM*** version of *Let’s Encrypt Authority X3*. Then, call `SSL_CTX_load_verify_locations` with the PEM encoded CA certificate. – jww Oct 31 '16 at 05:57
  • Thanks, that's the answer I needed. I found it works if I pass the certificates in the chain of trust to the server in a single file, concatenated together, in PEM format, and including the site cert, the CA cert, and RA cert(s). – webbje Nov 02 '16 at 14:53

0 Answers0