Search code examples
c++winapisspischannel

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


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!


Solution

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