首页 > 解决方案 > AcquireCredentialsHandleA() 为 PFX 文件返回 0x8009030e(安全包中没有可用的凭据)

问题描述

我正在尝试使用 SSPI 设置服务器端加密。我已成功(据我所知)加载存储为 PFX 文件的证书,但调用m_pSSPI->AcquireCredentialsHandleA()返回0x8009030e.

此方法似乎成功加载文件并返回一个CERT_CONTEXT对象。

HRESULT CTLSPackage::LoadCertContextFromFilePFX (PCWSTR pcwzFile, PCWSTR pcwzPassword, __deref_out PCCERT_CONTEXT* ppctxCert)
{
    HRESULT hr;
    HANDLE hFile, hSection = NULL;
    BOOL (WINAPI* pfnPFXIsPFXBlob)(CRYPT_DATA_BLOB*);
    HCERTSTORE (WINAPI* pfnPFXImportCertStore)(CRYPT_DATA_BLOB*, LPCWSTR, DWORD);
    PCCERT_CONTEXT (WINAPI* pfnCertEnumCertificatesInStore)(HCERTSTORE hCertStore, PCCERT_CONTEXT pPrevCertContext);
    CRYPT_DATA_BLOB blob; blob.pbData = NULL;
    HCERTSTORE pfxStore = NULL;

    hFile = CreateFile(pcwzFile, FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
    CheckIfGetLastError(INVALID_HANDLE_VALUE == hFile);
    blob.cbData = GetFileSize(hFile, NULL);

    hSection = CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, 0);
    CheckIfGetLastError(NULL == hSection);

    blob.pbData = reinterpret_cast<PBYTE>(MapViewOfFile(hSection, FILE_MAP_READ, 0, 0, 0));
    CheckIfGetLastError(NULL == blob.pbData);

    Check(TGetFunction(m_hCrypt32, "PFXIsPFXBlob", &pfnPFXIsPFXBlob));
    Check(TGetFunction(m_hCrypt32, "PFXImportCertStore", &pfnPFXImportCertStore));
    Check(TGetFunction(m_hCrypt32, "CertEnumCertificatesInStore", &pfnCertEnumCertificatesInStore));

    CheckIf(!pfnPFXIsPFXBlob(&blob), E_FAIL);
    pfxStore = pfnPFXImportCertStore(&blob, pcwzPassword, CRYPT_MACHINE_KEYSET | CRYPT_EXPORTABLE);
    CheckIf(NULL == pfxStore, SEC_E_NO_CREDENTIALS);

    *ppctxCert = pfnCertEnumCertificatesInStore(pfxStore, NULL);
    CheckIf(NULL == *ppctxCert, SEC_E_NO_CREDENTIALS);

Cleanup:
    if(pfxStore)
    {
        BOOL (WINAPI* pfnCertCloseStore)(HCERTSTORE, DWORD);
        if(SUCCEEDED(TGetFunction(m_hCrypt32, "CertCloseStore", &pfnCertCloseStore)))
            pfnCertCloseStore(pfxStore, 0);
    }
    if(blob.pbData)
        UnmapViewOfFile(blob.pbData);
    SafeCloseHandle(hSection);
    SafeCloseFileHandle(hFile);
    return hr;
}

结果立即传递给另一个类方法,该方法AcquireCredentialsHandleA()调用失败。

HRESULT CTLSPackage::AcquireCredentials (__in_opt PCCERT_CONTEXT pCertContext, PCredHandle phCreds)
{
    SCHANNEL_CRED SchannelCred;
    TimeStamp tsExpiry;

    ZeroMemory(&SchannelCred, sizeof(SchannelCred));

    SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
    if(pCertContext)
    {
        SchannelCred.cCreds = 1;
        SchannelCred.paCred = &pCertContext;
    }
    SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1 | SP_PROT_TLS1_1 | SP_PROT_TLS1_2;

    if(!m_fServer)
        SchannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS | SCH_USE_STRONG_CRYPTO;

    //
    // Create an SSPI credential.
    //

    return m_pSSPI->AcquireCredentialsHandleA(
                        NULL,                   // Name of principal
                        m_fServer ? NEGOSSP_NAME_A : UNISP_NAME_A,  // Name of package
                        m_fServer ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND,
                        NULL,                   // Pointer to logon ID
                        &SchannelCred,          // Package specific data
                        NULL,                   // Pointer to GetKey() func
                        NULL,                   // Value to pass to GetKey()
                        phCreds,                // (out) Cred Handle
                        &tsExpiry);             // (out) Lifetime (optional)
}

我的CTLSPackage::AcquireCredentials()代码路径也用于设置客户端加密,并且有效。对于服务器端路径,m_fServerTRUE. 该m_hCrypt32成员是从 加载的Crypt32.dll

我已经从示例中拼凑出这段代码,但我必须为服务器案例遗漏一些东西。我只需要设置 SSL/TLS 样式的加密,因此“安全包中没有可用的凭据”错误很奇怪,因为我不需要凭据身份验证。

有谁知道可能缺少什么?谢谢!

标签: c++winapisspischannel

解决方案


在 RbMm 的提示下,我找到了这篇文章: https ://www.codeproject.com/articles/125124/how-to-use-certificate-from-disk-with-microsoft-cr

简短的回答是,从文件加载 PFX 时需要使用 CryptAcquireCertificatePrivateKey(),并且需要将 UNISP_NAME_A 传递给 AcquireCredentialsHandleA()。

