Installing SSL cert on Amazon EC2 ELB - ssl

I'm new to Amazon Web Services and have been trying to install a SSL certificate on the EC2 instance. I tried following AWS documentation, but found it baffling. I then followed the guide at http://www.robertbrewitz.com/2014/09/aws-and-setting-up-a-custom-ssl-certificate/.
I bought my SSL cert with Go Daddy, and generated 2 files using openssl:
server.key
server.csr
The guide said I should expect 3 certificates:
DigiCertCA.crt
TrustedRoot.crt
star_yourdomain_com.crt
Instead, I confusingly received 2 files named:
f6f65901b1708ae5.crt
gd_bundle-g2-g1.crt
I assume f6f65901b1708ae5.crt is my domain cert (but I'm not certain). Anyway, the guide says I need an Elastic Load Balancer in order to install an SSL cert so I created one.
I generated the private key with:
openssl rsa -in server.key -text
and the public key certficate with:
openssl x509 -inform PEM -in f6f65901b1708ae5.crt
I was also required to enter a Certificate Chain. I wasn't sure what this was and how to get it, so I guessed the command:
openssl x509 -inform PEM -in gd_bundle-g2-g1.crt
and entered the resulting certificate key that began with “-----BEGIN CERTIFICATE-----”
The guide continues, saying I then need to set up Cloudfront. I installed the aws command line tool and in order to generate the PEMs I ran:
openssl rsa -in server.key -text > aws_private.pem
openssl x509 -inform PEM -in f6f65901b1708ae5.crt > aws_public.pem
openssl x509 -inform PEM -in gd_bundle-g2-g1.crt > aws_public.pem
I uploaded the SSL cert with:
aws iam upload-server-certificate --server-certificate-name mydomain_com \
--certificate-body file://aws_public.pem --private-key file://aws_private.pem \
--certificate-chain file://aws_chain.pem --path /cloudfront/mydomain_com/
This was successful.
I then had to create a Cloudfront distribution, which I did, choosing the SSL cert.
However when I go to my https url (https://www.example.org/), it's not working. http://www.example.org/ however does work.
As there are an extraordinarily large amount of steps just for installing an SSL cert, I suspect I've made a mistake along the way. The problem is, I don't know where. Has anybody any pointers?
Also, are there not any simpler ways for installing an SSL cert? It seems to be ridiculously complex for something so common. I'd be willing to pay an expert to do it for me (I'm a software developer with almost no knowledge of anything SSL related), but its difficult to find anyone for such a task (and there is the problem of having to hand over login details etc). Any help greatly appreciated.
EDIT
The suggestion below is that I should use AWS Certificate Manager. I've had a look and this seems like a far more painless option. However, I did spend 86 euro on an SSL cert from Go Daddy so I'd prefer if that didn't go to waste. Is any of my work salvageable? Are there mean to resell SSL certs?
EDIT
I still haven't found a real solution to this. To clarify, I have a very niche site that will have very few visitors. I have the site on a EC2 instance. I followed the site above which advised using a Load Balancer and Cloudfront in order to encrypt with SSL. However, it's not working and it's probably overkill anyway. Can anyone help me with this? I'd like to use the SSL cert I paid for, but if not, should I use something like Lets Encrypt?

You mentioned the guide saying to expect 3 files, including a "DigiCertCA.crt" file; sounds like it was written with DigiCert as your cert provider in mind, rather than GoDaddy.
Certificates
First, to make sure that that "f6f65901b1708ae5.crt" contains your requested certificate (and lay to rest any doubts there). To do this, you can compare the data (e.g. Common Name (CN), DNS Subject Alternative Names (SANs), etc) in your "server.csr" file (that's the Certificate Signing Request) with what's in that "f6f65901b1708ae5.crt" file:
$ openssl req -noout -text < server.csr
This should display human-readable text about the details for your domain in the CSR file. Compare that with:
$ openssl x509 -noout -text < f6f65901b1708ae5.crt
This should display similar human-readable text, with more details/fields filled in. But they should roughly have what you expect. Note that if you see an error similar to this:
51299:error:0906D06C:PEM routines:PEM_read_bio:no start line:/SourceCache/OpenSSL098/OpenSSL098-52.40.1/src/crypto/pem/pem_lib.c:648:Expecting: TRUSTED CERTIFICATE
Then it suggests that your "f6f65901b1708ae5.crt" file is in DER format, not PEM format. If not, then you already have a PEM file, which is what AWS ELBs expect. If you have a DER formatted cert, it's easy to convert to PEM format, using:
$ openssl x509 -in f6f65901b1708ae5.crt -inform DER -out f6f65901b1708ae5.pem -outform PEM
I just wanting to be thorough by mentioning this part.
Assuming, now, that we know that that "f6f65901b1708ae5.crt" contains the PEM formatted certificate for your domain, we're ready to handle the "certificate chain" part.
I looked at GoDaddy's online certificate repository, to see if the "gd_bundle-g2-g1.crt" file you mentioned was there, and it was. (It's OK for these files to be publicly available, as they contain the public certificates which are meant for anyone/everyone to use.) Looking at that gd_bundle-g2-g1.crt file, I found that it contains multiple certificates. This is important.
See, a "certificate chain" is a list of files which provide a trust path (or "chain"), from that "f6f65901b1708ae5.crt" certificate you have to the trusted root GoDaddy CA certificate. Each certificate has a subject (who it was issued to), and an issuer (who issued it). This means that you can walk "backward", from your certificate to the issuer's certificate, to that certificate's issuer's certificate, etc. This walking backward is the "certificate chain".
The fact that that "gd_bundle-g2-g1.crt" file contains multiple certificates implies that that file contains the certificate chain you need. It also means that you do not want to do this:
$ openssl x509 -inform PEM -in gd_bundle-g2-g1.crt > aws_public.pem
because openssl x509 only reads the first certificate in the specified file, and you need all of them.
Given all of the above, you might need only the following (to make sure that your private key is PEM formatted):
$ openssl rsa -in server.key -text > aws_private.pem
And then, since we'll assume that "f6f65901b1708ae5.crt" is already PEM formatted (and if not, you know how to convert it above), and we know that "gd_bundle-g2-g1.crt" is already PEM formatted, we'll now be ready to upload these to the AWS ELB.
AWS ELB
To upload the certificates and key for use by the ELB, use something like this:
$ aws iam upload-server-certificate \
--server-certificate-name redmatterapp_com2 \
--certificate-body file://f6f65901b1708ae5.crt \
--private-key file://aws_private.pem \
--certificate-chain file://gd_bundle-g2-g1.crt \
--path /cloudfront/redmatterapp_com/
Note that I used a different --server-certificate-name, just to make sure it didn't override/conflict with your existing configuration. As a suggestion, you might include the date of when the cert being uploaded was created (or, better, when it will expire), as part of the name, as a hint to your future self of when that cert was added, e.g. "redmatterapp_com-2016-02-18".
Also note that if you are not using CloudFront, then you should not use the --path option. If you were using CloudFront and then removed it, I'd highly recommend doing the above aws iam upload-server-certificate command again, only with a different --server-certificate-name and no --path option (and removing the previous name). This may mean re-configuring any existing ELB HTTPS listeners to use the new certificate name, but it may be necessary, as that --path affects SSL handling.
Once the above is done, using the e.g. AWS Console, you should be able to configure your AWS ELB, and under the "Listeners" tab, click "Edit". Add a Listener e.g. for "https". When adding any SSL-capable listener, you'll see a "Change" link appear under the "SSL Certificate" column/tab. Click that "Change", and choose the "existing certificate" button. Under the "Certificate Name" dropdown, then, you should see an entry for the --server-certificate-name string/label you used above. Choose that entry, then click "Save". Now connections to that Listener, on your AWS ELB, should be properly configured for SSL/TLS connections that browsers will trust.
Thus the HTTPS Listener configuration would look something like:
Load Balancer Protocol: HTTPS
Load Balancer Port: 443
Instance Protocol: HTTP
Instance Port: 80
Cipher: (default policy)
SSL Certificate: (--server-certificate-name name)
Note that you do not want to use port 443 for the instance port as well; if you do, that says that you want HTTPS from the ELB to your instance, which is not usually needed. (Some secure sites want this, but that's a different topic/store.) Thus the above configures the ELB to handle the SSL termination; to your Node.js server on the instance, it only ever receives plain HTTP requests on port 80:
client ---> HTTP ---> ELB port 80 ---> HTTP ---> server port 80
client ---> HTTPS --> ELB port 443 --> HTTP ---> server port 80
If your server needs to know whether the original request was HTTP or HTTPS, look for the X-Forwarded-For (and other request headers) that the AWS ELB will automatically add to the request.
Now, once you have the ELB configured, it's a good step to verify that it's working properly. First, you can use openssl s_client to verify that the SSL handshake works:
$ openssl s_client -connect example.com:443
CONNECTED(00000003)
depth=3 /C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
verify error:num=19:self signed certificate in certificate chain
verify return:0
---
Certificate chain
0 s:/OU=Domain Control Validated/CN=redmatterapp.com
i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy.com/repository//CN=Go Daddy Secure Certificate Authority - G2
1 s:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy.com/repository//CN=Go Daddy Secure Certificate Authority - G2
i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./CN=Go Daddy Root Certificate Authority - G2
2 s:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./CN=Go Daddy Root Certificate Authority - G2
i:/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
3 s:/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
i:/C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
---
Server certificate
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
subject=/OU=Domain Control Validated/CN=example.com
issuer=/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certs.godaddy.com/repository//CN=Go Daddy Secure Certificate Authority - G2
---
No client certificate CA names sent
---
SSL handshake has read 4929 bytes and written 456 bytes
---
New, TLSv1/SSLv3, Cipher is AES128-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : AES128-SHA
...
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
The above shows that we successfully completed an SSL handshake, and you can see that it shows the cert chain with the GoDaddy names; the "Server certificate" section should match the "f6f65901b1708ae5.crt" PEM file you loaded.
HTTPS and DNS
Now to test the HTTPS part; for this, I like to use curl, like so:
$ curl -kv https://example.com/
The -v option display more information, in particular whether the certificate matches the DNS name or not; the -k tells curl to ignore any certificate mismatches/issues, so that we can then see the verbose information about things.
This is where a little more configuration is necessary. If you use the auto-generated ELB DNS name in your curl command (or in your browser), then you will most likely see security warnings/issues. Why? Because part of HTTPS is verifying that the DNS name you use in your URL matches the domain/host name in your server certificate, either as the Common Name (CN) in your certificate, or as one of the DNS Subject Alternative Names (SANs). And you probably didn't include the ELB DNS name in your Certificate Signing Request (CSR) to GoDaddy.
This means, then, that the next step is to configure a DNS CNAME record that points to the AWS ELB name, e.g.:
www.example.com CNAME to aws-elb-1.elb.amazonaws.com
If you're using AWS for DNS, then you could do this using e.g. Route 53. Then you retry your curl command (or browser):
curl -kv https://www.example.com/
Another important thing to watch for (and you can see this in the curl -v output) is the contents of the Host request header. Some HTTP servers (i.e. the one running on your backend instance) are very picky about the value there; they want the Host header to match something in their configuration, and if it doesn't, they may refuse the request.
Another common gotcha is:
HTTP 503 Service Unavailable: Back-end server is at capacity
If you get this response from your ELB, it usually means that your backend servers are not responding or are failing to respond to the ELB healtcheck. Double-check that the port and path/URL used for the ELB healtcheck are correct, and that any firewalls or AWS Security Groups allow connectivity from the ELB to your instances.
So, now you should have an ELB which forwards port 80 (HTTP) and port 443 (HTTPS) to your backend instances. And you have a CNAME record for "www.example.com" in DNS that points to that ELB name. What's left?
HTTP Redirects
I highly recommend configuring your HTTP server to always redirect to the HTTPS equivalent of the same URL. Why?
For security of the data between your clients and your server, using SSL/TLS is now expected. So much so that more and more browsers automatically try HTTPS first, and only begrudgingly use HTTP as a fallback. Browsers like Chrome (and others) want to avoid this HTTP fallback so much so that they've introduced mechanisms like HTTP Strict Transport Security: a way for your site to tell the browser to only ever use HTTPS for the site, never HTTP. Plus it's always a better marketing story; in fact, you can expect to get negative reactions from your clients/users if you don't use HTTPS.
There's another thing that using HTTPS for all your site's traffic protects you from: someone else using their DNS name and your ELB. You have a CNAME record for "www.example.com" that points to "aws-elb-1.elb.amazonaws.com". But what if I also create my own CNAME for "www.evilco.com", which points to the same "aws-elb-1.elb.amazonaws.com"? Folks going to "www.evilco.com" will see your site, and think it's mine!
By forcing all traffic for your site to be HTTPS, you force all of the HTTPS clients to verify that the certificate presented by the server (i.e. your "f6f65901b1708ae5.crt" file) contains a Common Name (CN) or DNS Subject Alternative Name (SAN) that matches the domain name the client used in its URL. Obviously your certificate does not contain CN or DNS SANs for "www.evilco.com", and thus that verification process would fail -- and the end user would see that something is fishy. But if you allow HTTP traffic as well for your site, this phenomenon could happen -- and you, looking at your site's logs, would never know!
Postscript
I've not used the AWS Certificate Manager, or CloudFront, myself (so I can't comment on them), but I have used the above process, multiple times, for various certificate provisioning of multiple domains, at multiple jobs.
Hope this helps!

Related

Openssl certificate verification stricter/different than browsers? [duplicate]

This question already has answers here:
SSL working in chrome but sometimes in Firefox and not on IOS, Android or Blackberry
(2 answers)
Closed 1 year ago.
I've put together a Linux (Centos 7) server to serve eye-n-sky.net.
Serving content from that site to browsers on Win10 and Linux systems works beautifully. However, when I use openssl to access the site,
openssl s_client -connect eye-n-sky.net:443
the site certificate is rejected,
Verify return code: 21 (unable to verify the first certificate)
I've concluded that the way a browser verifies the certificate is different from what openssl does. Am I on the right track?
I've tested this on three different openssl instances (Debian, Centos, FreeBSD) and have consistent results.
Openssl as a client to other sites, e.g. www.godaddy.com, microsoft.com, work fine, being able to verify the certificate against the installed CA chain.
Believing that I was missing a CA cert, I used the -CAfile option to specify the possibly missing cert, to no effect.
What am I missing? I'm guessing that openssl has a stricter verification discipline, but I don't know where that gets configured.
Thanks,
Andy
Summary: yes, eye-n-sky was providing only it's cert when it needed to include the intermediate and root certs.
However, it took me forever to figure out that my Apache version did not support including the chain in the server cert file. Instead, I had to provide the chain file separately in an SSLCertificateChainFile directive.
OpenSSL's command-line s_client utility has nothing built in to validate the server's certificate. Browsers have a built-in list of trusted certificates to verify the server certificate against.
You have to supply the trusted certificates using options such as -CAfile file or -CApath directory. Per the OpenSSL 1.1.1 s_client man page:
-CApath directory
The directory to use for server certificate verification. This
directory must be in "hash format", see verify(1) for more
information. These are also used when building the client certificate
chain.
-CAfile file
A file containing trusted certificates to use during server
authentication and to use when attempting to build the client
certificate chain.
Note the use of words such as "certificate chain". If you go to godaddy.com you'll see that the server's cert is for *.godaddy.com, but it was signed by Go Daddy Secure Certificate Authority - G2, and that intermediate certificate was signed by Go Daddy Root Certificate Authority - G2 - a different certificate. There's a total of three certificates in that chain.
Verify return code 21 is "no signatures could be verified because the chain contains only one certificate and it is not self signed", so if your CA file only had the certificate from Go Daddy Root Certificate Authority - G2 and not the one from Go Daddy Secure Certificate Authority - G2, OpenSSL would see from the server's cert itself that it was signed by Go Daddy Secure Certificate Authority - G2 and could go no further - it doesn't have that cert to see who signed it.

Root Certificate of website through openssl command

I am trying to obtain the root certificate of various websites for my project, but I am not sure the certificates that I am getting back with this command, contains root certificate or not?
openssl s_client -showcerts -connect google.com:443
I was searching for an answer when I came across a post where wget was used to get the root certificate from the certificate repository of godaddy
wget https://certs.godaddy.com/repository/gd_bundle.crt -O ~/.cert/mail.nixcraft.net/gd.pem
how do i find the repository for every website?
The server must include the certification chain during TLS connection (https). The chain may include the CA root certificate, but it is optional, So you have no guarantee that it will be available. The TLS protocol expects the client to have the certificate in their truststore to verify the trust
You can download the server certificate of every site programmatically, but it is needed to look for the root CA certificate. As you can see, godaddy publish them in its website. In many cases the certificate itself includes a reference to download the root certificate

Securing a private IP address (https certificate)

I have an unusual use case :
a web server on the Internet is serving pages through HTTPS,
inside those web pages, there are calls to XMLHttpRequests to a locally connected device (IP over USB)
the device supports both HTTP and HTTPS,
the device is accessible on http(s)://192.168.0.1
the http calls fail because of insecure content in a https page,
the https calls fail because the certificate is not trusted (self-signed),
Side question: Since the device is locally connected to the PC, the encryption is pretty useless: Does a http header exists that allows insecure connections to a specific URL ? (like CORS for cross domain)
Main question: Is it possible to obtain a certificate for a private IP address ?
Edit: it seems that Plex had a similar problem and solved it the way described on this blog. This is a way too big for me.
Is it possible to obtain a certificate for a private IP address ?
A certificate can be bound to an IP address (see this). You can issue a self-signed certificate to a private address, but a trusted CA will not issue a certificate to a private address because it can not verify its identity.
For example a certificate issued to 192.168.0.1 would be theoretically valid in any context, and this should not be allowed by a trusted CA
Plex solves the problem with a Dynamic DNS and a wildcard certificate. The connection are done using the name (not the IP) of the device which is resolved to the private IP
Does a http header exists that allows insecure connections to a specific URL ? (like CORS for cross domain)
No, it does not exist. The browser blocks your XHR connections because they are HTTP connections initiated from a HTTPS page (mixed-content warning). Non-secure content can theoretically be read or modified by attackers, even though the parent page is served over HTTPs, so is normal and recommended that the browser warns the user.
To fix the mixed-content and https errors, you could serve the content through HTTPS and a self-signed certificate, and request users to import your root CA at browser.
An SSL certificate cannot be issued for Reserved IP addresses (RFC 1918 and RFC 4193 range)/ private IP addresses (IPv4, IPv6), Intranet for Internal Server Name, local server name with a non-public domain name suffix.
You could however use a 'self-signed' certificate. Here's how to create one:
Creating a Self-signed Certificate for a private IP
(example https://192.168.0.1) :
You need OpenSSL installed.
For example, on Ubuntu, you could install it by: sudo apt-get install openssl
(It may already be installed. Type "openssl version" to find out)
For Windows, you could try this: https://slproweb.com/products/Win32OpenSSL.html
Once OpenSSL is installed, go to OpenSSL prompt by entering 'openssl' on the console (LINUX), or the cmd prompt (WINDOWS).
$ openssl
OpenSSL>
Now do the following steps to create: Private key, Certificate Request, Self-signing the certificate, and putting it all together, by using the below commands:
i) Create KEY called mydomain.key:
OpenSSL> genrsa -out mydomain.key 2048
ii) Use the key to create a Certificate request called mydomain.csr
You could accept the default options, or specify your own information:
OpenSSL> req -new -key mydomain.key -out mydomain.csr
iii) use the above to create a certificate:
OpenSSL> x509 -req -days 1825 -in mydomain.csr -signkey mydomain.key -out mydomain.crt
iv) Put all the above to create a PEM certificate:
exit OpenSSL (OpenSSL> q) and go to certificate location and do:
$ sudo cat mydomain.key mydomain.crt >> mylabs.com.pem
mylabs.com.pem is your self-signed certificate. You can use this in requests like https://192.168.0.1 if your server supports https. Remember to check the port number for https(443).

