Short version: I'm seeing an SSL certificate chain that's different based on how I access the https server. What's going on, and how do I verify the certificate under these circumstances?
Slightly longer version:
I'm trying to use libcurl to verify the certificate of an SSL connection. The server I'm connecting to is Amazon S3.
When I access Amazon S3 in Firefox, I get this certificate chain:
VeriSign Class 3 Public Primary Certification Authority - G5
Serial Number: 18:DA:D1:9E:26:7D:E8:BB:4A:21:58:CD:CC:6B:3B:4A
VeriSign Class 3 Secure Server CA - G3
Serial Number: 6E:CC:7A:A5:A7:03:20:09:B8:CE:BC:F4:E9:52:D4:91
*.s3.amazonaws.com
Serial Number: 43:FB:BA:C2:66:27:E0:97:1E:1C:11:E0:30:C3:6B:66
When I access the same URL via the OpenSSL command line tool, I get this certificate chain:
*.s3.amazonaws.com
Serial number: 43fbbac26627e0971e1c11e030c36b66
VeriSign Class 3 Secure Server CA - G3
Serial number: 6ecc7aa5a7032009b8cebcf4e952d491
VeriSign Class 3 Public Primary Certification Authority - G5
Serial number: 35973187f3873a07327ece580c9b7eda
The "*.s3.amazonaws.com" and "VeriSign Class 3 Secure Server CA - G3" certificates appear to be the same, but the one after that is different! It's named "VeriSign Class 3 Public Primary Certification Authority - G5" in both chains, but the certificate serial number is different. (Other information is different as well; let me know if you want a longer dump.)
I believe that this difference is the reason that I can't get libcurl to verify the SSL certificate. The certificate with serial number "18:DA:D1..." is in my CACERT.PEM file, but the certificate with serial number "35:97:31..." is not.
Obviously, the simple fix would be to add certificate "35:97:31..." to my CACERT.PEM file, but I want to make the right change here, not just a quick fix.
What does this difference in certificate chains signify?
Is it even possible for a SSL server to return different certificate chains based on the client (Firefox vs. OpenSSL/libcurl)?
How should I get libcurl to verify this SSL certificate?
Really long version and background information:
I'm using libcurl with OpenSSL to download from Amazon S3. Libcurl is returning "SSL certificate problem: unable to get local issuer certificate", which I know means that the root certificate isn't listed in my CACERT.PEM file. (I'm using the one downloaded from curl's website, which is converted from Mozilla's certificate store.) I am able to verify certificates on other SSL connections, so I know my libcurl setup is correct.
To see what was going on, and why the certificate didn't verify, I pulled up the same URL in Firefox. Firefox didn't show any SSL certificate warnings. Here's the certification path Firefox shows. The root certificate, "VeriSign Class 3 Public Primary Certification Authority - G5", is listed in my CACERT.PEM file, and the serial number of the certificate matches what's shown in the screenshot.
Here's the serial numbers of all three certificates in the chain:
VeriSign Class 3 Public Primary Certification Authority - G5
Serial Number: 18:DA:D1:9E:26:7D:E8:BB:4A:21:58:CD:CC:6B:3B:4A
VeriSign Class 3 Secure Server CA - G3
Serial Number: 6E:CC:7A:A5:A7:03:20:09:B8:CE:BC:F4:E9:52:D4:91
*.s3.amazonaws.com
Serial Number: 43:FB:BA:C2:66:27:E0:97:1E:1C:11:E0:30:C3:6B:66
On a different platform (different OS, different version of OpenSSL, etc.), I tried accessing the same URL using the OpenSSL command line tool, and I got a different certification path!
$ openssl s_client -showcerts -connect stackoverflowtest.s3.amazonaws.com:443
CONNECTED(00000003)
depth=3 C = US, O = "VeriSign, Inc.", OU = Class 3 Public Primary Certification Authority
verify return:1
depth=2 C = US, O = "VeriSign, Inc.", OU = VeriSign Trust Network, OU = "(c) 2006 VeriSign, Inc. - For authorized use only", CN = VeriSign Class 3 Public Primary Certification Authority - G5
verify return:1
depth=1 C = US, O = "VeriSign, Inc.", OU = VeriSign Trust Network, OU = Terms of use at https://www.verisign.com/rpa (c)10, CN = VeriSign Class 3 Secure Server CA - G3
verify return:1
depth=0 C = US, ST = Washington, L = Seattle, O = Amazon.com Inc., OU = S3-A, CN = *.s3.amazonaws.com
verify return:1
---
Certificate chain
0 s:/C=US/ST=Washington/L=Seattle/O=Amazon.com Inc./OU=S3-A/CN=*.s3.amazonaws.com
i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
-----BEGIN CERTIFICATE-----
MIIFOTCCBCGgAwIBAgIQQ/u6wmYn4JceHBHgMMNrZjANBgkqhkiG9w0BAQUFADCB
tTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2Ug
YXQgaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDEvMC0GA1UEAxMm
VmVyaVNpZ24gQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzMwHhcNMTQwNjI1
MDAwMDAwWhcNMTUwNjA1MjM1OTU5WjB6MQswCQYDVQQGEwJVUzETMBEGA1UECBMK
V2FzaGluZ3RvbjEQMA4GA1UEBxQHU2VhdHRsZTEYMBYGA1UEChQPQW1hem9uLmNv
bSBJbmMuMQ0wCwYDVQQLFARTMy1BMRswGQYDVQQDFBIqLnMzLmFtYXpvbmF3cy5j
b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+mjGH4PrN0TDNmTsF
my2fHpHpTuySRwBMe/nuGIkL3cvKIxFKLHvkK9kx1UpO0skdQTdCY55LywhubLNO
fD19IzJdoRGdlqgkDAYC8vz3LRYj8WWsGnROfS/YFtgj25YaHPnsNp6lWrff4/qi
ctbojJpMxm+9Q0A4nTzrZymHEUkRbx6AVVUBVKH3uZi/w0aV+i4cp2bs+CYIK3DL
Qp584DJ9bOImgUhDfz19+Wtv64zIezE0Uz9eOkqgQ1X//XumyZWyD6N6+h/XqTnc
YTvIer/s83T/IngGMbfPRqjpQCay6ySXCNbJ5izMgo+gwN84t7JhaI+EYcxf1dDN
w0mXAgMBAAGjggF9MIIBeTAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIFoDAdBgNV
HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwZQYDVR0gBF4wXDBaBgpghkgBhvhF
AQc2MEwwIwYIKwYBBQUHAgEWF2h0dHBzOi8vZC5zeW1jYi5jb20vY3BzMCUGCCsG
AQUFBwICMBkaF2h0dHBzOi8vZC5zeW1jYi5jb20vcnBhMB8GA1UdIwQYMBaAFA1E
XBZTRMGCfh0gqyX0AWPYvnmlMCsGA1UdHwQkMCIwIKAeoByGGmh0dHA6Ly9zZC5z
eW1jYi5jb20vc2QuY3JsMFcGCCsGAQUFBwEBBEswSTAfBggrBgEFBQcwAYYTaHR0
cDovL3NkLnN5bWNkLmNvbTAmBggrBgEFBQcwAoYaaHR0cDovL3NkLnN5bWNiLmNv
bS9zZC5jcnQwLwYDVR0RBCgwJoISKi5zMy5hbWF6b25hd3MuY29tghBzMy5hbWF6
b25hd3MuY29tMA0GCSqGSIb3DQEBBQUAA4IBAQBlXrn1FTPjVIFbcuQNbBesrAMI
NV4L7jS1mobEwFrb7UrqZ7kHvuvoR/BpDygATyqLvPihs7nUc2TUHsw/41EAHKoq
QBVfRTOH0yWaTC6SYSx7fiElL+k55Pvrz4+7gLRy5zUlVX3iUw93zr95ka/LPuCL
7PQOFPeQDOgveDjcSNVtLcTfQfvog/rMSu/4XPFHu7zaZwUEurt9CzLeVbdB6O25
bHuHTaZLP0wmjCIbwgXu8bFWqOTnAjG70EtYrbIiQhl/ISJU6HioFzLiy5Ibp07r
RbV4ir5EI2EPKxIy30YDvpCQ0WQvYLWFV0qQOuOXkMC2M2IsBmVn2/9GQ7eP
-----END CERTIFICATE-----
1 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
-----BEGIN CERTIFICATE-----
MIIF7DCCBNSgAwIBAgIQbsx6pacDIAm4zrz06VLUkTANBgkqhkiG9w0BAQUFADCB
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
aG9yaXR5IC0gRzUwHhcNMTAwMjA4MDAwMDAwWhcNMjAwMjA3MjM1OTU5WjCBtTEL
MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQg
aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDEvMC0GA1UEAxMmVmVy
aVNpZ24gQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzMwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQCxh4QfwgxF9byrJZenraI+nLr2wTm4i8rCrFbG
5btljkRPTc5v7QlK1K9OEJxoiy6Ve4mbE8riNDTB81vzSXtig0iBdNGIeGwCU/m8
f0MmV1gzgzszChew0E6RJK2GfWQS3HRKNKEdCuqWHQsV/KNLO85jiND4LQyUhhDK
tpo9yus3nABINYYpUHjoRWPNGUFP9ZXse5jUxHGzUL4os4+guVOc9cosI6n9FAbo
GLSa6Dxugf3kzTU2s1HTaewSulZub5tXxYsU5w7HnO1KVGrJTcW/EbGuHGeBy0RV
M5l/JJs/U0V/hhrzPPptf4H1uErT9YU3HLWm0AnkGHs4TvoPAgMBAAGjggHfMIIB
2zA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLnZlcmlz
aWduLmNvbTASBgNVHRMBAf8ECDAGAQH/AgEAMHAGA1UdIARpMGcwZQYLYIZIAYb4
RQEHFwMwVjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL2Nw
czAqBggrBgEFBQcCAjAeGhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhMDQG
A1UdHwQtMCswKaAnoCWGI2h0dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMtZzUu
Y3JsMA4GA1UdDwEB/wQEAwIBBjBtBggrBgEFBQcBDARhMF+hXaBbMFkwVzBVFglp
bWFnZS9naWYwITAfMAcGBSsOAwIaBBSP5dMahqyNjmvDz4Bq1EgYLHsZLjAlFiNo
dHRwOi8vbG9nby52ZXJpc2lnbi5jb20vdnNsb2dvLmdpZjAoBgNVHREEITAfpB0w
GzEZMBcGA1UEAxMQVmVyaVNpZ25NUEtJLTItNjAdBgNVHQ4EFgQUDURcFlNEwYJ+
HSCrJfQBY9i+eaUwHwYDVR0jBBgwFoAUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwDQYJ
KoZIhvcNAQEFBQADggEBAAyDJO/dwwzZWJz+NrbrioBL0aP3nfPMU++CnqOh5pfB
WJ11bOAdG0z60cEtBcDqbrIicFXZIDNAMwfCZYP6j0M3m+oOmmxw7vacgDvZN/R6
bezQGH1JSsqZxxkoor7YdyT3hSaGbYcFQEFn0Sc67dxIHSLNCwuLvPSxe/20majp
dirhGi2HbnTTiN0eIsbfFrYrghQKlFzyUOyvzv9iNw2tZdMGQVPtAhTItVgooazg
W+yzf5VK+wPIrSbb5mZ4EkrZn0L74ZjmQoObj49nJOhhGbXdzbULJgWOw27EyHW4
Rs/iGAZeqa6ogZpHFt4MKGwlJ7net4RYxh84HqTEy2Y=
-----END CERTIFICATE-----
2 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
-----BEGIN CERTIFICATE-----
MIIExjCCBC+gAwIBAgIQNZcxh/OHOgcyfs5YDJt+2jANBgkqhkiG9w0BAQUFADBf
MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
HhcNMDYxMTA4MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx
FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZv
ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz
IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8
RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs70Pb
ZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP6HBR
TdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNH
iDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMB
AAGjggGRMIIBjTAPBgNVHRMBAf8EBTADAQH/MDEGA1UdHwQqMCgwJqAkoCKGIGh0
dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4GA1UdDwEB/wQEAwIBBjA9
BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVy
aXNpZ24uY29tL2NwczAdBgNVHQ4EFgQUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwNAYD
VR0lBC0wKwYJYIZIAYb4QgQBBgpghkgBhvhFAQgBBggrBgEFBQcDAQYIKwYBBQUH
AwIwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUr
DgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNp
Z24uY29tL3ZzbG9nby5naWYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhho
dHRwOi8vb2NzcC52ZXJpc2lnbi5jb20wDQYJKoZIhvcNAQEFBQADgYEADyWuSO0b
M4VMDLXC1/5N1oMoTEFlYAALd0hxgv5/21oOIMzS6ke8ZEJhRDR0MIGBJopK90Rd
fjSAqLiD4gnXbSPdie0oCL1jWhFXCMSe2uJoKK/dUDzsgiHYAMJVRFBwQa2DF3m6
CPMr3u00HUSe0gST9MsFFy0JLS1j7/YmC3s=
-----END CERTIFICATE-----
---
Server certificate
subject=/C=US/ST=Washington/L=Seattle/O=Amazon.com Inc./OU=S3-A/CN=*.s3.amazonaws.com
issuer=/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
---
No client certificate CA names sent
Server Temp Key: ECDH, prime256v1, 256 bits
---
SSL handshake has read 4624 bytes and written 399 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES128-SHA
Session-ID: B642ED1E6FE32F7A374B5A62847DEC63C2F37DCA7A18FD669B8F0FCDC98C49BF
Session-ID-ctx:
Master-Key: EE2D31F43D341A0895B36E0BCCE7557B221F1469AC1B7B0BA22D843C75F25F949822B0D0E22E967A1F373F034E9624E4
Key-Arg : None
Krb5 Principal: None
PSK identity: None
PSK identity hint: None
Start Time: 1430871883
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
closed
When I decode the certificates it lists, I get a different certificate chain than Firefox gave me.
*.s3.amazonaws.com
Serial number: 43fbbac26627e0971e1c11e030c36b66
VeriSign Class 3 Secure Server CA - G3
Serial number: 6ecc7aa5a7032009b8cebcf4e952d491
VeriSign Class 3 Public Primary Certification Authority - G5
Serial number: 35973187f3873a07327ece580c9b7eda
The cert in use, and its immediate parent cert are the same, but the next one up has the same name, but a different serial number.
Here's my version information:
> curld --version
curl 7.40.0 (i386-pc-win32ce) libcurl/7.40.0 OpenSSL/1.0.1e
Protocols: dict ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps tftp
Features: NTLM SSL
> curld --version
curl 7.40.0 (i386-pc-win32) libcurl/7.40.0 OpenSSL/1.0.1c
Protocols: dict ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps tftp
Features: AsynchDNS Largefile NTLM SSL
$ cat /etc/redhat-release
CentOS release 6.6 (Final)
$ openssl version
OpenSSL 1.0.1e-fips 11 Feb 2013
$ yum list openssl
Installed Packages
openssl.x86_64 1.0.1e-30.el6.8 #updates
Firefox: 37.0.2, running on Windows 7 x64.
I did review this question: SSL Certificate - Certification Path in browser different from Certificate Chain File, but my problem appears to be different: In that case, the certificate chain in the OpenSSL command line tool went 1 - 2 - 3 - 4, and in IE it was 1 - 2 - 3. Because IE considered "3" to be a root certificate, the chain stopped early. In my case, Firefox is reporting 1 - 2 - 3, and OpenSSL is reporting 1 - 2 - 4; The chain is different.
The server sends the same chain certificates to firefox and s_client:
CN=.s3.amazonaws.com SAN=DNS:.s3.amazonaws.com,DNS:s3.amazonaws.com
CN=VeriSign Class 3 Secure Server CA - G3
CN=VeriSign Class 3 Public Primary Certification Authority - G5
But the way the certificates will be verified differs depending on the SSL stack and the trusted root certificates of the client. And in case of curl you run into an old OpenSSL validation problem. Details:
Firefox has a trusted root certificate similar to the certificate#3 send by the client. This means that it is a different certificate but it contains the same public key, so that the signature for certificate#2 is still valid. Since Firefox underlying TLS stack (NSS) thus has found a usable trust anchor it will consider the chain as verified and ignore the certificate#3 sent by the server.
But the version of curl you have uses OpenSSL as the TLS library. OpenSSL tries to get the longest match, that it it will try to find a trusted root certificate which signed certificate#3. If it fails it will not try with a shorter trust chain but simply fail. This is a long-standing bug which is the cause of many many strange problems like this and it looks like the issue is for now only fixed in the latest development branch (not released).
The solution is probably to use a CA store for curl which still includes the old deprecated 1024bit CA's so that it contains the trust anchor "/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority"
For even more details about this problem see
more detailed writeup
OpenSSL bug #2732 from 2012
workarounds with patch
newer Bug reports which indicate a bug fix for current development branch (after 1.0.2)
Related
As a followup to this original question:
Unable to get certificate locally
I did solve the original problem as jww answered.
And I have now followed the same steps of importing the certificate chain for our company site, from "comodo". I simply added them to the file which I originally used with the "google.com" root certs.
Now, although this still works fine with "google", when I connect to our company website, I still get the error code 20 on the SSL_get_verify_result() call.
Is this a result of our using a "wildcard" certificate? i.e.: *.domain.com.
The version of OpenSSL I am currently using is 1.0.1g.
I don't see any other differences from my perspective.
Thanks for any advice.
----- Updated ------
First, let me comment that I am not mentioning our domain, and not posting too much material from the OpenSSL command, as I am not familiar enough with what should be kept confidential.
What I did was combine the base64 encoded certificates into one big file, as the previous post instructed. And I obtained them via the browser "export" utility in the same manner for both. That means the certificates that we use, as well as the google certificates from my previous post are all concatenated. Specifically it looks like this now:
Our Company Cert
-----BEGIN CERTIFICATE-----
....
-----END CERTIFICATE-----
and it is signed by these guys - ComodoRSA
-----BEGIN CERTIFICATE-----
....
-----END CERTIFICATE-----
and that is signed at the root here - ComodoRoot
-----BEGIN CERTIFICATE-----
....
-----END CERTIFICATE-----
and this is the GOOGLE G3 who signed the "www.google.com"
-----BEGIN CERTIFICATE-----
....
-----END CERTIFICATE-----
and the GOOGLE G3 is signed by this one - globalSign
-----BEGIN CERTIFICATE-----
....
-----END CERTIFICATE-----
Then the code segment I use to test looks like this:
strcpy(host,"our.domain.com");
// strcpy(host,"www.google.com");
/* Build our SSL context*/
ctx = initialize_ctx(KEYFILE,NULL);
/* Connect the TCP socket*/
sock = tcp_connect(host,port);
And then later...
result = SSL_get_verify_result(ssl);
switch(result) {
case X509_V_ERR_CERT_HAS_EXPIRED : break;
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN : break;
case X509_V_OK : break;
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT : break;
default :puts("Certificate doesn't verify");
}
Simply put, this same code, using this same CRT file, does not give me a "20" error when I use the www.google.com" host, but does give me an error "20" when I use our server. The extent of the test involves changing that commented out name of the host.
Connections to the HTTPS server with commercial clients (Chrome, IE, FF...) have no errors.
As for the comment that recommended the command, I get the following (hopefully I pasted the necessary information):
For Google:
depth=2 OU = GlobalSign Root CA - R2, O = GlobalSign, CN = GlobalSign verify return:1
depth=1 C = US, O = Google Trust Services, CN = Google Internet Authority G3
verify return:1
depth=0 C = US, ST = California, L = Mountain View, O = Google LLC, CN = www.google.com
verify return:1
read:errno=0
---- other stuff ----
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=Google Internet Authority G3
1 s:/C=US/O=Google Trust Services/CN=Google Internet Authority G3
i:/OU=GlobalSign Root CA - R2/O=GlobalSign/CN=GlobalSign
---
And for our domain, I am getting the following (company specifics hidden):
depth=2 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO RSA Certification Authority
verify error:num=20:unable to get local issuer certificate
verify return:0
read:errno=10054
---- other stuff ---
Certificate chain
0 s:/C=US/postalCode=00000/ST=IL/L=city/street=main/O=company./OU=PremiumSSL Wildcard/CN=*.domain.com
i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Organization Validation Secure Server CA
1 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Organization Validation Secure Server CA
i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
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
----
Now, the chain is that our certificate is signed by "ComodoRSA", and that it is signed by "ComodoRoot".
However, as I originally indicated, our certificate is a "wildcard" certificate, where the google certificate isn't.
So that was my question: Is there a problem using wild card certificates with the version of openSSL 1.0.1g?
--- EDIT2 ----
I am adding more content to the post, so I can include an image from the browser.
Our certificate is on a live web site, and is not self signed.
I am checking the common name in a portion of the code not shown. In this post I am only hoping for advice on this error.
I found a site that uses the same chain we use: DrudgeReport.com
I simply extracted the certificates with the browser, and saved them into a file. This was identical to the steps I used form the google.com site. (View certificate and copy to file)
The result from Drudge is error 19 which is "self signed", not error 20 which was my error. The root level (comodo secure) is the same when I copy to file from either site (as I would expect).
As I learn this from your comments and the link, I see the next intermediate question is: how do I know what certificates are being sent by the server, and which were in the store? Then I would know which to import. What, if any, is the indicator from the openssl command output you provided?
The educational material on openssl and this is rather difficult to find. Just a lot of doc, which to a novice such as myself, is rather circular in it's definitions.
Thanks for your feedback so far.
So that was my question: Is there a problem using wild card certificates with the version of openSSL 1.0.1g?
It is not a problem of wildcard certificates. In fact, your code does not even check the subject of the certificate at all (i.e. your code is insecure), it mostly checks the certificate chain, expiration and purpose of the certificate. And the error message from openssl s_client clearly points out the problem:
verify error:num=20:unable to get local issuer certificate
Thus, the problem is not the certificate subject but that it cannot find a local trust anchor. Looking at the certificate chain provided by your server gives the following certificate chain:
[1] CN=*.domain.com, issued by [2]
[2] CN=COMODO RSA Organization Validation Secure Server CA, issued by [3]
[3] CN=COMODO RSA Certification Authority, issued by [ROOT]
The expected [ROOT] is "CN=AddTrust External CA Root" - only this CA is not in your list of trusted root CA.
While you give not the detailed names of the certificates you have in your local trust store my guess is that the CA you call "ComodoRoot" is similar to what I have as "[3] CN=COMODO RSA Certification Authority" in the list. Only, in your trust store it is likely the self-signed version of the certificate while in the certificate chain provided by the server it is a certificate issued by "[ROOT] CN=AddTrust External CA Root". Both certificates have the same public and private key which means that the signatures in certificate chain can be successfully validated with both.
But, the old (and long unsupported version) of OpenSSL you are using can not properly deal with this kind of situation. It will follow the certificate chain send by the server and then expect the last certificate in the chain signed by on of the certificates in your trust store. If this fails it will not check if a shorter chain might be validated successfully instead.
This means it will succeed if you either have "CN=AddTrust External CA Root" in your trust store or if the server sends a short chain which ends with "[2] CN=COMODO RSA Organization Validation Secure Server CA" since then it will find the issuer for this (your "ComodoRoot", i.e. "CN=COMODO RSA Certification Authority") in your trust store.
For a more detailed explanation of this problem see this answer at stackoverflow.com or this article. Note that there is no way to fix this in code with OpenSSL 1.0.1 - you need to either add the missing certificate to your trust store or make changes to the certificate chain send by the server.
I need to connect to some https://website.com. So, the site is available via VPN. I'm connected to the VPN and I can open the site in browser.
I've downloaded certificates from browser:
Then I cat both file into one certificate.pem
But when I'm trying to execute command
openssl s_client -connect website.com:443 -CAfile /path/to/certificate.pem
When I execute it in a terminal I have an error.
CONNECTED(00000003)
depth=1 /C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA
verify error:num=2:unable to get issuer certificate
issuer= /C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA
verify return:0
---
Certificate chain
0 s:/C=AU/ST=Wales/L=Place/O=Company
Ltd/OU=D&D/CN=website.com
i:/C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA
---
Server certificate
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
subject=/C=AU/ST=Wales/L=Place/O=Company
Ltd/OU=D&D/CN=website.com
issuer=/C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA
---
No client certificate CA names sent
---
SSL handshake has read 2034 bytes and written 328 bytes
---
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES128-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : DHE-RSA-AES128-SHA
Session-ID: 1533BA958D51B9FEAE4C3534F4A417A5896ED17DCAAE83E89E6C2A7F615A583A
Session-ID-ctx:
Master-Key: 5CF D4ACA242B602AAFSDF1234X23E99BD4C62862888947FACFF0E7503BA34C2DD0EC193FA525204A539
Key-Arg : None
Start Time: 1509781851
Timeout : 300 (sec)
Verify return code: 0 (ok)
openssl historically and by default validates a certificate chain only if it ends at a root. Having the server aka end-entity or leaf cert in the truststore is useless, and the intermediate(s) should not be needed because RFCs require the server to send it(them), but your server is apparently defective or misconfigured because it does not. Thus for your server having the intermediate and root, but not the server cert, in the file used for -CAfile will work, assuming they are in PEM format.
Alternatively, recent (and supported) releases 1.0.2 and 1.1.0 add an option -partial_chain. If specified, this validates if the truststore has any anchor, not just a root. For your server, having either the server cert or the intermediate in the file used for -CAfile is sufficient, again in PEM format.
These cases are described on the man page for verify(1) which is referenced from the man page for s_client(1). Some systems may make the section 1ssl or similar, and if your system is not properly installed or is Windows, they are on the web here.
Remember that openssl historically and by default does not check the server name in the cert. 1.1.0 has new options -verify_name and -verify_hostname that do so. These are described on the man page for verify and referenced on that for s_client.
Also remember that many servers, though apparently not yours, now use Server Name Indication (SNI) extension to support multiple 'virtual' hosts with different certificates, and will either give a wrong cert or reject or fail the connection if SNI is missing. openssl s_client does not send SNI by default, but the option -servername does so; this is described on the man page. Update: OpenSSL 1.1.1 in 2018 s_client now does send SNI by default.
In general looking at the man pages for a program tells you useful information about how the program works and how to use it, and is recommended.
Especially since this is not a programming or development question, and really off-topic for StackOverflow; I would try to propose migration to SuperUser or ServerFault, but they already have numerous dupes.
This error means that openssl is looking for the issuer certificate with the subject "/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA" but it is not provided in the file /path/to/certificate.pem. Suggest to run "openssl x509 -in /path/to/certificate.pem -text" to see the subject of the certificate in this file - should be different from the requested one.
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.
I have been trying this authentication method for the last 3 days without any success, I am totally new to the field MVC and Identity. I required to do an external authentication for my site with Facebook, Microsoft and Twitter, so I am using Identity 2.0 with MVC 5.
Facebook and Microsoft are already done, but I have trouble doing the same for twitter, because they change their authentication policy last December, so now I require an HTTPS site (which is not the problem) with a Twitter SSL certificate (I have problems with this one).
I am using IIS Express to run my website in development mode.
I already created my app on twitter's developer site, I got my API key, API Secret, Access Token and Access Secret.
I already change my Startup.Auth.cs, to uncomment app.UseTwitterAuthentication with my consumerKey (ApiKey) and consumerSecret (ApiSecret). But when I run my site and try to sign-in with Twitter I keep receiving this error: the remote certificate is invalid according to the validation procedure.
See these discussion issues:
https://dev.twitter.com/discussions/24239
https://dev.twitter.com/docs/security/using-ssl
Reading a little bit about this issue, I know require a Twitter SSL certificate, which I got, but cannot figure out how to use it.
Please, I am desperate and I need a solution to this problem, can anyone enlighten me on how to make this work with VS 2013 and IIS Express? I have no idea what to do.
... now I require an HTTPS site (which is not the problem) with a Twitter SSL certificate (I have problems with this one)
According to Twitter is updating its SSL certificates for api.twitter.com, the signing root is changing to VeriSign Class 3 Public Primary Certification Authority - G5.
It appears it has already changed:
$ openssl s_client -connect api.twitter.com:443
CONNECTED(00000003)
depth=1 C = US, O = "VeriSign, Inc.", OU = VeriSign Trust Network, OU = Terms of use at https://www.verisign.com/rpa (c)10, CN = VeriSign Class 3 Secure Server CA - G3
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
0 s:/C=US/ST=California/L=San Francisco/O=Twitter, Inc./OU=Twitter Security/CN=api.twitter.com
i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
1 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
...
Go to Download Verisign Root Certificates and download VeriSign Class 3 Primary CA - G5. It has thumbprint 4e b6 d5 78 49 9b 1c cf 5f 58 1e ad 56 be 3d 9b 67 44 a5 e5.
After downloading the certificate, import it into the Computer's Root Certificate Authority Store. For instuctions, see Manage Trusted Root Certificates on TechNet. Be sure to follow Adding certificates to the Trusted Root Certification Authorities store for a local computer. You want the local computer, and you want Trusted Root Certification Authorities store.
Once you use the downloaded certificate as a trust anchor, the chain will verify. Notice the -CAfile option, and the Verify return code: 0 (ok) at the end:
$ openssl s_client -connect api.twitter.com:443 -CAfile VeriSign-Class\ 3-Public-Primary-Certification-Authority-G5.pem
CONNECTED(00000003)
depth=2 C = US, O = "VeriSign, Inc.", OU = VeriSign Trust Network, OU = "(c) 2006 VeriSign, Inc. - For authorized use only", CN = VeriSign Class 3 Public Primary Certification Authority - G5
verify return:1
depth=1 C = US, O = "VeriSign, Inc.", OU = VeriSign Trust Network, OU = Terms of use at https://www.verisign.com/rpa (c)10, CN = VeriSign Class 3 Secure Server CA - G3
verify return:1
depth=0 C = US, ST = California, L = San Francisco, O = "Twitter, Inc.", OU = Twitter Security, CN = api.twitter.com
verify return:1
---
Certificate chain
0 s:/C=US/ST=California/L=San Francisco/O=Twitter, Inc./OU=Twitter Security/CN=api.twitter.com
i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
1 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
---
...
Start Time: 1407206791
Timeout : 300 (sec)
Verify return code: 0 (ok)
I have a certificate (for example this one) saved in a local file. Using openssl from the command line, how can I display the entire chain from this certificate to a root CA? I tried:
openssl verify -verbose -purpose sslserver -CApath /etc/ssl/certs InCommonServerCA.txt
and got this confusing output that only seems to show the leaf certificate:
InCommonServerCA.txt: C = US, O = Internet2, OU = InCommon, CN = InCommon Server CA
error 26 at 0 depth lookup:unsupported certificate purpose
OK
Any ideas?
For local certificates you can see the subject and direct issuer using:
openssl x509 -noout -subject -issuer -in test.crt
subject= /C=US/ST=Utah/L=SLC/O=My Organization/CN=my.server.com
issuer= /C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - SHA256 - G2
But that doesn't indicate if the certificate includes any intermediate certificates or the full chain of trust. The verify command you listed will fail if your system cannot validate the chain (example: you are missing an intermediate certificate or the root is not trusted), showing an error message like:
error 20 at 0 depth lookup:unable to get local issuer certificate
If you want to verify each entry in the file, you can use this script to show the chain of trust for a local certificate:
~ % ssl_chain.sh google.crt
0: subject= /C=US/ST=California/L=Mountain View/O=Google Inc/CN=www.google.com
issuer= /C=US/O=Google Inc/CN=Google Internet Authority G2
1: subject= /C=US/O=Google Inc/CN=Google Internet Authority G2
issuer= /C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
2: subject= /C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
issuer= /C=US/O=Equifax/OU=Equifax Secure Certificate Authority
google.crt: OK
If you want to verify the chain and purpose, your openssl command is correct. The "OK" indicates the chain verifies. The error indicates there is an issue with that certificate being used for an sslserver purpose. It looks like your certificate is a CA cert, not a leaf cert.
What kind of chain info are you trying to display? You could look at the subject and issuer fields to show chaining. The verify command you used above proves that the one cert signed the other cert.