在gSoap中将私钥与Windows证书存储一起使用 [英] Use private key with Windows certficate store in gSoap

查看:125
本文介绍了在gSoap中将私钥与Windows证书存储一起使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用OpenSSL编写一个gSoap客户端,该客户端使用Windows证书.我有一个PEM证书和一个PEM私钥.当我将它们组合成一个文件并将其提供给gSoap时,它可以正常工作:

I'm writing a gSoap client with OpenSSL that uses Windows certificates. I have a PEM certificate and a PEM private key. When I combine them into a single file and give it to gSoap it works fine:

soap_ssl_client_context( &soap,
                         SOAP_SSL_DEFAULT,
                         "certkey.pem", /* required only when client must authenticate to server         */
                         NULL, /* password to read the key file (not used with GNUTLS)                 */
                         NULL, /* cacert file to store trusted certificates                            */
                         NULL, /* capath to directory with trusted certificates                        */
                         NULL  /* if randfile!=NULL: use a file with random data to seed randomness    */
                                 )

但是,当我将证书安装到Windows存储中并通过X509_STORE_add_cert从那里加载它时,它将不起作用.我的猜测是我必须以某种方式使用私钥,但是我不知道该以哪种方式使用.我该怎么办?

But when I install the certificate into the Windows storage and load it from there via X509_STORE_add_cert it does not work. My guess is that I have to use the private key somehow but I don't know in what way. What should I do?

推荐答案

您正确的是,您需要加载私钥,并且X509_STORE_add_cert是不正确的.如果要将证书用于服务器或客户端,则需要使用用于证书和证书私钥的SSL_CTX_use_xxx 或SSL_use_xxx.

You are correct that you need to load the private key and also X509_STORE_add_cert is the incorrect. If you want to use a certificate for a server or client you need to set the certificate into a ssl context using SSL_CTX_use_xxx or SSL_use_xxx for the certificate and for the private key of the certificate.

例如

SSL_CTX_use_certificate_chain_file(ctx, "cert.pem");
SSL_CTX_use_PrivateKey_file(ctx, "cert.pem", SSL_FILETYPE_PEM);

以上假设"cert.pem"同时包含证书链和私钥.

The above assumes the "cert.pem" holds both the certificate chain and the private key.

更新:

我假设"Windows存储"是指"Windows证书存储". 在Windows证书存储区中使用证书的主要问题是私钥的使用.如果私钥标记为不可导出",则只能使用 Windows Crypto API .因此,如果您希望使用带有存储在Windows证书存储区中的私钥的证书,则需要(很容易)将证书导出",并将私钥导出到openssl x509和rsa对象中,以在SSL_CTX_xxx函数中使用.我发现导出私钥的最佳方法是使用 RSA_setxxx 函数> RSA 结构.

I am assuming that by "Windows Storage" you mean the "Windows Certificate Store". The main problem with using a certificate in the windows certificate store is the use of the private key. If the private key is marked as "non-exportable" then you can only "use" the private key using the Windows Crypto API. So if you wish to use a certificate with the private key stored in the windows certificate store you need to "export" the certificate (easy enough) and the private key into openssl x509 and rsa objects to be used in the SSL_CTX_xxx functions. The best I have found to export a private key is using the NCryptExportKey using the BCRYPT_RSAFULLPRIVATE_BLOB blob type and then breaking up the BCRYPT_RSAKEY_BLOB manually into a openssl RSA structure using the RSA_setxxx functions.

RSA* 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;
    }

    RSA* rsa = 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)
            {
                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);
            }
        }
    }

    if(free_key)
    {
        NCryptFreeObject(key_handle);
    }

    return rsa;
}

bool set_ctx_certificate_and_private_key(SSL_CTX* ctx, const PCCERT_CONTEXT context)
{
    auto const x509 = d2i_X509(nullptr, const_cast<const unsigned char **>(&context->pbCertEncoded), context->cbCertEncoded);
    if (!x509)
    {
        return false;
    }

    if(!SSL_CTX_use_certificate(ctx, x509))
    {
        X509_free(x509);
        return false;
    }
    X509_free(x509);

    auto const rsa = extract_private_key(context);
    if (!rsa)
    {
        return false;
    }

    auto const success = SSL_CTX_use_RSAPrivateKey(ctx, rsa) == 1;
    RSA_free(rsa);
    return success;
}

这篇关于在gSoap中将私钥与Windows证书存储一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