SSL client verification failure

I have a backend server with SSL only as defaut public interface. This server listens on 443 with cert, key and ca files on, ssl verify client is set to true.
On client side, I have client cert, key and a ca file made of 2 CA files catted together (intermediate CA, root CA) in a single ca.pem file. When I do curl request on my backend server, it works fine. I also tested the server / client cert and key with gnutls-serv and openssl s_server to ensure all was valid.
But when I create a keystore on Apigee (client cert and key file [pem format]) and a trustore (ca.pem file), there is an error:
- target.name fsbca-test
- Properties
- Expression ("fsbca-test" equals target.name)
- ExpressionResult true
- Tree TARGET_fsbca-test
- error The Service is temporarily unavailable
- error.cause General SSLEngine problem
- error.cause.cause General SSLEngine problem
- error.class com.apigee.messaging.adaptors.http.HttpAdaptorException
- state TARGET_REQ_FLOW
- type ErrorPoint
If I put SSL verify client to false on my backend, then the request is correctly diverted by Apigee and I get the response.
If I put IgnoreValidationErrors to true in the target endpoints property, then the request is diverted to my backend server but I can see an error in the server's log: "client sent no required SSL certificate while reading c...".
Any ideas about what could be wrong in what I am doing?
Additional track: could it be an issue on Apigee side with the CA file made of 2 certs (it may ignore trailing certificates found in a .pem). If wanted to test pkcs12 and jks but I failed to upload them to Apigee (the API doc page only describes .pem, JAR and cert action). I wrote a small Java client with pkcs12 keystore and jks trustore and it worked fine from my local workstation.
Thank you in advance for any piece of information that could help me.
Regards
Fr
You should upload your certificates separately (one per cert), and you need the entire trust chain of certificates to be stored in your truststore.
Here is the page about SSL to your backend.
EDIT:
Here is a method I know works:
1) Separate certs into separate PEM files. Do not put more than once cert in a file.
2) Validate each cert using openssl:
openssl x509 -noout -text -in <cert file name>
Validate that no certs are expired, and that the Subject and Issuer fields create a chain of all the certs, with identical names.
The server's certificate, the root certificate, and all certificates in between need to be in the truststore.

