Python ssl: IP address in check_hostname - ssl

Can Python3 ssl library work with IP addresses, not hostnames in the certificate?
Let's suppose I try to connect to a server:
ip = '192.168.0.99'
context = ssl.create_default_context(cafile='ca.crt')
with socket.create_connection((ip, 443)) as sock:
with context.wrap_socket(sock, server_hostname=ip) as ssock:
print(ssock.version())
When I run it, I get an error:
SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)
IP address 192.168.0.99 is written into certificate Common Name and into Subject Alt Name too.
$ openssl x509 -in san.crt -noout -text | grep -B 1 192.168.0.99
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = RU, ST = MSK, O = Internet Widgits Pty Ltd, OU = example, CN = 192.168.0.99
--
Not After : Jul 4 21:31:32 2030 GMT
Subject: C = RU, ST = MSK, O = Internet Widgits Pty Ltd, OU = example, CN = 192.168.0.99
--
X509v3 Subject Alternative Name:
IP Address:192.168.0.99

SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)
The error is not about the hostname not matching the certificate but about the inability to find a locally trusted issuer for the certificate.
context = ssl.create_default_context(cafile='ca.crt')
I assume your intention is that ca.crt is the issuer for san.crt. But according to what you show from the certificate it looks like a self-signed certificate instead (subject and issuer is the same) and not signed by ca.crt.

Related

Openssl: error 20 at 0 depth lookup: unable to get local issuer certificate

I have 3 certificates rootca.pem, intermediateca.pem and server.pem
Openssl verify intermediateca by root is fine
openssl verify -verbose -CAfile rootca.pem intermediateca.pem
intermediateca.pem: OK
Server certificate, signed by intermediate - verification failed
openssl verify -verbose -CAfile rootca.pem -untrusted intermediateca.pem server.pem
CN = 2ip.ru
error 20 at 0 depth lookup: unable to get local issuer certificate
error server.pem: verification failed
I check hash subject-issuer of rootca intermediateca and intermediateca server. hash correct
I paste my certificate chain here
The Authority Key Identifier (AKI) is messed up in the certificates, which causes it to fail to build the trust path. Both the leaf certificate and the intermediate certificate have the AKI point to the root certificate:
# leaf
Issuer: C = RU, O = JSC Sberbank-AST, CN = int_ca
AKI: keyid:6C:C5:5B:22:4B:2D:CA:EC:C1:15:03:F6:5D:AD:C4:E8:4C:1D:06:89
# intermediate
Issuer: DC = ru, DC = sberbank-ast, CN = sberbank-ast-SUN-CA
AKI: keyid:6C:C5:5B:22:4B:2D:CA:EC:C1:15:03:F6:5D:AD:C4:E8:4C:1D:06:89
As can be seen, both leaf certificate and intermediate certificate wrongly claim to be issued by the same CA based on the Authority Key Identifier, while they correctly claim to be issued by different CA using the Issuer field.
But not only the Issuer field must match the Subject field of the issuer, the Authority Key Identifier must match the Subject Key Identifier of the issuer. While this is true for the relation between intermediate CA and root CA it is not true for the relation between leaf certificate and intermediate CA - and thus verify fails here.

build.shibboleth.net gives wrong certificate for some regions

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.

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?

openssl verify not working with GeoTrust Certificate

