I am using the command ...
openssl s_client -showcerts -connect reds-cluster-01:443
And I get the output:
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
verify return:1
depth=1 C = US, O = DigiCert Inc, CN = RapidSSL TLS DV RSA Mixed SHA256 2020 CA-1
verify return:1
depth=0 CN = *.my-co-example.com
verify return:1
CONNECTED(00000003)
---
Certificate chain
0 s:/CN=*.my-co-example.com
i:/C=US/O=DigiCert Inc/CN=RapidSSL TLS DV RSA Mixed SHA256 2020 CA-1
-----BEGIN CERTIFICATE-----
MIIGnxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxgkqhkiG9w0BAQsFADBZ
...
I assume that means somewhere on the filesystem of my server there would be a file somewhere that has the string ...
MIIGnxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxgkqhkiG9w0BAQsFADBZ
... in it. How can I find that file without having to execute something like?
sudo grep -sr MIIGnxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxgkqhkiG9w0BAQsFADBZ /
I assume that means somewhere on the filesystem of my server there would be a file somewhere that has the string ...
This assumption is wrong. What you see is part of the server certificate, which need to be checked against a CA certificate located in the local trust store. The server certificate is typically not in the local trust store. See SSL Certificate framework 101: How does the browser actually verify the validity of a given server certificate? for more on certificates are checked.
You can see more with "CURL -W CERTS" from Daniel Stenberg (also on Stack Overflow), which works on curl for the past 25+ years.
When a client connects to a TLS server it gets sent one or more certificates during the handshake.
Those certificates are verified by the client, to make sure that the server is indeed the right one: the server the client expects it to be; no impostor and no man in the middle etc.
When such a server certificate is signed by a Certificate Authority (CA), that CA’s certificate is normally not sent by the server but the client is expected to have it already in its CA store.
(So no file to check on the filesystem)
Ever since the day SSL and TLS first showed up in the 1990s user have occasionally wanted to be able to save the certificates provided by the server in a TLS handshake.
The openssl tool has offered this ability since along time and is actually one of my higher ranked stackoverflow answers.
Now (for curl 7.88.0,to be shipped in February 2023), Daniel proposes:
Using the –write-out (-w) option and the new variables %{certs} and %{num_certs}, curl can now do what you want.
Get the certificates from a server in PEM format:
$ curl https://curl.se -w "%{certs}" -o /dev/null > cacert.pem
$ curl --cacert cacert.pem https://curl.se/
That is easier to parse than the openssl s_client -showcerts -connect current alternative.
Related
One of our Jenkins builds is failing trying to connect to build.shibboleth.net. So I did some analysis.
Please look at these openssl results from 2 regions.
In South Asia region: It gives the cert of test.shibboleth.net, which is wrong.
$ openssl s_client -host build.shibboleth.net -port 443 -prexit -showcerts
CONNECTED(00000005)
depth=1 C = US, ST = Ohio, O = Shibboleth Consortium, CN = Shibboleth Project Intermediate CA
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
0 s:/C=US/ST=Ohio/O=Shibboleth Consortium/CN=test.shibboleth.net
i:/C=US/ST=Ohio/O=Shibboleth Consortium/CN=Shibboleth Project Intermediate CA
...
...
In USA region: It gives the cert of shibboleth.net, which is correct.
$ openssl s_client -host build.shibboleth.net -port 443 -prexit -showcerts
CONNECTED(00000005)
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = shibboleth.net
verify return:1
---
Certificate chain
0 s:CN = shibboleth.net
i:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
...
...
And resolveip build.shibboleth.net gives 3.213.250.186 from both regions.
Why is it giving a wrong certificate for one region? I have no clue how to fix this. Any ideas?
I'd speculate that the results above aren't caused by region dependence but instead that you're using a different version of openssl on the two client machines.
We used to run the different vhosts on different IP addresses. Now, one address is used for everything and we rely on SNI to distinguish requests so that the right certificate is returned.
If the openssl client doesn't present the server name then you might get a default certificate instead of the one you expected. Whether openssl does that or not depends on the version of openssl in use (openssl version 1.1.1 does, earlier versions don't).
You can try adding -servername build.shibboleth.net to your openssl command to see if that changes the behaviour.
I have a certificate chain as:
root CA -> intermediate CA -> org CA -> client Cert
When I verify the client cert with CA as root CA -> intermediate CA -> org CA, it works:
$ cat org_1_ca/ca_crt.pem intermediate_ca/ca_crt.pem root_ca/ca_crt.pem > /tmp/test123.pem
$ openssl verify -CAfile /tmp/test123.pem client/client_crt.pem
client_crt.pem: OK
But when I chained my client cert with org CA (org CA -> client Cert), and have the rest of the chain as CA (root CA -> intermediate CA), it doesn't:
$ cat intermediate_ca/ca_crt.pem root_ca/ca_crt.pem > /tmp/test12.pem
$ openssl verify -CAfile /tmp/test12.pem client/org1_client_crt.pem
client/org1_client_crt.pem: C = US, ST = CA, L = LA, O = PP, OU = TEST, CN = user
error 20 at 0 depth lookup:unable to get local issuer certificate
Is this something that fundamentally wrong or openssl verify doesn't like that? I tried the same thing with nginx and and openssl connect and there was no luck there. Any help is appreciated.
The openssl commandline verify operation reads only one certificate, the first one, from the file given as operand, or from each file if more than one is given. This differs from the files specified with the -CAfile -trusted -untrusted options which can (and typically do) contain multiple certs.
Your file client/org1_client_crt.pem presumably contains the client cert and the 'org CA' cert, in that order. Only the client cert is used, the 'org CA' cert is ignored, and as a result you do not have a valid chain to verify.
If you want to use commandline to mimic/test the validation that a receiver (for a client cert, the server) would do, supply the leaf cert as the operand and all other transmitted (chain) certs with -untrusted, and the anchor(s) plus any 'known' intermediates in the truststore either explicit or defaulted.
There is no openssl connect operation; I assume you mean openssl s_client with options including -connect since that's one place it would make sense to use a client cert chain. The -cert option to s_client similarly uses only the first cert in the file. There is no option on commandline to specify the client chain except in the most recent version, 1.1.0, and even there it isn't documented so you have to read the help message carefully or the code, although the API/library has long supported this for code you write yourself.
Through 1.0.2 if you want to send a client cert with full chain to the server (as you should per the RFCs), assuming the server requests client authentication which is not usual and not the default for nginx (among others), you have to use a trick: supply all the certs needed for the client chain in the truststore, in addition to the anchor(s) needed to verify the server, either using -CAfile and/or -CApath explicitly, or using (modifying if needed) the default truststore unless your openssl is an older non-RedHat version where the default truststore didn't work in s_client s_server s_time only.
And the same is true about the server cert/chain in s_server except that it is used almost always instead of very rarely.
I have an application running on Centos7 that needs to connect to a remote host over HTTPS. However, it is unable to verify the certificate and fails. Also, if I try to download a file from the server using wget, I get the below error:
[root#foo:~]# wget https://10.65.127.9/index.html
--2017-05-22 09:03:01-- https://10.65.127.9/index.html
Connecting to 10.65.127.9:443... connected.
ERROR: cannot verify 10.65.127.9's certificate, issued by ‘/CN=us6877vnxe7827’:
Unable to locally verify the issuer's authority.
To connect to 10.65.127.9 insecurely, use `--no-check-certificate'.
So I get the certificate from the host:
openssl s_client -connect 10.65.127.9:443 <<<'' | openssl x509 -out /etc/pki/ca-trust/source/anchors/mycert.pem
And execute the following to process it:
update-ca-trust extract
This however results in the same issue.. If I run:
openssl s_client -connect 10.65.127.9:443 -showcerts -debug
I do get some errors and various messages:
depth=0 CN = us6877vnxe7827
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = us6877vnxe7827
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
0 s:/CN=us6877vnxe7827
i:/CN=us6877vnxe7827
Server certificate
subject=/CN=us6877vnxe7827
issuer=/CN=us6877vnxe7827
---
No client certificate CA names sent
---
Verify return code: 21 (unable to verify the first certificate)
Any ideas what I may be missing? If any further info helps, please let me know.
For wget you need to provide the certificate authority (CA) certificate(s) that signed the https server certificate. If you have those CA certificates - add them under --ca-certificate=file or --ca-directory=directory options
If you don't have them and you want to skip https server certificate verification (unsecure and can be dangerous) then use --no-check-certificate option.
I had the same problem with Jenkins trying to connect to our GitLab server.
The server does have a valid official certificate in our case, but Java didn't except it.
You are right about downloading the certificate.
However, the application you are mentioning is probably running inside a Java Virtual Machine (as a lot of applications are).
So from the point that you downloaded the certificate to a PEM file, you may have to add it to the VM's trusted certificates instead.
This article describes how to do that. Hope it helps.
I want to connect to ssl://iot.eclipse.org:8883 using Client certficate authentication.
How I can obtain CA certificate?
Do I require to generate my own client certificate with provided CA certificate.
Or client certificate is also bundled along with CA certificate.
Using openssl to check it appears that the certificate for iot.eclipse.org is from the Let's Encrypt project.
$ openssl s_client -showcerts -connect iot.eclipse.org:8883CONNECTED(00000003)
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
0 s:/CN=iot.eclipse.org
i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
-----BEGIN CERTIFICATE-----
So you should be able to check this certificate is valid with the standard CA set in most modern OS/Applications.
You will not be able to get hold of the CA cert/private key to generate your own client certs for obvious reasons and they do not issue client certificates ( and that is assuming that the eclipse.org broker is set up to authenticate clients with the same CA, it doesn't have to).
Also it doesn't make sense to authenticate against this broker as you have no way to set an ACL to control which users can subscribe/publish to specific topics as it's a public demonstration broker. If you want to secure access then you will have to run your own public broker
EDIT: You don't want to do client certificate authentication, you just want to verify the server cert, this is very different.
To do this with mosquitto_pub or mosquitto_sub you have to specify a CA cert or a path to a directory of multiple certificates in order to enable ssl/tls for the connection. You would specify an individual CA cert if you were using a private CA, but since the iot.eclipse.org broker is using a well known public CA you need to specify the path to the system collection of CA certs.
On Linux that is /etc/ssl/certs so you would publish as follows:
mosquitto_pub -h iot.eclipse.org -p 8883 --capath /etc/ssl/certs/ -t testing/ben -m foo
If a certificate is like a passport which proves your identity, then CA is just like a passport office(1). You could consider Verisign, Entrust etc as passport offices. CA certficate is analogous to passport office providing a way to check if a passport is valid or not.
To prove their identity any two parties,( read server and client ), could use certificates. To verify the authenticity of a party( read server ), you need CA certificate. Linux system(Ubuntu) holds commonly used CA certificates at /etc/ssl/certs.
A client certificate is needed only if you need to authenticate yourself to the server. Here server is iot.eclipse.org which doesn't ask for client authentication and so you don't need client certificate.
So, to communicate securely with server ( read TLS ), you can use the CA store present in your system(Ubuntu) as below.
mosquitto_pub -h iot.eclipse.org -p 8883 -t my_topic -m my_message --capath /etc/ssl/certs/
How do I get complete certificate chain for a server? Though some claim one should be able to do just that with openssl s_client -showcerts, it turns not always to be the case.
echo | openssl s_client -CApath /etc/ssl/certs -connect www.ssllabs.com:443 \
-showcerts | grep -B2 BEGIN
depth=3 C = SE, O = AddTrust AB, OU = AddTrust External TTP Network, CN = AddTrust External CA Root
verify return:1
depth=2 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO RSA Certification Authority
verify return:1
depth=1 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO RSA Domain Validation Secure Server CA
verify return:1
depth=0 OU = Domain Control Validated, OU = PositiveSSL, CN = www.ssllabs.com
verify return:1
0 s:/OU=Domain Control Validated/OU=PositiveSSL/CN=www.ssllabs.com
i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
-----BEGIN CERTIFICATE-----
--
1 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
-----BEGIN CERTIFICATE-----
--
2 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
-----BEGIN CERTIFICATE-----
DONE
Here we have three certificates our of four. All except of the AddTrust External CA Root certificate. (Possibly because it is not included into the certificate bundle. And not like this is required. And yes, I can find the missing one at /etc/ssl/certs)
How do I get all certificates for a server in a fully automatic fashion?
Meta: I tried to answer this in superuser but you deleted it. Fortunately when I found this copy most of my work was still sitting in a scratch notepad I hadn't closed, otherwise I wouldn't have been willing to do the research work twice.
s_client -showcerts shows the certs sent by the server; according to the RFCs, this should be a valid chain in upward order except that the root MAY (in RFC2119 definition i.e. allowed but not particularly recommended) be omitted. However, not all servers are configured correctly, and some may send extra, missing, and/or out-of-order certs. Also depending on the CA used there may be more than one valid chain but the server can only send one. openssl currently will use only the chain sent, but this will change soon in 1.0.2, and other reliers already sometimes find a different chain than the one sent.
openssl: if the received chain is complete up to and maybe including a root which is in the truststore used (whose default location depends on system or build, and in any case can always be overridden) then openssl client will validate it as okay -- unless it is expired, or revoked and that info is available which usually it isn't. In that case you can write a client program that connects after setting a cert-verify callback function that outputs the full certs as processed by the validation loop, or other info from them you want, as opposed to s_client which uses a callback that logs (only) the subject name in the depth=n lines, which you can see in your example includes all 4 certs in the chain here. openssl is opensource, so a client program that does things mostly like s_client could be a modified copy of s_client (in this case specifically s_cb.c).
Java can also do this and is a good bit shorter to write, but requires Java be installed. If the received chain validates against an anchor in the truststore used (which defaults to a set of public roots but can be modified or overridden, and can have non-root anchors) you similarly can write a program (maybe 20 lines) to connect using a HandshakeCompletedListener which dumps the info from event.getPeerCertificates(). However if the chain doesn't validate, Java aborts the handshake with an exception and you get no certs at all, unlike the openssl case where you might get partial information before the error occurs -- plus openssl's checking, at least by default, isn't quite as strict anyway.
UPDATE: for completeness, in Java 7+, commandline keytool -printcert -sslserver displays the chain sent, in a rather cluttered format.
Among the browsers I can easily check, Firefox and Chrome on Windows (at least) can write out the chain they found and validated. ISTR but can't easily retest the Firefox error/exception dialog can also do this for a chain that fails to validate and may be incomplete. These are not automatic as-is, but I've seen ads for numerous "simulate GUI user input" tools that apparently could drive them as desired.
You get the chain including the builtin trusted root certificate inside the verify_callback (see SSL_CTX_set_verify. With a small Perl program you can dump the chain like this:
#!/usr/bin/perl
use strict;
use warnings;
use IO::Socket::SSL;
IO::Socket::SSL->new(
PeerHost => 'www.google.com:443',
SSL_verify_callback => sub {
my $cert = $_[4];
my $subject = Net::SSLeay::X509_NAME_oneline(Net::SSLeay::X509_get_subject_name($cert));
my $issuer = Net::SSLeay::X509_NAME_oneline(Net::SSLeay::X509_get_issuer_name($cert));
print "# $subject (issuer=$issuer)\n";
print Net::SSLeay::PEM_get_string_X509($cert),"\n";
return 1;
}
) or die $SSL_ERROR||$!;