Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

I am trying to use curl to connect to a site using HTTPS. But curl is failing to verify the SSL cert.

$ curl --verbose --capath ./certs/ --head https://example.com/
* About to connect() to example.com port 443 (#0)
*   Trying 1.1.1.1... connected
* Connected to example.com (1.1.1.1) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: none
  CApath: ./certs/
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS alert, Server hello (2):
* SSL certificate problem, verify that the CA cert is OK. Details:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
* Closing connection #0
curl: (60) SSL certificate problem, verify that the CA cert is OK. Details:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
More details here: http://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.

I know about the -k option. But I do actually want to verify the cert.

The certs directory has been properly hashed with c_rehash . and it contains:

  • A Verisign intermediate cert
  • Two self-signed certs

The above site should be verified with the Verisign intermediate cert.

When I use the --cacert option instead (and point directly to the Verisign cert) curl is able to verify the SSL cert.

$ curl --verbose --cacert ./certs/verisign-intermediate-ca.crt --head https://example.com/
* About to connect() to example.com port 443 (#0)
*   Trying 1.1.1.1... connected
* Connected to example.com (1.1.1.1) port 443 (#0)
* successfully set certificate verify locations:
*   CAfile: ./certs/verisign-intermediate-ca.crt
  CApath: /etc/ssl/certs
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using RC4-SHA
*     Server certificate:
*    subject: C=US; ST=State; L=City; O=Company; OU=ou1; CN=example.com
*    start date: 2011-04-17 00:00:00 GMT
*    expire date: 2012-04-15 23:59:59 GMT
*    common name: example.com (matched)
*    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
*    SSL certificate verify ok.
> HEAD / HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-pc-linux-gnu) libcurl/7.19.7 OpenSSL/0.9.8k zlib/1.2.3.3 libidn/1.15
> Host: example.com
> Accept: */*
> 
< HTTP/1.1 404 Not Found
HTTP/1.1 404 Not Found
< Cache-Control: must-revalidate,no-cache,no-store
Cache-Control: must-revalidate,no-cache,no-store
< Content-Type: text/html;charset=ISO-8859-1
Content-Type: text/html;charset=ISO-8859-1
< Content-Length: 1267
Content-Length: 1267
< Server: Jetty(7.2.2.v20101205)
Server: Jetty(7.2.2.v20101205)

< 
* Connection #0 to host example.com left intact
* Closing connection #0
* SSLv3, TLS alert, Client hello (1):

In addition, if I try hitting one of the sites using a self signed cert and the --capath option, it also works. (Let me know if I should post an example of that.) This implies that curl is finding the cert directory, and it is properly hash.

Finally, I am able to verify the SSL cert with openssl, using its -CApath option.

$ openssl s_client -CApath ./certs/ -connect example.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=State/L=City/O=Company/OU=ou1/CN=example.com
verify return:1
---
Certificate chain
 0 s:/C=US/ST=State/L=City/O=Company/OU=ou1/CN=example.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
---
Server certificate
-----BEGIN CERTIFICATE-----
<cert removed>
-----END CERTIFICATE-----
subject=/C=US/ST=State/L=City/O=Company/OU=ou1/CN=example.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
---
SSL handshake has read 1563 bytes and written 435 bytes
---
New, TLSv1/SSLv3, Cipher is RC4-SHA
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : RC4-SHA
    Session-ID: D65C4C6D52E183BF1E7543DA6D6A74EDD7D6E98EB7BD4D48450885188B127717
    Session-ID-ctx: 
    Master-Key: 253D4A3477FDED5FD1353D16C1F65CFCBFD78276B6DA1A078F19A51E9F79F7DAB4C7C98E5B8F308FC89C777519C887E2
    Key-Arg   : None
    Start Time: 1303258052
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---
QUIT
DONE

How can I get curl to verify this cert using the --capath option?

share|improve this question
    
Just to clarify, this is a server I (somewhat) control. It is entirely possible that the SSL cert was installed incorrectly. That said, Firefox and Chromium don't show any SSL warnings. –  phylae Apr 20 '11 at 0:21
    
I found the answer. The intermediate certificate file also included a root certificate. (It had two -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- bits.) It seems that openssl, when used directly on the command line, can handle this. But curl (and some other programs which use openssl, I tried Ruby 1.9.2) can't. I removed this extra root certificate and curl works now too. –  phylae Apr 20 '11 at 4:14
    
To further clarify, it seems that the server also did not have the intermediate certificate properly installed. Because it was a Verisign cert, I was able to use this tool to confirm that: knowledge.verisign.com/support/ssl-certificates-support/… –  phylae Apr 21 '11 at 19:34
    
I don't know what was going on for this to work before, but now (even with a properly installed cert on the server and a proper intermediate cert) I'm still getting this exact same problem. –  phylae May 2 '11 at 19:10
    
I found a different root cert (one that was preinstalled on my system) and that seems to have addressed the issue. I wonder if openssl was using my system-installed certs in addition to the cert directory that I specified on the command line, while curl was using only the specified cert directory. –  phylae May 2 '11 at 19:48
show 2 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.