Display received cert with curl? - ssl

With a slightly older version of curl, I had a handy batch file:
curl --verbose -k https://%1 2>&1 |grep -E "Connected to|subject|expire"
This would show me the IP connected to, with the subject and expiration date of the actual certificate negotiated, even if that was not the correct certificate for that domain name -- which is sometimes a problem for our hosting (we host literally thousands of domains on our multitenant application, about half with their own certs).
In specific, I would see something like this in the stderr output before grep filtered it:
* Server certificate:
* subject: CN=academy-fran.chi.v6.pressero.com
* start date: Feb 22 04:55:00 2017 GMT
* expire date: May 23 04:55:00 2017 GMT
* issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
* SSL certificate verify ok.
Today I had to reinstall the OS on my machine, and reinstalled curl. Now at version 7.52.1 (x86_64-w64-mingw32); previous one seems to have been 7.49.1 (i686-pc-cygwin). Curl no longer displays ANY certificate information, regardless of whether -k is used or not, if the TLS connection succeeds or not.
Is there an option that will give it back to me?

For anyone else on OSX or Linux, you can add this to your ~/.zshrc file:
function seecert () {
nslookup $1
(openssl s_client -showcerts -servername $1 -connect $1:443 <<< "Q" | openssl x509 -text | grep -iA2 "Validity")
}
Example usage, after you have run a source ~/.zshrc after the above additions:
% seecert www.google.com
Server: 1.1.1.1
Address: 1.1.1.1#53
Non-authoritative answer:
Name: www.google.com
Address: 172.217.10.100
depth=2 OU = GlobalSign Root CA - R2, O = GlobalSign, CN = GlobalSign
verify return:1
depth=1 C = US, O = Google Trust Services, CN = GTS CA 1O1
verify return:1
depth=0 C = US, ST = California, L = Mountain View, O = Google LLC, CN = www.google.com
verify return:1
DONE
Validity
Not Before: Nov 3 07:39:18 2020 GMT
Not After : Jan 26 07:39:18 2021 GMT
Thanks go to #ross-presser and his answer for the inspiration for this function.

Here is my replacement batch file, using openssl instead of curl:
#echo off
nslookup %1
(openssl s_client -showcerts -servername %1 -connect %1:443 <nul |openssl x509 -text |findstr /I "DNS After") 2>nul
This gives me this output:
C:\>seecert www.google.com
Server: 192.168.1.1
Address: 192.168.1.1#53
Non-authoritative answer:
Name: www.google.com
Address: 172.217.10.228
Name: www.google.com
Address: 2607:f8b0:4006:813::2004
Not After : Aug 16 09:49:00 2018 GMT
DNS:www.google.com

Related

Understanding openssl. Where is the cert file?

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.

openssl how to check server name indication (SNI)

