首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C++ OpenSSL: libssl无法在Windows上验证证书

C++ OpenSSL: libssl无法在Windows上验证证书
EN

Stack Overflow用户
提问于 2022-03-04 09:26:49
回答 1查看 316关注 0票数 0

我做了很多周详的工作,但似乎找不到合适的办法解决这个问题。许多StackOverflow帖子都是关于Ruby的,但是我或多或少地(通过https://gitlab.com/eidheim/Simple-Web-Server库)直接使用了C++应用程序/库集,并且需要解决如何为用户完全透明地修复这个问题(他们不应该为了使用应用程序而连接任何自定义证书验证文件)。

在Windows上,当我尝试使用SimpleWeb HTTPS客户端时,如果打开证书验证,连接就会失败,因为连接的证书无法验证。Linux上的情况并非如此,在Linux上,验证工作正常。

建议我遵循this solution将Windows证书导入到OpenSSL中,以便验证例程可以使用它们。然而,就我所见,这似乎并没有什么不同。我已经深入研究了libssl验证函数的核心,以尝试并确切了解发生了什么,尽管上面的答案建议将Windows证书添加到新的X509_STORE中,但SSL连接上下文似乎有自己的存储库,在初始化连接时设置。这使我认为,简单地创建一个新的X509_STORE并在那里添加证书是没有帮助的,因为连接实际上并不使用该存储。

很可能是因为我花了那么多时间调试libssl的细节,所以我忽略了解决这个问题的实际方法应该是什么。OpenSSL是否提供了一种常规的方法来查找我没有设置的系统证书?或者,问题是否是SimpleWeb库/ASIO初始化OpenSSL的方式?我知道库允许您为证书提供“验证文件”的路径,但我觉得这不是一个合适的解决方案,因为作为开发人员,我应该使用最终用户系统上的证书,而不是对自己的证书进行硬编码。

编辑:对于上下文,这是我在一个小示例应用程序中使用的代码:

代码语言:javascript
运行
复制
#define MY_ENCODING_TYPE  (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)

static void LoadSystemCertificates()
{
    HCERTSTORE hStore;
    PCCERT_CONTEXT pContext = nullptr;
    X509 *x509 = nullptr;
    X509_STORE *store = X509_STORE_new();

    hStore = CertOpenSystemStore(NULL, "ROOT");

    if (!hStore)
    {
        return;
    }

    while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != nullptr)
    {
        const unsigned char* encodedCert = reinterpret_cast<const unsigned char*>(pContext->pbCertEncoded);
        x509 = d2i_X509(nullptr, &encodedCert, pContext->cbCertEncoded);

        if (x509)
        {
            X509_STORE_add_cert(store, x509);
            X509_free(x509);
        }
    }

    CertCloseStore(hStore, 0);
}

static void MakeRequest(const std::string& address)
{
    using Client = SimpleWeb::Client<SimpleWeb::HTTPS>;

    Client httpsClient(address);
    httpsClient.io_service = std::make_shared<asio::io_service>();

    std::cout << "Making request to: " << address << std::endl;

    bool hasResponse = false;
    httpsClient.request("GET", [address, &hasResponse](std::shared_ptr<Client::Response> response, const SimpleWeb::error_code& error)
    {
        hasResponse = true;

        if ( error )
        {
            std::cerr << "Got error from " << address << ": " << error.message() << std::endl;
        }
        else
        {
            std::cout << "Got response from " << address << ":\n" << response->content.string() << std::endl;
        }
    });

    while ( !hasResponse )
    {
        httpsClient.io_service->poll();
        httpsClient.io_service->reset();

        std::this_thread::sleep_for(std::chrono::milliseconds(20));
    }
}

int main(int, char**)
{
    LoadSystemCertificates();
    MakeRequest("google.co.uk");

    return 0;
}

调用返回我:Got error from google.co.uk: certificate verify failed

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-03-07 13:38:49

好吧,对于将来可能有帮助的人来说,这就是我解决这个问题的方法。This answer对一个相关的问题有所帮助。

事实证明,问题确实在于SSL上下文没有使用我设置的证书存储。其他一切都还好,但拼图中缺少的部分是对SSL_CTX_set_cert_store()的调用,它获取证书存储并将其提供给SSL上下文。

在SimpleWeb库的上下文中,最简单的方法似乎是子类SimpleWeb::Client<SimpleWeb::HTTPS>类,并将以下内容添加到构造函数中:

代码语言:javascript
运行
复制
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <wincrypt.h>

class MyClient : public SimpleWeb::Client<SimpleWeb::HTTPS>
{
public:
    MyClient( /* ... */ ) :
        SimpleWeb::Client<SimpleWeb::HTTPS>( /* ... */ )
    {
        AddWindowsRootCertificates();
    }

private:
    using OpenSSLContext = asio::ssl::context::native_handle_type;

    void AddWindowsRootCertificates()
    {
        // Get the SSL context from the SimpleWeb class.
        OpenSSLContext sslContext = context.native_handle();
        
        // Get a certificate store populated with the Windows root certificates.
        // If this fails for some reason, the function returns null.
        X509_STORE* certStore = GetWindowsCertificateStore();

        if ( sslContext && certStore )
        {
            // Set this store to be used for the SSL context.
            SSL_CTX_set_cert_store(sslContext, certStore);
        }
    }

    static X509_STORE* GetWindowsCertificateStore()
    {
        // To avoid populating the store every time, we keep a static
        // pointer to the store and just initialise it the first time
        // this function is called.
        static X509_STORE* certificateStore = nullptr;

        if ( !certificateStore )
        {
            // Not initialised yet, so do so now.

            // Try to open the root certificate store.
            HCERTSTORE rootStore = CertOpenSystemStore(0, "ROOT");

            if ( rootStore )
            {
                // The new store is reference counted, so we can create it
                // and keep the pointer around for later use.
                certificateStore = X509_STORE_new();
                
                PCCERT_CONTEXT pContext = nullptr;

                while ( (pContext = CertEnumCertificatesInStore(rootStore, pContext)) != nullptr )
                {
                    // d2i_X509() may modify the pointer, so make a local copy.
                    const unsigned char* content = pContext->pbCertEncoded;
                    
                    // Convert the certificate to X509 format.
                    X509 *x509 = d2i_X509(NULL, &content, pContext->cbCertEncoded);

                    if ( x509 )
                    {
                        // Successful conversion, so add to the store.
                        X509_STORE_add_cert(certificateStore, x509);
                        
                        // Release our reference.
                        X509_free(x509);
                    }
                }

                // Make sure to close the store.
                CertCloseStore(rootStore, 0);
            }
        }

        return certificateStore;
    }
};

显然,如果您的类需要在多个平台上编译,则需要将GetWindowsCertificateStore()抽象到特定于平台的某个地方。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71349240

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档