OpenSSL: unable to verify the first certificate for Experian URL

I am trying to verify an SSL connection to Experian in Ubuntu 10.10 with OpenSSL client.
openssl s_client -CApath /etc/ssl/certs/ -connect dm1.experian.com:443
The problem is that the connection closes with a Verify return code: 21 (unable to verify the first certificate).
I've checked the certificate list, and the Certificate used to sign Experian (VeriSign Class 3 Secure Server CA - G3) is included in the list.
/etc/ssl/certs/ca-certificates.crt
Yet I don't know why it is not able to verify the first certificate.
The entire response could be seen here:
https://gist.github.com/1248790
The first error message is telling you more about the problem:
verify error:num=20:unable to get local issuer certificate
The issuing certificate authority of the end entity server certificate is
VeriSign Class 3 Secure Server CA - G3
Look closely in your CA file - you will not find this certificate since it is an intermediary CA - what you found was a similar-named G3 Public Primary CA of VeriSign.
But why does the other connection succeed, but this one doesn't? The problem is a misconfiguration of the servers (see for yourself using the -debug option). The "good" server sends the entire certificate chain during the handshake, therefore providing you with the necessary intermediate certificates.
But the server that is failing sends you only the end entity certificate, and OpenSSL is not capable of downloading the missing intermediate certificate "on the fly" (which would be possible by interpreting the Authority Information Access extension). Therefore your attempt fails using s_client but it would succeed nevertheless if you browse to the same URL using e.g. FireFox (which does support the "certificate discovery" feature).
Your options to solve the problem are either fixing this on the server side by making the server send the entire chain, too, or by passing the missing intermediate certificate to OpenSSL as a client-side parameter.
Adding additional information to emboss's answer.
To put it simply, there is an incorrect cert in your certificate chain.
For example, your certificate authority will have most likely given you 3 files.
your_domain_name.crt
DigiCertCA.crt # (Or whatever the name of your certificate authority is)
TrustedRoot.crt
You most likely combined all of these files into one bundle.
-----BEGIN CERTIFICATE-----
(Your Primary SSL certificate: your_domain_name.crt)
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
(Your Intermediate certificate: DigiCertCA.crt)
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
(Your Root certificate: TrustedRoot.crt)
-----END CERTIFICATE-----
If you create the bundle, but use an old, or an incorrect version of your Intermediate Cert (DigiCertCA.crt in my example), you will get the exact symptoms you are describing.
SSL connections appear to work from browser
SSL connections fail from other clients
Curl fails with error: "curl: (60) SSL certificate : unable to get local issuer certificate"
openssl s_client -connect gives error "verify error:num=20:unable to get local issuer certificate"
Redownload all certs from your certificate authority and make a fresh bundle.
I came across the same issue installing my signed certificate on an Amazon Elastic Load Balancer instance.
All seemed find via a browser (Chrome) but accessing the site via my java client produced the exception javax.net.ssl.SSLPeerUnverifiedException
What I had not done was provide a "certificate chain" file when installing my certificate on my ELB instance (see https://serverfault.com/questions/419432/install-ssl-on-amazon-elastic-load-balancer-with-godaddy-wildcard-certificate)
We were only sent our signed public key from the signing authority so I had to create my own certificate chain file. Using my browser's certificate viewer panel I exported each certificate in the signing chain. (The order of the certificate chain in important, see https://forums.aws.amazon.com/message.jspa?messageID=222086)
Here is what you can do:-
Exim SSL certificates
By default, the /etc/exim.conf will use the cert/key files:
/etc/exim.cert
/etc/exim.key
so if you're wondering where to set your files, that's where.
They're controlled by the exim.conf's options:
tls_certificate = /etc/exim.cert
tls_privatekey = /etc/exim.key
Intermediate Certificates
If you have a CA Root certificate (ca bundle, chain, etc.) you'll add the contents of your CA into the exim.cert, after your actual certificate.
Probably a good idea to make sure you have a copy of everything elsewhere in case you make an error.
Dovecot and ProFtpd should also read it correctly, so dovecot no longer needs the ssl_ca option.
So for both cases, there is no need to make any changes to either the exim.conf or dovecot.conf(/etc/dovecot/conf/ssl.conf)
If you are using MacOS use:
sudo cp /usr/local/etc/openssl/cert.pem /etc/ssl/certs
after this Trust anchor not found error disappears
For those using zerossl.com certificates, drag and drop all certificates (as is) to their respective folders.
Cut and pasting text into existing files, may cause utf8 issues - depending upon OS, format and character spacings.