I'm trying to verify whether a TLS client checks for server name indication (SNI).
I'm trying at first to reproduce the steps using openssl.
I tried to connect to google with this openssl command
openssl version
openSSL 1.1.1b 26 Feb 2019
openssl s_client -connect google.com:443 -servername "ibm.com"
Purposely I set the TLS extension -servername to a wrong domain, and I expect that openssl refuses connection, but it proceeds.
I also tried specifiying a CA certificate but nothing happened.
What should I expect when choosing a wrong SNI?
Log
openssl s_client -connect www.google.com:443 -servername "ibm.com"
CONNECTED(000001BC)
depth=1 C = US, O = Google Trust Services, CN = GTS CA 1O1
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C = US, ST = California, L = Mountain View, O = Google LLC, CN = www.google.com
verify return:1
---
Certificate chain
0 s:C = US, ST = California, L = Mountain View, O = Google LLC, CN = www.google.com
i:C = US, O = Google Trust Services, CN = GTS CA 1O1
1 s:C = US, O = Google Trust Services, CN = GTS CA 1O1
i:OU = GlobalSign Root CA - R2, O = GlobalSign, CN = GlobalSign
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFiTCCBHGgAwIBAgIRAOojQokwkAg5AgAAAABSqVUwDQYJKoZIhvcNAQELBQAw
QjELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFUdvb2dsZSBUcnVzdCBTZXJ2aWNlczET
MBEGA1UEAxMKR1RTIENBIDFPMTAeFw0xOTEyMjAxMzEzNDNaFw0yMDAzMTMxMzEz
NDNaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH
Ew1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgTExDMRcwFQYDVQQDEw53
d3cuZ29vZ2xlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMdb
zkTc78UBXe8/rti2OxAGZQUQ1WnilkCSqRuMn/gmc1jeiIDeZCWFurFin9+RBB/K
ib5xQx2iZ1ifcV+DOvDT16LEa887TehETAADpBnTJmVi0Z6GXjQQ9pyLrv+1PDYI
3z9Slkw3ZGVeMUE31etonDRB9lPN9skF09s1LvitIi4XdPXgaTNCBEWNMs1Tlv8H
1+UlaQiamriyTii4pptXv+KKsunDC//OEv1pm0cZnEeop8USMHermBzYkaFXC3ae
2hvV7Bj7w8c6PqHcTQ+e7xhoKoIzFVtneNoEyQL1h9QGtPdTofs/sidgd//Wo7sB
0JV1zq2EtSsKlp/N+U0CAwEAAaOCAlIwggJOMA4GA1UdDwEB/wQEAwIFoDATBgNV
HSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQiK5HMzk32
vjkQNky4VkFcyt/kQTAfBgNVHSMEGDAWgBSY0fhuEOvPm+xgnxiQG6DrfQn9KzBk
BggrBgEFBQcBAQRYMFYwJwYIKwYBBQUHMAGGG2h0dHA6Ly9vY3NwLnBraS5nb29n
L2d0czFvMTArBggrBgEFBQcwAoYfaHR0cDovL3BraS5nb29nL2dzcjIvR1RTMU8x
LmNydDAZBgNVHREEEjAQgg53d3cuZ29vZ2xlLmNvbTAhBgNVHSAEGjAYMAgGBmeB
DAECAjAMBgorBgEEAdZ5AgUDMC8GA1UdHwQoMCYwJKAioCCGHmh0dHA6Ly9jcmwu
cGtpLmdvb2cvR1RTMU8xLmNybDCCAQIGCisGAQQB1nkCBAIEgfMEgfAA7gB1ALIe
BcyLos2KIE6HZvkruYolIGdr2vpw57JJUy3vi5BeAAABbyOoMvgAAAQDAEYwRAIg
Ba8Zw9vk29aITM7udOH9zPzytfh5vjTxz2JPuNQuF6gCIEcduOoMHCV9s3G9uEwV
KXwfjtYwYvVlfKlCMW4ilFujAHUAXqdz+d9WwOe1Nkh90EngMnqRmgyEoRIShBh1
loFxRVgAAAFvI6gzIgAABAMARjBEAiBRslW536auv4WHgspy1wNvLEwS2VH66MMV
MJUcgN6IvwIgepYRaAuSuiJeAla6KuAnPCeJvXlscWTnFbm85DIEaegwDQYJKoZI
hvcNAQELBQADggEBAKqExHCpU6rjr/XMezkzy+fp76TST2l39vqIJKDdkQPe8V0I
afWgkc/T3z4bZx/4plzW+iAvk4KTyvDWNbv2xh3njAB6FoJyZkf9/H6zahLSaS4z
qiI3axO3rSD6AW6G5u5cKIN8IaJzLc6CgW+NkxMulOM//u008jZIvp6qGwVfeMlc
1kDocDf8imLam7yM4BQKvOPb5w7e+SgKO6qxRkhFsL18xgh7HZk8F1fvFFhGyuYQ
WL0jORJvjomn/uMxiU9UFlAiVtsY0zmyuVIEp2rDpdfaG8AnVV4BLnR6Ey8TpHzR
w1b3ocoOJi0is55pSMwU8L9RE7cz9MP9krrb7zU=
-----END CERTIFICATE-----
subject=C = US, ST = California, L = Mountain View, O = Google LLC, CN = www.google.com
issuer=C = US, O = Google Trust Services, CN = GTS CA 1O1
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 3018 bytes and written 389 bytes
Verification error: unable to get local issuer certificate
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 20 (unable to get local issuer certificate)
---
The certificate you got back is not yours, it's Google's.
Google implements SNI. Your request states that it wants to establish a connection with the hostname "ibm.com" that Google does not know about. So it decides to answer with its own certificate.
If this was a browser, you would get a name mismatch warning of some kind.
So to answer your question, to test an invalid SNI, look for the hostname in the output. Here is a command I use:
echo -n | openssl s_client -connect google.com:443 -servername ibm.com | openssl x509 -noout -text | grep ibm.com
You won't get any hits, that means that either SNI is not supported or the server you are connecting to does not recognize it.
I am checking on a Mac and I had to make a modification by adding the -connect directive.
echo -n | openssl s_client -connect google.com:443 -servername ibm.com | openssl x509 -noout -text | grep ibm.com
Since we don't need to input anything, I'd update the command to close the openssl command by forcing input from /dev/null:
echo -n | openssl s_client -connect google.com:443 -servername ibm.com </dev/null | openssl x509 -noout -text | grep ibm.com

