【问题标题】:OpenSSL no shared cipherOpenSSL 没有共享密码
【发布时间】:2020-05-27 13:25:17
【问题描述】:

如何在 OpenSSL 中使用从 Windows 证书存储区读取的证书?

我已经基于 Boost Beasts 的http_server_async_ssl.cpp 设置了一个 Windows 项目 - 基本上在我的项目中添加了一个 https 服务器。当我在该示例中使用硬编码证书时,一切正常,但是当我导出该证书并将其加载到我的代码中时,我得到“no shared cipher”。

I set up a minimal example。在main.cpp:79-83 中,我使用load_server_certificate,它根据指纹加载证书——这就是“no shared cipher”失败的原因。如果我改为将其注释掉并使用load_static_server_certificate(原始示例硬编码键),它就可以工作(我可以发布,获得回复,)。

基本上,该程序搜索给定的证书指纹(使用CertEnumCertificatesInStore 搜索整个存储区),一旦找到匹配项,它就会加载证书。我基于将证书加载到 an example 的上下文中,我在阅读有关此问题的每个 Stack Overflow 问题时发现 - this answer especially

ex/certificate_helpers.cpp

509 = d2i_X509(nullptr, const_cast<const BYTE**>(&pCertContext->pbCertEncoded), pCertContext->cbCertEncoded);
bio = BIO_new(BIO_s_mem());
PEM_write_bio_X509(bio, x509);
ctx.add_certificate_authority(boost::asio::buffer(certificates.data(), certificates.size()), ec);

我的上下文是在我的流开始之前设置的(这似乎是一个常见问题),我的证书似乎正在正确加载。在证书存储中,我的证书有一个私钥。

我的下一步是确定我是否可以提取该私钥并可能使用add_private_key 加载它,我还看到该示例使用 Diffie-Hellman 参数,如果这也是必需的,我不知道如何使用它 - 我需要它吗?

