Boost.Asio SSL 上下文未验证证书

Boost.Asio SSL context not verifying certificate(s)

这是我之前问题的延续,

我有一个从 Windows 上的系统 CA 存储生成的 CA 文件。生成的文件中不包含任何 invalid/expired 证书。

我的 Boost Asio 代码不想使用此 CA 存储进行验证。文件加载正常,ssl::context::load_verify_file 无错误返回,但验证回调不断被调用,preverified 设置为 false

这里是一个使用证书文件的 openssl 的例子运行:

Loading 'screen' into random state - done
CONNECTED(00000178)
depth=3 /C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
verify return:1
depth=2 /C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA
Certification Authority
verify return:1
depth=1 /C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA
Domain Validation Secure Server CA
verify return:1
depth=0 /OU=Domain Control Validated/OU=PositiveSSL/CN=example.org
verify return:1
---
Certificate chain
 0 s:/OU=Domain Control Validated/OU=PositiveSSL/CN=example.org
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
 1 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain 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
---
Server certificate
-----BEGIN CERTIFICATE-----
...snipped...
-----END CERTIFICATE-----
subject=/OU=Domain Control Validated/OU=PositiveSSL/CN=example.org
issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
---
Acceptable client certificate CA names
/OU=Domain Control Validated/OU=PositiveSSL/CN=example.org
/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
---
SSL handshake has read 5399 bytes and written 334 bytes
---
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA
Server public key is 2048 bit
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : DHE-RSA-AES256-SHA
    Session-ID: 8C04CCAE22F4B111AD13F10448ACDAD0C7F567F22C0D05829BDAE1DF9F29A005

    Session-ID-ctx:
    Master-Key: B99A5E1D0C3CF5421C41CDE88B6F21FD9816800409775C497859FAFCAE3A8942
1A670D72808C804A33A10BF9A26B22AB
    Key-Arg   : None
    Start Time: 1477664766
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)

还有另一个 运行 没有证书文件的 openssl:

Loading 'screen' into random state - done
CONNECTED(00000178)
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
---
Certificate chain
 0 s:/OU=Domain Control Validated/OU=PositiveSSL/CN=example.org
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
 1 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain 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
---
Server certificate
-----BEGIN CERTIFICATE-----
...snipped...
-----END CERTIFICATE-----
subject=/OU=Domain Control Validated/OU=PositiveSSL/CN=example.org
issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
---
Acceptable client certificate CA names
/OU=Domain Control Validated/OU=PositiveSSL/CN=example.org
/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA
---
SSL handshake has read 5399 bytes and written 334 bytes
---
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA
Server public key is 2048 bit
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : DHE-RSA-AES256-SHA
    Session-ID: 18036DF0E136729F9FE1BD8F51AA2FEF687D84D43918895B3F5847A2EB8C7109

    Session-ID-ctx:
    Master-Key: DA4BCC28FB9F4F5BCD9C9FBB51AA02B8A380F0580258A0F67E56BA2BFD627C54
AB700C343F0741A77AC037E54272EB1E
    Key-Arg   : None
    Start Time: 1477665097
    Timeout   : 300 (sec)
    Verify return code: 20 (unable to get local issuer certificate)

如您所见,证书文件包含相应的根证书,因为 openssl 仅在使用该文件时连接。

我的问题是,为什么openssl验证使用这个文件,而Boost Asio不验证?我该如何纠正这个问题,以便 Boost Asio 使用生成的 CA 存储进行验证?

如果您的目标是使用带有 boost asio 的 windows ca 商店,您可以 "attach" 证书从商店到 boost ssl 上下文,像这样:

#include <boost/asio/ssl/context.hpp>
#include <wincrypt.h>

void add_windows_root_certs(boost::asio::ssl::context &ctx)
{
    HCERTSTORE hStore = CertOpenSystemStore(0, "ROOT");
    if (hStore == NULL) {
        return;
    }

    X509_STORE *store = X509_STORE_new();
    PCCERT_CONTEXT pContext = NULL;
    while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != NULL) {
        // convert from DER to internal format
        X509 *x509 = d2i_X509(NULL,
                              (const unsigned char **)&pContext->pbCertEncoded,
                              pContext->cbCertEncoded);
        if(x509 != NULL) {
            X509_STORE_add_cert(store, x509);
            X509_free(x509);
        }
    }

    CertFreeCertificateContext(pContext);
    CertCloseStore(hStore, 0);

    // attach X509_STORE to boost ssl context
    SSL_CTX_set_cert_store(ctx.native_handle(), store);
}

这将从 windows ca 商店加载证书(类似于您链接的问题)。但是它没有将证书转换为 base64,而是使用 d2i_X509 将它们转换为内部 OpenSSL 格式并将它们添加到 OpenSSL X509_STORE。然后 SSL_CTX_set_cert_store 将该存储附加到 boost ssl 上下文。您可以使用它来设置您的 ssl 上下文,然后将其用于 ssl 套接字:

namespace ssl = boost::asio::ssl;
ssl::context ctx(ssl::context::tlsv12_client);
ctx.set_options(ssl::context::default_workarounds
                            | ssl::context::no_sslv2
                            | ssl::context::no_sslv3
                            | ssl::context::tlsv12_client);

add_windows_root_certs(ctx);
ctx.set_verify_mode(ssl::verify_peer | ssl::verify_fail_if_no_peer_cert);

// use custom verify_callback here for debugging purposes
ctx.set_verify_callback(ssl::rfc2818_verification(address));

ssl::stream<boost::asio::ip::tcp::socket> socket(io, ctx);
// socket ready to connect to ssl host

问题是创建了另一个未加载系统证书的 boost::asio::ssl::context,而是使用 set_default_verify_paths,它适用于 Linux 但不适用于 Windows.它在 Linux 上工作,因为系统证书位于默认验证路径中,但在 Windows 上,它是一个空目录(我没有使用任何证书设置我的 OpenSSL 安装)。

对于遇到类似问题的其他任何人,请确保您实际使用的是 boost::asio::ssl::context 您正在将证书加载到其中,这样您就不会像我一样在这上面浪费太多时间。