Multi-domain SAN certificate throwing 'unable to verify the first certificate' error

I have a self-signed multi-domain SAN certificate as below :
$ openssl x509 -in trustedCertificates.pem -noout -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
d0:6e:6b:66:c6:55:44:09
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=WA, L=Seattle, O=MyOrg, OU=MyDept, CN=*.us-west-2.compute.internal
Validity
Not Before: Mar 23 22:15:10 2018 GMT
Not After : Mar 23 22:15:10 2019 GMT
Subject: C=US, ST=WA, L=Seattle, O=MyOrg, OU=MyDept, CN=*.us-west-2.compute.internal
Subject Public Key Info:
...
...
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Key Usage:
Key Encipherment, Data Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication
X509v3 Subject Alternative Name:
DNS:*.us-west-2.compute.amazonaws.com
Signature Algorithm: sha256WithRSAEncryption
...
...
I use this certificate to configure+enable SSL on presto server. Further, I import this certificate in the java truststore of the presto-cli client machine (in my case, both are on same host). However, when I connect to the presto-server, I keep getting the below error message even though I do use a FQDN that matches the CN in the certificate :
$ presto-cli --server https://ip-xxx-xx-xx-xxx.us-west-2.compute.internal:8446 --catalog hive --schema default --debug --user Administrator --password
Password:
presto:default> show schemas;
Error running command: javax.net.ssl.SSLPeerUnverifiedException: Hostname ip-xxx-xx-xx-xxx.us-west-2.compute.internal not verified:
certificate: sha256/zN/GPT/AWTkUAEpAGhhSTvQAmIYPRmLBxvBU6oJDQfM=
DN: CN=*.us-west-2.compute.internal, OU=MyDept, O=MyOrg, L=Seattle, ST=WA, C=US
subjectAltNames: [*.us-west-2.compute.amazonaws.com]
...
...
at com.facebook.presto.cli.Presto.main(Presto.java:32)
Caused by: javax.net.ssl.SSLPeerUnverifiedException: Hostname ip-xxx-xx-xx-xxx.us-west-2.compute.internal not verified:
certificate: sha256/zN/GPT/AWTkUAEpAGhhSTvQAmIYPRmLBxvBU6oJDQfM=
DN: CN=*.us-west-2.compute.internal, OU=MyDept, O=MyOrg, L=Seattle, ST=WA, C=US
subjectAltNames: [*.us-west-2.compute.amazonaws.com]
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:308)
at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.java:268)
at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:160)
at
...
...
com.facebook.presto.client.JsonResponse.execute(JsonResponse.java:130)
... 7 more
When I do a connection check, I see this error :
$ openssl s_client -connect ip-xxx-xx-xx-xxx.us-west-2.compute.internal:8446
CONNECTED(00000003)
depth=0 C = US, ST = WA, L = Seattle, O = MyOrg, OU = MyDept, CN = *.us-west-2.compute.internal
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C = US, ST = WA, L = Seattle, O = MyOrg, OU = MyDept, CN = *.us-west-2.compute.internal
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
0 s:/C=US/ST=WA/L=Seattle/O=MyOrg/OU=MyDept/CN=*.us-west-2.compute.internal
i:/C=US/ST=WA/L=Seattle/O=MyOrg/OU=MyDept/CN=*.us-west-2.compute.internal
---
Server certificate
-----BEGIN CERTIFICATE-----
...
...
If I use a certificate with only single domain CN=*.us-west-2.compute.internal without any SAN extension, everything seem to be working fine. Any ideas on what's going wrong here?