I have a newly purchased GeoTrust domain certificate and a matching CA file and would like to verify with openssl.
openssl verify -verbose -purpose any
-CAfile /full/path/sub.domain.com-geotrust.crt /full/path/sub.domain.com.crt
From this page: https://secure.marumoto.us/motowiki/tiki-index.php?page=Verify+a+Certificate+Chain
The issuer of each certificate in the chain should match the subject of the next certificate in the chain. For example the issuer of myserver.mydomain.com.cert should match the subject of myintermediate_ca.cert, and the issuer of myintermediate_ca.cert should match the subject of myroot_ca.cert. You can use the following command to view a certificate in .pem or base64 format.
From
openssl x509 -text -in sub.domain.crt and
openssl x509 -text -in sub.domain-geotrust.crt (CA root file)
sub.domain.com
Subject: OU=GT44865949,
OU=See www.geotrust.com/resources/cps (c)15,
OU=Domain Control Validated - QuickSSL(R),
CN=sub.domain.com
Issuer:
commonName = GeoTrust DV SSL CA - G4
organizationalUnitName = Domain Validated SSL
organizationName = GeoTrust Inc.
countryName = US
Intermediate:
Subject:
commonName = GeoTrust DV SSL CA - G4
organizationalUnitName = Domain Validated SSL
organizationName = GeoTrust Inc.
countryName = US
Issuer:
commonName = GeoTrust Global CA
organizationName = GeoTrust Inc.
countryName = US
Root:
Subject:
commonName = GeoTrust Global CA
organizationName = GeoTrust Inc.
countryName = US
Issuer:
organizationalUnitName = Equifax Secure Certificate Authority
organizationName = Equifax
countryName = US
It appears my Issuer and Subject fields are matching up properly but I am getting the following error with openssl:
error 20 at 0 depth lookup:unable to get local issuer certificate
/full/path/sub.domain.com.crt: /OU=GT44865949/OU=See www.geotrust.com/resources/cps (c)15/OU=Domain Control Validated - QuickSSL(R)/CN=sub.domain.com
error 20 at 0 depth lookup:unable to get local issuer certificate
I'd like to use this and a similar GeoTrust certificate at a different subdomain to do two-way SSL authentication at a restful web url but the certs won't verify with 'openssl verify' against the CA files issued with them. Any suggestions much appreciated.
As per discussion, it is a matter of allowing openssl to see the entire validation chain: with both GeoTrust CA intermediates and the root.
openssl should be run with CADir parameter containing all the 3 CAs in PEM format. The actual root for GeoTrust can either be extracted from your favourite browser, or here: filedropper.com/geotrustglobalca.
When you configure your actual server, just make sure that you send the intermediates on the Server Hello, some sites don't do that and break clients that do not have the intermediates cached.
Following on from RomanK's answer, you can get the GeoTrust Global CA from their root certificate store. They have a number of primary/universal/global certificates listed there, so make sure to get the right one for your intermediate certificate.
As per the chain display, the root certificate is not self signed . Its shows its issued by Equifax. Openssl will continue giving the error "local issuer certificate not found" till it gets a self signed root certificate. When it comes across a root self signed then it's able to verify the certificate chain as complete.

How to verify certificate chain with openssl

I am trying to verify a certificate file with OpenSSL. Can you explain me why s_client connection succeeds, but verify file with the same certificate chain fails? How can I verify the file?
Note I compiled OpenSSL 1.0.1k myself, it shouldn't be using any distro-specific config. And I provided the same CAfile to both commands.
$ openssl s_client -CAfile /etc/pki/tls/certs/ca-bundle.crt -connect www.google.com:443
WARNING: can't open config file: /usr/local/ssl/openssl.cnf
CONNECTED(00000003)
depth=3 C = US, O = Equifax, OU = Equifax Secure Certificate Authority
verify return:1
depth=2 C = US, O = GeoTrust Inc., CN = GeoTrust Global CA
verify return:1
depth=1 C = US, O = Google Inc, CN = Google Internet Authority G2
verify return:1
depth=0 C = US, ST = California, L = Mountain View, O = Google Inc, CN = www.google.com
verify return:1
---
Certificate chain
0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=www.google.com
i:/C=US/O=Google Inc/CN=Google Internet Authority G2
1 s:/C=US/O=Google Inc/CN=Google Internet Authority G2
i:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
2 s:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
i:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority
---
...
Verify return code: 0 (ok)
---
If I run it with -showcerts argument, it outputs all three certificates sent from server. I concatenated them into file google.pem. But the chain can't be verified. See:
$ openssl verify -CAfile /etc/pki/tls/certs/ca-bundle.crt google.pem
WARNING: can't open config file: /usr/local/ssl/openssl.cnf
google.pem: C = US, ST = California, L = Mountain View, O = Google Inc, CN = www.google.com
error 20 at 0 depth lookup:unable to get local issuer certificate
Applying a patch suggested on https://stackoverflow.com/a/27606964/1823988 doesn't help.
I found it. openssl verify doesn't expect certificate file to contain its chain. Chain needs to be passed with -untrusted argument. It works with the same file, trust is still determined by finding a trusted root in -CAfile.
openssl verify -CAfile /etc/pki/tls/certs/ca-bundle.crt -untrusted google.pem google.pem