Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use SSL_CERT_FILE for OpenSSL Windows (OpenSSL 1.0.1c)

How (if at all) can one define a single trusted certificate file for OpenSSL on Windows (Win-7, OpenSSL 1.0.1c) using the SSL_CERT_FILE environment variable?

Various research led me to download the December '12 version of Mozilla's trusted certificates in PEM format, from here: http://curl.haxx.se/docs/caextract.html This contains all of the certs and assorted related info concatenated together into one file.

I've found various references to the usage of the environment variables SSL_CERT_DIR and SSL_CERT_FILE with respect to other products which rely on OpenSSL. For instance, http://lynx.isc.org/current/README.sslcerts indicates that one can set both of these, and the underlying OpenSSL libraries will use them. However, that hasn't been my experience with the OpenSSL tool itself.

I was able to use SSL_CERT_DIR successfully, but with great pain, as follows. I exported (from IE 8) the certificate from www.wellsfargo.com (selected randomly), along with the two certificates in its trust chain, both from Verisign. I put each of the two Verisign certs in a directory C:\ca_stuff, and for each, generated a hash thus

openssl x509 -hash -noout -in "Verisign Intl Server.cer"

which had output a302054c, and from this created a link thus

mklink a302054c.0 "Verisign Intl Server.cer"

and likewise for the other Verisign cert. I then put the Wells Fargo cert. in a different directory, and was able to verify it successfully using

set SSL_CERT_DIR=C:\ca_stuff openssl verify "Wells Fargo web.cer"

However, after defining SSL_CERT_FILE, pointing to the downloaded cacert.pem downloaded from the cURL site, the same command failed. It did so with and without having SSL_CERT_DIR defined. I verified that the necessary CA certificates were in the bundle, and confirmed their serial numbers matched those I'd manually extracted from IE.

It seems like an arduous process to manually extract each certificate and put it in its own file with a hash link pointing at it. If this were Unix, I could automate it, but on Windows... I've apparently misunderstood something about how to get one big CA cert file working with OpenSSL.

Thank you in advance for any recommendations, insights and assistance.

like image 745
Jerry Oberle Avatar asked Jan 11 '13 20:01

Jerry Oberle


People also ask

How do I find OpenSSL on Windows?

Run OpenSSLOpen the command prompt using 'Windows' + 'r' then type 'cmd' to open command prompt. Type openssl version command on CLI to ensure OpenSSL is installed and configured on your Windows machine. You should see the version information if OpenSSL is configured correctly.

What is Ssl_cert_file?

Specifies the name of the file containing the SSL server certificate. Relative paths are relative to the data directory. This parameter can only be set in the postgresql. conf file or on the server command line. The default is server.


1 Answers

How (if at all) can one define a single trusted certificate file for OpenSSL

The CAFile is simply a concatenation of self-signed certificates that you trust and want to use. If you only want to trust one, then there should only be one in the CA File.

I prefer the PEM encoding because its easier to inspect with a text editor (-----BEGIN CERTIFICATE----- and -----END CERTIFICATE-----). For example, here's the ca-bundle.pem from Startcom (http://www.startssl.com/certs/):

Image of Startcom's ca-bundle.pem file

So, to create one, just use cat and redirections (or copy and paste):

# Empty my-ca-file.pem
echo "" > my-ca-file.pem
# Add Startcom certs
cat startcom-ca-bundle.pem >> my-ca-file.pem
# Add others as desired
...

Various research led me to download the December '12 version of Mozilla's trusted certificates in PEM format...

Well, that's one of the lists you can use. When you use Mozilla's list, you are saying "I trust Mozilla to do the right thing". Keep in mind Mozilla rewarded Trustwave's bad behavior when Trustwave was caught intercepting SSL/TLS traffic. Even though Trustwave violated at least two inclusion policies, Mozilla continued to include them because Trustwave promised never to do it again. See Remove Trustwave Certificate(s) from trusted root certificates for details.

If you don't trust Mozilla's judgement, then you can use OpenSSL's built-in list at /usr/lib/ssl/certs/ca-certificates.crt, use another list (most major vendors have them), or build your own.

Using a different vendor's list is usually the equivalent of trading the devil you know for the devil you don't know. For example, Apple has a list they use that you can inspect at iOS: List of available trusted root certificates (iOS 7). But Apple's list has lots of problems: http://seclists.org/fulldisclosure/2013/Sep/186 and http://seclists.org/fulldisclosure/2013/Sep/184.

I would recommend building your own list or pinning certificates. Pinning certificates or public keys is better because it neutralizes the systemic problems in SSL/TLS that allowed Trustwave to do what they did. See OWASP's Certificate and Public Key Pinning for details.


... on Windows (Win-7, OpenSSL 1.0.1c) using the SSL_CERT_FILE environment variable?

I don't know how to do it through environmental variables because I don't use them. But there should be no difference between Linux/Unix/OSX/Windows (except, perhaps, the handling of long file names and spaces).