Using '-servername' param with openssl s_client

I am installing a new SSL certificate on Centos6/Apache and my web browser keeps picking up the old certificate. To test my setup, I am using "openssl s_client" but I am seeing different results based on the "-servername" parameter. No one seems to us this parameter and it does not appear in the man pages but I saw it mentioned here OpenSSL: Check SSL Certificate Expiration Date and More .
If I run this command:
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -issuer -subject -dates
I get the correct date for the certificate.
(notBefore=Apr 20 00:00:00 2017 GMT notAfter=Apr 20 23:59:59 2018 GMT)
However, if I intruduce the -servername parameter into the commmand
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -issuer -subject -dates
I then get the expired date that my browser is showing -
(notBefore=Apr 20 00:00:00 2016 GMT notAfter=Apr 20 23:59:59 2017 GMT)
Can anyone explain why this is happening, as this must be related to the reason why my SSL certificate shows as expired in my browser.
Thanks
O
The servername argument to s_client is documented (briefly) on this page:
https://www.openssl.org/docs/man1.0.2/apps/s_client.html
Essentially it works a little like a "Host" header in HTTP, i.e. it causes the requested domain name to be passed as part of the SSL/TLS handshake (in the SNI - Server Name Indication extension). A server can then host multiple domains behind a single IP. It will respond with the appropriate certificate based on the requested domain name.
If you do not request a specific domain name the server does not know which certificate to give you, so you end up with a default one. In your case one of the certificates that the server is serving up for your domain has expired, but the default certificate has not.
You need to make sure you are updating the correct VirtualHost entry for your domain, e.g. see:
https://www.digicert.com/ssl-support/apache-multiple-ssl-certificates-using-sni.htm

Browser, s_client without SNI and expired certificate

When I access one of my subdomains: say https://foo.example.com in a browser and inspect the certificates, the certificate looks great. When I use openssl from a remote computer it shows an expired certificate. How can this be?
I tried to reproduce what was found in this question, but my scenario is different. When I run
echo | openssl s_client -showcerts -connect foo.example.com:443 2>&1 | grep Verify
I see:
Verify return code: 10 (certificate has expired)
When I run:
echo | openssl s_client -showcerts -connect foo.example.com:443 2>&1 | openssl x509 -noout -dates
I get:
notBefore=Sep 27 15:10:20 2014 GMT
notAfter=Sep 27 15:10:20 2015 GMT
It looks expired but the browser doesn't show it as expired. Here it is in the browser:
See the 1st comment by #jww. He pointed out that I needed to add -tls1 -servername foo.example.com to my openssl command. His comment:
Try adding -tls1 -servername foo.example.com. I'm guessing you have a front-end server that's providing a default domain for requests without SNI, and the default domain is routed to an internal server with the old certificate. When the browsers connect, they use SNI and get the server for which you have updated the certificate. Or, there could be an intermediate with an expired certificate in the chain that's being served. If you provide real information, its easier for us to help you with problems like this.