Executed the command:
openssl s_client -connect (redacted):443
and I get the output
depth=1 C = US, O = Let's Encrypt, CN = R3
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = *.(redacted)
verify return:1
---
Certificate chain
0 s:CN = *.(redacted)
i:C = US, O = Let's Encrypt, CN = R3
1 s:C = US, O = Let's Encrypt, CN = R3
i:O = Digital Signature Trust Co., CN = DST Root CA X3
---
What does the error "unable to get local issuer certificate" mean? From the above I can see that the full chain is there, and that the root CA "DST Root CA X3" signed the "R3" cert. So surely that R3 cert does not need to be explicitly trusted? Is this good enough?
Is this cause for concern?
verify error:num=20:unable to get local issuer certificate
The trust chain from the leaf (server certificate) via the intermediate chain certificates (R3) must end in a locally trusted root CA (DST Root CA X3). Obviously this root CA is not locally trusted in the CA store used by your openssl setup (or maybe you have explicitly used -CApath or -CAfile).
In my development environment I am building code that will connect with an API that only accepts TLS 1.2 (Authorize.net Ruby SDK).
#<OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=error: certificate verify failed (self signed certificate in certificate chain)>
My colleage can run the code in his local environment and not get this error. Does it depend on ssl? I'm using openssl 1.1.1h he is using libressl.
Update:
The output of:
─$ openssl s_client -showcerts -connect apitest.authorize.net:443
is:
CONNECTED(00000005) depth=1 C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2012 Entrust, Inc. - for authorized use only", CN = Entrust Certification Authority - L1K verify return:1 depth=0 C = US, ST = California, L = Foster City, O = Authorize.Net, CN = *.authorize.net verify return:1
--- Certificate chain 0 s:C = US, ST = California, L = Foster City, O = Authorize.Net, CN = *.authorize.net i:C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2012 Entrust, Inc. - for authorized use only", CN = Entrust Certification Authority
- L1K
-----BEGIN CERTIFICATE----- MIIGxzCCBa+gAwIBAgIQfjbSKF+9lNAAAAAAUP5NeTANBgkqhkiG9w0BAQsFADCB ujELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsT H1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAy MDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEuMCwG A1UEAxMlRW50cnVzdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEwxSzAeFw0y MDAyMDUyMDQ0MDhaFw0yMTAzMTUyMTE0MDhaMGoxCzAJBgNVBAYTAlVTMRMwEQYD VQQIEwpDYWxpZm9ybmlhMRQwEgYDVQQHEwtGb3N0ZXIgQ2l0eTEWMBQGA1UEChMN QXV0aG9yaXplLk5ldDEYMBYGA1UEAwwPKi5hdXRob3JpemUubmV0MIIBIjANBgkq hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnBMtpwsQcEI3Pd7MKBPs/bhVye/yYNnL Pp3zrJloBy217QtpZpbXj9c1Sjt7xMXv9RotOt9aSbOsAc7CGkKPuwUIcdVbQg/8 gOlWxuT2zF+gek3NzmQSUSbrnVLe5XuT5OQBxjLH922Rm5OTJ3k6rcrQz6Q3nN37 hBClYcHBlTdnFTfCFsSDMAm2w9njNgLSkF1JaPnWfTPnda8xBmqLwJBDgTEy/a1P kPnbsosyp1/R9cE5Dn7VuFfzEgN/D9/YNbg8KxK63O5GVswc/mSzHT19wb4lgXlF zptvoYHKIyl1KwCw4/tt5fAuI59KRUAYzQKQFTcRZbNcYAEccmKilwIDAQABo4ID FjCCAxIwKQYDVR0RBCIwIIIPKi5hdXRob3JpemUubmV0gg1hdXRob3JpemUubmV0 MIIBfQYKKwYBBAHWeQIEAgSCAW0EggFpAWcAdQBVgdTCFpA2AUrqC5tXPFPwwOQ4 eHAlCBcvo6odBxPTDAAAAXAXNAX7AAAEAwBGMEQCIAOcM5IXfx51+2Ne2ApYlwkw /sHPMPZUSbXYwF6m4P4uAiBZOnqTkH63rvErM3JZ3nLc0om+qqRLriHI5KZxYN4O JQB2AFYUBpov18Ls0/XhvUSyPsdGdrm8mRFcwO+UmFXWidDdAAABcBc0BggAAAQD AEcwRQIhAJrKOTbyOlcTpELU1FDB844Svqz0BQFO5a8beVuPQoulAiAZwnatMff6 6VGE80UJ8BqHDwPFZOjj5jt4dHtYKonU/AB2AKS5CZC0GFgUh7sTosxncAo8NZgE
+RvfuON3zQ7IDdwQAAABcBc0BgQAAAQDAEcwRQIgbC4cCT2jB38DWbzWobGL4NG1 9BdOTPaWd3acm2v+7yACIQDE2H8U5+65+IMrn5UXRFP/DdSCHNQI/xr7OKEQGDJp 4zAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC MDMGA1UdHwQsMCowKKAmoCSGImh0dHA6Ly9jcmwuZW50cnVzdC5uZXQvbGV2ZWwx ay5jcmwwSwYDVR0gBEQwQjA2BgpghkgBhvpsCgEFMCgwJgYIKwYBBQUHAgEWGmh0 dHA6Ly93d3cuZW50cnVzdC5uZXQvcnBhMAgGBmeBDAECAjBoBggrBgEFBQcBAQRc MFowIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLmVudHJ1c3QubmV0MDMGCCsGAQUF BzAChidodHRwOi8vYWlhLmVudHJ1c3QubmV0L2wxay1jaGFpbjI1Ni5jZXIwHwYD VR0jBBgwFoAUgqJwdN28Uz/Pe9T3zX+nYMYKTL8wHQYDVR0OBBYEFK069bMPyloE nNXcyli5AieCD9MiMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAB2wqZ17 AAmYCO4EnmXWDTp1D9iBbNQxYSnj6oR9w0kMVahQ4Q43rdERaw2BsNWByG2peXP0 GRoOTUr5dpAKu5qa1A4V93Y/pc8RuDM0mxI/EmgzO7D4eSPRvC6735sa6XVDc5mk G9hccypCAR6u+tYeXGTmcYG10zunnHPQIf67zM1INdm+pfMfBc/IzvZ7tVWkfOEQ Wl2C/+i5hcdrIONTWD7QWqmr34kZckq+NLcF0z2M70ZdwvdeWxf1xFjgGfdoakuH PsuQI8XDA9DMc3r4GbTid70rTB+z0DNajMz/jGOm7r/Sp2C8wK+ukrzd9vVpZyf/ Q+GfoA0hiovUxiI=
-----END CERTIFICATE----- 1 s:C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2012 Entrust, Inc. - for authorized use only", CN = Entrust Certification Authority - L1K i:C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2009 Entrust, Inc. - for authorized use only", CN = Entrust Root Certification Authority - G2
-----BEGIN CERTIFICATE----- MIIFDjCCA/agAwIBAgIMDulMwwAAAABR03eFMA0GCSqGSIb3DQEBCwUAMIG+MQsw CQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5jLjEoMCYGA1UECxMfU2Vl IHd3dy5lbnRydXN0Lm5ldC9sZWdhbC10ZXJtczE5MDcGA1UECxMwKGMpIDIwMDkg RW50cnVzdCwgSW5jLiAtIGZvciBhdXRob3JpemVkIHVzZSBvbmx5MTIwMAYDVQQD EylFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjAeFw0x NTEwMDUxOTEzNTZaFw0zMDEyMDUxOTQzNTZaMIG6MQswCQYDVQQGEwJVUzEWMBQG A1UEChMNRW50cnVzdCwgSW5jLjEoMCYGA1UECxMfU2VlIHd3dy5lbnRydXN0Lm5l dC9sZWdhbC10ZXJtczE5MDcGA1UECxMwKGMpIDIwMTIgRW50cnVzdCwgSW5jLiAt IGZvciBhdXRob3JpemVkIHVzZSBvbmx5MS4wLAYDVQQDEyVFbnRydXN0IENlcnRp ZmljYXRpb24gQXV0aG9yaXR5IC0gTDFLMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEA2j+W0E25L0Tn2zlem1DuXKVh2kFnUwmqAJqOV38pa9vH4SEkqjrQ jUcj0u1yFvCRIdJdt7hLqIOPt5EyaM/OJZMssn2XyP7BtBe6CZ4DkJN7fEmDImiK m95HwzGYei59QAvS7z7Tsoyqj0ip/wDoKVgG97aTWpRzJiatWA7lQrjV6nN5ZGhT JbiEz5R6rgZFDKNrTdDGvuoYpDbwkrK6HIiPOlJ/915tgxyd8B/lw9bdpXiSPbBt LOrJz5RBGXFEaLpHPATpXbo+8DX3Fbae8i4VHj9HyMg4p3NFXU2wO7GOFyk36t0F ASK7lDYqjVs1/lMZLwhGwSqzGmIdTivZGwIDAQABo4IBDDCCAQgwDgYDVR0PAQH/ BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwMwYIKwYBBQUHAQEEJzAlMCMGCCsG AQUFBzABhhdodHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAwBgNVHR8EKTAnMCWgI6Ah hh9odHRwOi8vY3JsLmVudHJ1c3QubmV0L2cyY2EuY3JsMDsGA1UdIAQ0MDIwMAYE VR0gADAoMCYGCCsGAQUFBwIBFhpodHRwOi8vd3d3LmVudHJ1c3QubmV0L3JwYTAd BgNVHQ4EFgQUgqJwdN28Uz/Pe9T3zX+nYMYKTL8wHwYDVR0jBBgwFoAUanImetAe 733nO2lR1GyNn5ASZqswDQYJKoZIhvcNAQELBQADggEBADnVjpiDYcgsY9NwHRkw y/YJrMxp1cncN0HyMg/vdMNY9ngnCTQIlZIv19+4o/0OgemknNM/TWgrFTEKFcxS BJPok1DD2bHi4Wi3Ogl08TRYCj93mEC45mj/XeTIRsXsgdfJghhcg85x2Ly/rJkC k9uUmITSnKa1/ly78EqvIazCP0kkZ9Yujs+szGQVGHLlbHfTUqi53Y2sAEo1GdRv c6N172tkw+CNgxKhiucOhk3YtCAbvmqljEtoZuMrx1gL+1YQ1JH7HdMxWBCMRON1 exCdtTix9qrKgWRs6PLigVWXUX/hwidQosk8WwBD9lu51aX8/wdQQGcHsFXwt35u Lcw=
-----END CERTIFICATE----- 2 s:C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2009 Entrust, Inc. - for authorized use only", CN = Entrust Root Certification Authority - G2 i:C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2009 Entrust, Inc. - for authorized use only", CN = Entrust Root Certification Authority - G2
-----BEGIN CERTIFICATE----- MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v 1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g==
-----END CERTIFICATE-----
--- Server certificate subject=C = US, ST = California, L = Foster City, O = Authorize.Net, CN = *.authorize.net
issuer=C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2012 Entrust, Inc. - for authorized use only", CN = Entrust Certification Authority - L1K
--- No client certificate CA names sent
--- SSL handshake has read 4298 bytes and written 641 bytes Verification: OK
--- New, TLSv1.2, Cipher is AES256-GCM-SHA384 Server public key is 2048 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE No ALPN negotiated SSL-Session:
Protocol : TLSv1.2
Cipher : AES256-GCM-SHA384
Session-ID: A2982432F7DD99178A611C2F5D25409F91236B173AF83B08C0E479142EB590AF
Session-ID-ctx:
Master-Key: ECBB0DEAE245D006AA30D090D9D00B8C937DBECB2F8D1A19EC8B720A5B3A1A946B55FC00C20778E0FD89E6EF98A730E1
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1608845194
Timeout : 7200 (sec)
Verify return code: 0 (ok)
Extended master secret: yes
And I don't see a self-signed cert in there? Or am I on the wrong trail...
The code is simple and you can try it at home to test yourself if you feel inclined.
include AuthorizeNet::API
trx = AuthorizeNet::API::Transaction.new('8Gxa...', '6LKh9...', gateway: :sandbox)
request = CreateCustomerProfileRequest.new
request.profile = CustomerProfileType.new
request.profile.merchantCustomerId = 'abc'
request.profile.description = 'name'
response = trx.create_customer_profile(request)
So I need two things:
To understand if this is a problem with my setup/environment, and how to fix it.
Or, to understand if this is a problem on the API's part (Authorize.net) and how to compensate for it or what tell them is actually happening
Did you double-check with the developer which CA certificates are loaded by libressl in his setup?
A self-signed in the chain error implies that the verify operation is able to build the trust chain, but not to find a certificate authority in that chain.
Since the certificate chain inspection and validation does work in other places, I would first verify his setup.
More precisely, if we look at the chain:
$ openssl s_client -showcerts -connect apitest.authorize.net:443
CONNECTED(00000003)
depth=2 C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2009 Entrust, Inc. - for authorized use only", CN = Entrust Root Certification Authority - G2
verify return:1
depth=1 C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2012 Entrust, Inc. - for authorized use only", CN = Entrust Certification Authority - L1K
verify return:1
depth=0 C = US, ST = California, L = Foster City, O = Authorize.Net, CN = *.authorize.net
verify return:1
---
Certificate chain
0 s:C = US, ST = California, L = Foster City, O = Authorize.Net, CN = *.authorize.net
i:C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2012 Entrust, Inc. - for authorized use only", CN = Entrust Certification Authority - L1K
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
1 s:C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2012 Entrust, Inc. - for authorized use only", CN = Entrust Certification Authority - L1K
i:C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2009 Entrust, Inc. - for authorized use only", CN = Entrust Root Certification Authority - G2
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
2 s:C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2009 Entrust, Inc. - for authorized use only", CN = Entrust Root Certification Authority - G2
i:C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2009 Entrust, Inc. - for authorized use only", CN = Entrust Root Certification Authority - G2
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
---
Server certificate
subject=C = US, ST = California, L = Foster City, O = Authorize.Net, CN = *.authorize.net
issuer=C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2012 Entrust, Inc. - for authorized use only", CN = Entrust Certification Authority - L1K
---
No client certificate CA names sent
---
SSL handshake has read 4298 bytes and written 641 bytes
Verification: OK
---
This tells us that the chain uses Entrust G2 root certificate, with the L1k certificate.
This CA certificate can be downloaded from https://www.entrust.com/resources/certificate-solutions/tools/root-certificate-downloads (to double check).
I would suggest verifying with the developer that this cert is correctly trusted in his setup.
apitest.authorize.net has a wildcard certificate.
From file x509v3.h in openssl-1.1.1h
/* Disable wildcard matching for dnsName fields and common name. */
# define X509_CHECK_FLAG_NO_WILDCARDS 0x2
This shows that wildcard matching is disabled by default.
In the v3_utl.c
if (flags & X509_CHECK_FLAG_NO_WILDCARDS)
equal = equal_nocase;
else
equal = equal_wildcard;
This means Openssl will use non case sensitive comparison and in that case apitest.authorize.netwill not match *.authorize.net
I guess your server doesn't have to send the Entrust's root certificate who is necessarily a self signed certificate.
2 s:C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2009 Entrust, Inc. - for authorized use only", CN = Entrust Root Certification Authority - G2
i:C = US, O = "Entrust, Inc.", OU = See www.entrust.net/legal-terms, OU = "(c) 2009 Entrust, Inc. - for authorized use only", CN = Entrust Root Certification Authority - G2
Ug it turns out that this is actually expected for the authorize.net sandbox. I need to pass a verify_ssl flag and set it to false.
I made a program that connects to website(tls) and save certificate chain to files.
Sometimes certificate chain from a website is looking different from what I expected.
One of this certificate chain is issued from Sectigo(ex Comodo) CA.
I think "AddTrust External CA Root" should located in the last certificate of chain but is located in second certificate in its chain.(please look at below Certificate chain part )
$ openssl s_client -showcerts -connect adblockplus.org:443
CONNECTED(00000003)
depth=3 C = SE, O = AddTrust AB, OU = AddTrust External TTP Network, CN = AddTrust External CA Root
verify return:1
depth=2 C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network, CN = USERTrust RSA Certification Authority
verify return:1
depth=1 C = GB, ST = Greater Manchester, L = Salford, O = Sectigo Limited, CN = Sectigo RSA Extended Validation Secure Server CA
verify return:1
depth=0 serialNumber = HRB 73508, jurisdictionC = DE, businessCategory = Private Organization, C = DE, postalCode = 50825, ST = Nordrhein-Westfalen, L = K\C3\B6ln, street = Lichtstra\C3\9Fe 25, O = Eyeo GmbH, OU = COMODO EV SSL, CN = www.adblockplus.org
verify return:1
---
Certificate chain
0 s:/serialNumber=HRB 73508/jurisdictionC=DE/businessCategory=Private Organization/C=DE/postalCode=50825/ST=Nordrhein-Westfalen/L=K\xC3\xB6ln/street=Lichtstra\xC3\x9Fe 25/O=Eyeo GmbH/OU=COMODO EV SSL/CN=www.adblockplus.org
i:/C=GB/ST=Greater Manchester/L=Salford/O=Sectigo Limited/CN=Sectigo RSA Extended Validation Secure Server CA
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
1 s:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
2 s:/C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=USERTrust RSA Certification Authority
i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
3 s:/C=GB/ST=Greater Manchester/L=Salford/O=Sectigo Limited/CN=Sectigo RSA Extended Validation Secure Server CA
i:/C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=USERTrust RSA Certification Authority
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
---
Server certificate
subject=/serialNumber=HRB 73508/jurisdictionC=DE/businessCategory=Private Organization/C=DE/postalCode=50825/ST=Nordrhein-Westfalen/L=K\xC3\xB6ln/street=Lichtstra\xC3\x9Fe 25/O=Eyeo GmbH/OU=COMODO EV SSL/CN=www.adblockplus.org
issuer=/C=GB/ST=Greater Manchester/L=Salford/O=Sectigo Limited/CN=Sectigo RSA Extended Validation Secure Server CA
my question is:
Is this case is normal?
Is the web server(adblockplus this time) making Certificate path ?
How to determine valid certificate path?
Any comments are welcome. thanks
A TLS-was-SSL server is supposed to send the certificate chain in the correct order in the handshake, but some don't, and most clients including OpenSSL will still handle it correctly, by matching up issuer=subject names, as long as the leaf (end-entity) cert is first. Note the trace of the validation process:
depth=3 C = SE, O = AddTrust AB, OU = AddTrust External TTP Network, CN = AddTrust External CA Root
verify return:1
depth=2 C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network, CN = USERTrust RSA Certification Authority
verify return:1
depth=1 C = GB, ST = Greater Manchester, L = Salford, O = Sectigo Limited, CN = Sectigo RSA Extended Validation Secure Server CA
verify return:1
depth=0 serialNumber = HRB 73508, jurisdictionC = DE, businessCategory = Private Organization, C = DE, postalCode = 50825, ST = Nordrhein-Westfalen, L = K\C3\B6ln, street = Lichtstra\C3\9Fe 25, O = Eyeo GmbH, OU = COMODO EV SSL, CN = www.adblockplus.org
verify return:1
You can see that the certificates were used in the correct top-to-bottom order even though they weren't received in the correct bottom-to-top order.
This misbehavior is common enough TLS 1.3 was changed to officially permit it. Compare TLS 1.2 in RFC 5246 7.4.2:
certificate_list ... The sender's
certificate MUST come first in the list. Each following
certificate MUST directly certify the one preceding it. Because
certificate validation requires that root keys be distributed
independently, the self-signed certificate that specifies the root
certificate authority MAY be omitted from the chain, under the
assumption that the remote end must already possess it in order to
validate it in any case.
to TLS 1.3 in RFC 8446 4.4.2, emphasis added:
... The sender's certificate MUST come in the first
CertificateEntry in the list. Each following certificate SHOULD
directly certify the one immediately preceding it. Because
certificate validation requires that trust anchors be distributed
independently, a certificate that specifies a trust anchor MAY be
omitted from the chain, provided that supported peers are known to
possess any omitted certificates.
Note: Prior to TLS 1.3, "certificate_list" ordering required each
certificate to certify the one immediately preceding it; however,
some implementations allowed some flexibility. Servers sometimes
send both a current and deprecated intermediate for transitional
purposes, and others are simply configured incorrectly, but these
cases can nonetheless be validated properly. For maximum
compatibility, all implementations SHOULD be prepared to handle
potentially extraneous certificates and arbitrary orderings from any
TLS version, with the exception of the end-entity certificate which
MUST be first.
(And the same is true in the other direction for client certificates, but they are used only very rarely, while server certificates almost always are.)
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
I recently purchased a free SSL certificate from Comodo. It came with a certification authority bundle file that contains all of the intermediate certificates as well as the root certificate. When I run the command "openssl s_client -connect www.mydomain.com:443 -showcerts" it shows a certificate path that looks like this:
depth=4 C = SE, O = AddTrust AB, OU = AddTrust External TTP Network, CN = AddTrust External CA Root
verify return:1
depth=3 C = US, ST = UT, L = Salt Lake City, O = The USERTRUST Network, OU = http://www.usertrust.com, CN = UTN-USERFirst-Hardware
verify return:1
depth=2 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO Certification Authority
verify return:1
depth=1 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = EssentialSSL CA
verify return:1
depth=0 OU = Domain Control Validated, OU = Free SSL, CN = www.mydomain.com
verify return:1
However, when I go to www.mydomain.com in any browser and look at the certificate presented by the server, it shows the following certificate path (taken from IE9 certificate window):
COMODO
EssentialSSL CA
www.mydomain.com
Notice that there are fewer certificates in the chain (depth of 2 versus 4 from the openssl command), and that the root certificate is the COMODO certificate as opposed to the AddTrust External CA Root certificate. Can someone explain why the browser shows a different path than the openssl command?
Note, in both cases the certificate chain presented by the server passes validation (verify result 0 from openssl, no warnings in the browser).
IE9 has the comodo ca as a trusted authority in its trust chain, and therefore doesn't show the signers of the comodo ca.
OpenSSL s_client -showcerts shows the whole certificate chain.