作为参考,这里是修改后的代码:

HRESULT CTLSPackage::LoadCertContextFromFilePFX (PCWSTR pcwzFile, PCWSTR pcwzPassword, __deref_out PCCERT_CONTEXT* ppctxCert)
{
    HRESULT hr;
    HANDLE hFile, hSection = NULL;
    BOOL (WINAPI* pfnPFXIsPFXBlob)(CRYPT_DATA_BLOB*);
    HCERTSTORE (WINAPI* pfnPFXImportCertStore)(CRYPT_DATA_BLOB*, LPCWSTR, DWORD);
    PCCERT_CONTEXT (WINAPI* pfnCertFindCertificateInStore)(HCERTSTORE hCertStore, DWORD dwCertEncodingType, DWORD dwFindFlags, DWORD dwFindType, const void* pvFindPara, PCCERT_CONTEXT pPrevCertContext);
    BOOL (WINAPI* pfnCryptAcquireCertificatePrivateKey)(PCCERT_CONTEXT pCert, DWORD dwFlags, void* pvReserved, HCRYPTPROV_OR_NCRYPT_KEY_HANDLE *phCryptProvOrNCryptKey, DWORD* pdwKeySpec, BOOL* pfCallerFreeProvOrNCryptKey);
    HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hProv;
    DWORD dwKeySpec;
    BOOL fFreeProv = FALSE;
    CRYPT_DATA_BLOB blob; blob.pbData = NULL;
    HCERTSTORE hpfxStore = NULL;

    hFile = CreateFile(pcwzFile, FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
    CheckIfGetLastError(INVALID_HANDLE_VALUE == hFile);
    blob.cbData = GetFileSize(hFile, NULL);

    hSection = CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, 0);
    CheckIfGetLastError(NULL == hSection);

    blob.pbData = reinterpret_cast<PBYTE>(MapViewOfFile(hSection, FILE_MAP_READ, 0, 0, 0));
    CheckIfGetLastError(NULL == blob.pbData);

    Check(TGetFunction(m_hCrypt32, "PFXIsPFXBlob", &pfnPFXIsPFXBlob));
    Check(TGetFunction(m_hCrypt32, "PFXImportCertStore", &pfnPFXImportCertStore));
    Check(TGetFunction(m_hCrypt32, "CertFindCertificateInStore", &pfnCertFindCertificateInStore));
    Check(TGetFunction(m_hCrypt32, "CryptAcquireCertificatePrivateKey", &pfnCryptAcquireCertificatePrivateKey));

    CheckIf(!pfnPFXIsPFXBlob(&blob), HRESULT_FROM_WIN32(ERROR_BAD_FORMAT));
    hpfxStore = pfnPFXImportCertStore(&blob, pcwzPassword, 0);
    if(NULL == hpfxStore && pcwzPassword && L'\0' == *pcwzPassword)
    {
        hpfxStore = pfnPFXImportCertStore(&blob, NULL, 0);
        CheckIf(NULL == hpfxStore, SEC_E_NO_CREDENTIALS);
    }

    *ppctxCert = pfnCertFindCertificateInStore(hpfxStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL);
    CheckIfGetLastError(NULL == *ppctxCert);

    // Acquire the private key and make it available for the later AcquireCredentalsHandle() call.
    if(!pfnCryptAcquireCertificatePrivateKey(*ppctxCert, 0, NULL, &hProv, &dwKeySpec, &fFreeProv))
    {
        DWORD dwError = GetLastError();
        FreeCertificateContext(*ppctxCert);
        *ppctxCert = NULL;
        CheckWin32Error(dwError);
    }

Cleanup:
    if(fFreeProv)
        FreeProvOrNCryptKey(hProv, dwKeySpec);
    if(hpfxStore)
    {
        BOOL (WINAPI* pfnCertCloseStore)(HCERTSTORE, DWORD);
        if(SUCCEEDED(TGetFunction(m_hCrypt32, "CertCloseStore", &pfnCertCloseStore)))
            pfnCertCloseStore(hpfxStore, 0);
    }
    if(blob.pbData)
        UnmapViewOfFile(blob.pbData);
    SafeCloseHandle(hSection);
    SafeCloseFileHandle(hFile);
    return hr;
}

HRESULT CTLSPackage::AcquireCredentials (__in_opt PCCERT_CONTEXT pCertContext, PCredHandle phCreds)
{
    SCHANNEL_CRED SchannelCred;
    TimeStamp tsExpiry;

    ZeroMemory(&SchannelCred, sizeof(SchannelCred));

    SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
    if(pCertContext)
    {
        SchannelCred.cCreds = 1;
        SchannelCred.paCred = &pCertContext;
    }
    SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3 | SP_PROT_TLS1 | SP_PROT_TLS1_1 | SP_PROT_TLS1_2;

    SchannelCred.dwFlags = SCH_USE_STRONG_CRYPTO;
    if(!m_fServer)
        SchannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;

    //
    // Create an SSPI credential.
    //

    return m_pSSPI->AcquireCredentialsHandleA(
                        NULL,                   // Name of principal
                        UNISP_NAME_A,           // Name of package
                        m_fServer ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND,
                        NULL,                   // Pointer to logon ID
                        &SchannelCred,          // Package specific data
                        NULL,                   // Pointer to GetKey() func
                        NULL,                   // Value to pass to GetKey()
                        phCreds,                // (out) Cred Handle
                        &tsExpiry);             // (out) Lifetime (optional)
}

推荐阅读