AcquireCredentialsHandleA() returns 0x8009030e (No credentials are available in the security package) for PFX file

0

I'm trying to setup server-side encryption using SSPI. I'm successfully (as far as I can tell) loading a certificate stored as a PFX file, but the call to m_pSSPI->AcquireCredentialsHandleA() returns 0x8009030e.

This method seems to successfully load the file and return a CERT_CONTEXT object.

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

The result is immediately passed to another class method, which makes the failing AcquireCredentialsHandleA() call.

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)
}

My CTLSPackage::AcquireCredentials() code path is also used for setting up client-side encryption, and that works. For the server-side path, m_fServer is TRUE. The m_hCrypt32 member was loaded from Crypt32.dll.

I've cobbled together this code from samples, but I must be missing something for the server case. I only need to setup SSL/TLS-style encryption, so the "No credentials are available in the security package" error is weird because I have no need for credential authentication.

Does anyone know what might be missing? Thanks!

c++
winapi
sspi
schannel
asked on Stack Overflow Jul 6, 2020 by David

1 Answer

0

With a hint from RbMm, I then found this article: https://www.codeproject.com/articles/125124/how-to-use-certificate-from-disk-with-microsoft-cr

The short answer is that CryptAcquireCertificatePrivateKey() needed to be used when loading a PFX from a file, and UNISP_NAME_A needed to be passed to AcquireCredentialsHandleA().

For reference, here is the revised code:

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)
}
answered on Stack Overflow Jul 29, 2020 by David

User contributions licensed under CC BY-SA 3.0