如何在Windows上使用SSL_CERT_FILE来处理OpenSSL(OpenSSL 1.0.1c)?

6
如何(如果有必要)使用SSL_CERT_FILE环境变量在Windows上(Win-7,OpenSSL 1.0.1c)为OpenSSL定义单个可信证书文件?各种研究导致我从这里下载了Mozilla的PEM格式的受信任证书的2012年12月版本:http://curl.haxx.se/docs/caextract.html。这包含了所有证书和相关信息拼接在一起的一个文件。我发现各种参考资料涉及到使用SSL_CERT_DIR和SSL_CERT_FILE环境变量来处理依赖于OpenSSL的其他产品。例如,http://lynx.isc.org/current/README.sslcerts指出可以设置这两个变量,底层的OpenSSL库将使用它们。然而,对于OpenSSL工具本身,我的经验并非如此。
我成功地使用了SSL_CERT_DIR,但是很痛苦,具体步骤如下。我从www.wellsfargo.com(随机选择)中导出证书以及它的信任链中来自Verisign的两个证书。我将这两个Verisign证书放在一个名为C:\ca_stuff的目录中,并为每个证书生成了一个哈希值,具体命令如下:
openssl x509 -hash -noout -in "Verisign Intl Server.cer"
该命令输出a302054c,然后我创建了一个链接,如下所示:
mklink a302054c.0 "Verisign Intl Server.cer"
另一个Verisign证书同理。然后,我将Wells Fargo证书放在另一个目录中,并使用以下命令成功验证了它:
set SSL_CERT_DIR=C:\ca_stuff openssl verify "Wells Fargo web.cer"
然而,在定义了指向从cURL网站下载的cacert.pem的SSL_CERT_FILE之后,相同的命令失败了。无论是否定义了SSL_CERT_DIR,情况都是如此。我验证了必要的CA证书已经包含在证书捆绑包中,并确认它们的序列号与我手动从IE中提取的序列号匹配。
似乎手动提取每个证书并将其放入具有哈希链接指向它的自己的文件中是一个艰难的过程。如果这是Unix,我可以自动化它,但在Windows上...我显然误解了一些关于如何使用OpenSSL使一个大CA证书文件工作的事情。
非常感谢您提供的建议、见解和帮助。
1个回答

6
如何为OpenSSL定义单个可信证书文件(如果可以的话)?
CAFile只是您信任并希望使用的自签名证书的串联。如果您只想信任一个证书,则CA文件中应该只有一个证书。
我更喜欢PEM编码,因为它更容易用文本编辑器检查(-----BEGIN CERTIFICATE----------END CERTIFICATE-----)。例如,这里是来自Startcom的ca-bundle.pem(http://www.startssl.com/certs/):
因此,要创建一个证书,请使用cat和重定向(或复制和粘贴)。
# 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
...

各种研究让我下载了Mozilla 12年12月版本的PEM格式受信任证书列表... 好吧,这是你可以使用的列表之一。当你使用Mozilla的列表时,你在说“我相信Mozilla会做正确的事情”。请记住,当Trustwave被发现拦截SSL/TLS流量时,Mozilla奖励了Trustwave的不良行为。尽管Trustwave违反了至少两项包含政策,但Mozilla仍然将他们包含在内,因为Trustwave承诺永远不会再这样做了。详见Remove Trustwave Certificate(s) from trusted root certificates。如果你不相信Mozilla的判断,那么你可以使用OpenSSL内置的列表/usr/lib/ssl/certs/ca-certificates.crt、使用其他列表(大多数主要供应商都有)或者自己构建。使用不同供应商的列表通常相当于用你所不知道的魔鬼换取你所知道的魔鬼。例如,苹果有一个列表,在iOS: List of available trusted root certificates (iOS 7)上可以查看。但是苹果的列表存在很多问题:http://seclists.org/fulldisclosure/2013/Sep/186http://seclists.org/fulldisclosure/2013/Sep/184。我建议你构建自己的列表或者固定证书。固定证书或公钥更好,因为它可以中和SSL/TLS中允许Trustwave做他们所做的系统性问题。详见OWASP的Certificate and Public Key Pinning
我不知道如何通过环境变量来实现它,因为我不使用它们。但是Linux/Unix/OSX/Windows之间应该没有区别(除了可能处理长文件名和空格的方式不同)。查看OpenSSL源代码,你会在cryptlib.h中找到以下内容:
#define X509_CERT_FILE_EVP       "SSL_CERT_FILE"

x509_def.c使用X509_CERT_FILE_EVP

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

X509_get_default_cert_file_envby_file.c 中被用于 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);

因此,在使用SSL_CERT_FILE时,最好(必须?)使用PEM格式的连接。
最后,请确保SSL_CERT_FILE没有被配置文件覆盖。有关详细信息,请参见OpenSSL config(5)

手动提取每个证书并将其放入具有哈希链接的自己的文件中似乎是一个繁琐的过程。

我认为在使用SSL_CERT_FILE-CAfileSSL_CTX_load_verify_locations时不需要重新计算哈希值。
在使用-CAfileSSL_CTX_load_verify_locations时,我从未重新计算过哈希值,一切都运作良好。当出现问题时,通常是因为(1)根证书不存在或不受信任;或者(2)中间证书不存在。
对于上述第(2)项,您需要服务器发送所有所需的证书以构建链。否则,客户端将不知道在哪里查找缺少的中间证书。这是PKI中一个众所周知的问题,称为“Which Directory”问题(客户端不知道要搜索缺少的证书的X500目录)。
相关的是,以下是如何在OpenSSL的s_client中使用它们。这实际上是可行的,因为pagepeeker.com使用StartCom,如果省略-CAfile选项,则会失败:
$ 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/emailAddress=alexandru.florescu@gmail.com
   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
...

以下是我用于设置SSL/TLS连接的一部分代码(除了公钥固定):

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;

SSL_CTX_load_verify_locations失败也没关系。这意味着您不信任任何东西,因此您会关闭或终止。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接