Looking at the OpenSSL sources, you have the following in cryptlib.h:

#define X509_CERT_FILE_EVP       "SSL_CERT_FILE"

x509_def.c uses X509_CERT_FILE_EVP:

const char *X509_get_default_cert_file_env(void)
    { return(X509_CERT_FILE_EVP); }

X509_get_default_cert_file_env is used in by_file.c in by_file_ctrl:

...
switch (cmd)
{
    case X509_L_FILE_LOAD:
        if (argl == X509_FILETYPE_DEFAULT)
        {
            file = (char *)getenv(X509_get_default_cert_file_env());
            if (file)
                ok = (X509_load_cert_crl_file(ctx,file,
                                              X509_FILETYPE_PEM) != 0);

            else
                ok = (X509_load_cert_crl_file(ctx,X509_get_default_cert_file(),
                                              X509_FILETYPE_PEM) != 0);

            if (!ok)
            {
                X509err(X509_F_BY_FILE_CTRL,X509_R_LOADING_DEFAULTS);
            }
        }
        else
        {
            if(argl == X509_FILETYPE_PEM)
                ok = (X509_load_cert_crl_file(ctx,argp,
                                              X509_FILETYPE_PEM) != 0);
            else
                ok = (X509_load_cert_file(ctx,argp,(int)argl) != 0);
        }
        break;
}
return(ok);

So, a concatenation of PEM formats is preferred (required?) when using SSL_CERT_FILE.

Finally, be sure the SSL_CERT_FILE is not being overridden by a configuration file setting. See OpenSSL config(5) for details.


It seems like an arduous process to manually extract each certificate and put it in its own file with a hash link pointing at it.

I don't believe you need to rehash when using SSL_CERT_FILE, -CAfile, or SSL_CTX_load_verify_locations.

I've never rehashed when using -CAfile or SSL_CTX_load_verify_locations, and everything has worked fine. When things break, it usually because (1) the root certificate is not present or trusted; or (2) an intermediate certificate is not present.

For item (2) above, you need the server to send all the required certificates to build the chain. Otherwise, a client won't know where to look to find a missing intermediate certificate. Is a well known problem in PKI called the "Which Directory" problem (the client does not know which X500 directory to search for the missing certificate).


Related, here's how to use them in OpenSSL's s_client. This actually works because pagepeeker.com uses StartCom, and it will fail if you omit the -CAfile option:

$ echo "GET / HTTP\1.1" | openssl s_client -connect api.pagepeeker.com:443 -CAfile startcom-ca-bundle.pem
CONNECTED(00000003)
depth=2 C = IL, O = StartCom Ltd., OU = Secure Digital Certificate Signing, CN = StartCom Certification Authority
verify error:num=19:self signed certificate in certificate chain
verify return:0
---
Certificate chain
 0 s:/description=8CTO6gSuxeRRsIXl/C=RO/CN=api.pagepeeker.com/[email protected]
   i:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Class 1 Primary Intermediate Server CA
 1 s:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Class 1 Primary Intermediate Server CA
   i:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority
 2 s:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority
   i:/C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIGZTCCBU2gAwIBAgIDCJkoMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ
TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0
...

And related code when doing C programming. This is part of the code I use to setup a SSL/TLS connection (in addition to public key pinning):

int ret = 0;
unsigned long ssl_err = 0;
SSL_CTX* ctx = NULL;

do
{
    ret = SSL_library_init();
    ssl_err = ERR_get_error();
    if(!(1 == ret))
    {
        display_error("SSL_library_init", ssl_err);
        break; /* failed */
    }

    /* SSLv23_method() is 'everything' */
    const SSL_METHOD* method = SSLv23_method();
    ssl_err = ERR_get_error();
    if(!(NULL != method))
    {
        display_error("SSLv23_method", ssl_err);
        break; /* failed */
    }

    /* http://www.openssl.org/docs/ssl/ctx_new.html */
    ctx = SSL_CTX_new(method);
    ssl_err = ERR_get_error();
    if(!(ctx != NULL))
    {
        display_error("SSL_CTX_new", ssl_err);
        break; /* failed */
    }

    /* Enable standard certificate validation and our callback */
    /* https://www.openssl.org/docs/ssl/ctx_set_verify.html */
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, my_verify_cb);
    /* Cannot fail ??? */

    /* Remove most egregious */
    const long flags = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION;
    long old_opts = SSL_CTX_set_options(ctx, flags);
    UNUSED(old_opts);

    /* http://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html */
    ret = SSL_CTX_load_verify_locations(ctx, "startcom-ca-bundle.pem", NULL);
    ssl_err = ERR_get_error();
    if(!(1 == ret))
        display_warning("SSL_CTX_load_verify_locations", ssl_err);

} while(0);

// Use context
return ctx;

Its OK if SSL_CTX_load_verify_locations fails. It means you won't trust anything, so you fail closed or shut.

like image 100
jww Avatar answered Oct 22 '22 08:10

jww