【问题讨论】:

    标签: c++ https openssl ssl-certificate boost-beast


    【解决方案1】:

    要使用证书,您可以像使用 add_certificate_authority 接口一样使用 use_certificate 接口。

    因为私钥变得有点困难。请参阅我的回答 here 作为将 RSA 私钥读入 EVP_PKEY 的示例(ECC 需要不同的代码)。然后就可以使用PEM_write_bio_PrivateKey函数生成pem blob,通过use_private_key接口来使用。

    如果您直接使用 ssl_context native_handle 方法调用 openssl 函数,也可以将对话跳过到 PEM 格式。

    例如

    X509 *cert = readCert();
    SSL_CTX_use_certificate(ctx.native_handle(), cert); // instead of the use_certificate call
    
    EVP_PKEY *key = readKey();
    SSL_use_PrivateKey(ctx.native_handle(), key); // instead of the use_private_key call
    
    X509 *cert = readChainCert();
    SSL_CTX_add_extra_chain_cert(ctx.native_handle(), cert); // use chain cert
    
    X509 *cert = readCaCert();
    X509_STORE *store = SSL_CTX_get_cert_store(ctx.native_handle()); // instead of the add_certificate_authority call
    X509_STORE_add_cert(store, cert);
    

    更新: 添加示例以使用 PCCERT_CONTEXT(即使用 CryptAcquireCertificatePrivateKey api)。

    EVP_PKEY* extract_private_key(const PCCERT_CONTEXT context)
    {
        HCRYPTPROV_OR_NCRYPT_KEY_HANDLE key_handle;
        DWORD key_spec = 0;
        BOOL free_key;
        if (!CryptAcquireCertificatePrivateKey(context, CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG | CRYPT_ACQUIRE_SILENT_FLAG, nullptr, &key_handle, &key_spec, &free_key))
        {
            return nullptr;
        }
    
        EVP_PKEY* pkey = nullptr;
        DWORD length = 0;
        if(SUCCEEDED(NCryptExportKey(key_handle, NULL, BCRYPT_RSAFULLPRIVATE_BLOB, nullptr, nullptr, 0, &length, 0)))
        {
            auto data = std::make_unique<BYTE[]>(length);
    
            if(SUCCEEDED(NCryptExportKey(key_handle, NULL, BCRYPT_RSAFULLPRIVATE_BLOB, nullptr, data.get(), length, &length, 0)))
            {
                // https://docs.microsoft.com/en-us/windows/desktop/api/bcrypt/ns-bcrypt-_bcrypt_rsakey_blob
                auto const blob = reinterpret_cast<BCRYPT_RSAKEY_BLOB*>(data.get());
    
                if(blob->Magic == BCRYPT_RSAFULLPRIVATE_MAGIC)
                {
                    auto rsa = RSA_new();
    
                    // n is the modulus common to both public and private key
                    auto const n = BN_bin2bn(data.get() + sizeof(BCRYPT_RSAKEY_BLOB) + blob->cbPublicExp, blob->cbModulus, nullptr);
                    // e is the public exponent
                    auto const e = BN_bin2bn(data.get() + sizeof(BCRYPT_RSAKEY_BLOB), blob->cbPublicExp, nullptr);
                    // d is the private exponent
                    auto const d = BN_bin2bn(data.get() + sizeof(BCRYPT_RSAKEY_BLOB) + blob->cbPublicExp + blob->cbModulus + blob->cbPrime1 + blob->cbPrime2 + blob->cbPrime1 + blob->cbPrime2 + blob->cbPrime1, blob->cbModulus, nullptr);
    
                    RSA_set0_key(rsa, n, e, d);
    
                    // p and q are the first and second factor of n
                    auto const p = BN_bin2bn(data.get() + sizeof(BCRYPT_RSAKEY_BLOB) + blob->cbPublicExp + blob->cbModulus, blob->cbPrime1, nullptr); 
                    auto const q = BN_bin2bn(data.get() + sizeof(BCRYPT_RSAKEY_BLOB) + blob->cbPublicExp + blob->cbModulus + blob->cbPrime1, blob->cbPrime2, nullptr); 
    
                    RSA_set0_factors(rsa, p, q);
    
                    // dmp1, dmq1 and iqmp are the exponents and coefficient for CRT calculations
                    auto const dmp1 = BN_bin2bn(data.get() + sizeof(BCRYPT_RSAKEY_BLOB) + blob->cbPublicExp + blob->cbModulus + blob->cbPrime1 + blob->cbPrime2, blob->cbPrime1, nullptr); 
                    auto const dmq1 = BN_bin2bn(data.get() + sizeof(BCRYPT_RSAKEY_BLOB) + blob->cbPublicExp + blob->cbModulus + blob->cbPrime1 + blob->cbPrime2 + blob->cbPrime1, blob->cbPrime2, nullptr); 
                    auto const iqmp = BN_bin2bn(data.get() + sizeof(BCRYPT_RSAKEY_BLOB) + blob->cbPublicExp + blob->cbModulus + blob->cbPrime1 + blob->cbPrime2 + blob->cbPrime1 + blob->cbPrime2, blob->cbPrime1, nullptr); 
    
                    RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp);
    
                    pkey = EVP_PKEY_new();
    
                    // ownership of rsa transferred to pkey
                    EVP_PKEY_assign_RSA(pkey, rsa);
                }
            }
        }
    
        if(free_key)
        {
            NCryptFreeObject(key_handle);
        }
    
        return pkey;
    }
    

    【讨论】:

    • 嘿,谢谢!可以肯定的是,readCertreadKeyreadhChainCertreadCaCert 都只是示例函数,对吧?要获得密钥,我会使用CryptAcquireCertificatePrivateKeyCertGetCertificateChainetc?这是否需要所有部分?或者有什么我可以跳过的吗?当我听到“添加 https”时,这一切似乎比我最初想象的要复杂。 :)
    • “cert”示例只是 d2i_X509 调用的结果。密钥将是调用我的答案以读取 rsa 私钥的结果。它只是跳过了将 cer/key 转换为 pem 格式的部分,只是为了在 asio 中使用它们(pem_write_bio_xxx 调用)。
    • answer 似乎已经从键的句柄开始(我猜来自CryptAcquireCertificatePrivateKey?)我不能直接使用它并将其读入SSL_use_PrivateKey 吗?我不确定如何在该答案中使用代码(对不起,我对证书真的很陌生。)而且一切(证书/pkey/chain cert/authority)都是必需的?再次感谢。
    • 是的,您可以使用 CryptAcquireCertiicatePrivateKey。问题是 windows 和 openssl 之间的私钥没有通用格式,您可以从 win32 API 中提取。您必须将私钥分解成它的位,然后在另一侧重建它。这就是我的示例答案正在做的事情
    • @AdamHunyadi,我对你这样做没有意见。
    猜你喜欢
    • 2018-08-12
    • 1970-01-01
    • 2017-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多