locked
SChannel/CertCreateSelfSignCertificate Problems in Win2k8 RRS feed

  • Question

  • I've been tasked with fixing an old service that doesn't work on Win2k8. This service is using SChannel via SSPI to provide the server end of an SSL connection. Everything works as expected on Win2k and Win2k3, but I'm getting a SEC_E_UNKNOWN_CREDENTIALS error from SChannel's AcquireCredentialsHandle routine on Win2k8.

    I've killed the better part of the day trying to figure this one out. I found some similar posts in other forums which suggest that the private key for my cert may be stored in the wrong CSP. From what I can tell, I don't think this is my problem. Here's the relevant code:

    CRYPT_KEY_PROV_INFO keyinfo;
    
    ZeroMemory(&keyinfo, sizeof(keyinfo));
    keyinfo.pwszContainerName = NULL;
    keyinfo.pwszProvName = NULL;
    keyinfo.dwProvType = PROV_RSA_SCHANNEL;
    keyinfo.dwKeySpec = AT_KEYEXCHANGE;
    
    CRYPT_ALGORITHM_IDENTIFIER sign_alg;
    
    sign_alg.pszObjId = szOID_RSA_SHA1RSA;
    sign_alg.Parameters.cbData = 0;
    
    PCCERT_CONTEXT  pCertContext = CertCreateSelfSignCertificate(NULL, &name_blob, 0,
    &keyinfo, &sign_alg, NULL, NULL, NULL);
    
    SCHANNEL_CRED  SchannelCred;
    ZeroMemory(&SchannelCred, sizeof(SchannelCred));
    
    SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
    
    SchannelCred.cCreds = 1;
    SchannelCred.paCred = &pCertContext;
    
    SchannelCred.grbitEnabledProtocols = 0;	
    
    /* Call SChannel's AcquireCredentialsHandle routine. */
    g_pAcquireCredentialsHandle(NULL, UNISP_NAME, SECPKG_CRED_INBOUND, NULL, &SchannelCred, NULL, NULL, phCreds, &tsExpiry);
    /* Fails with SEC_E_UNKNOWN_CREDENTIALS */

    I'm quickly running out out of ideas. Any help will be greately appreciated.

    Tuesday, May 5, 2009 12:13 AM

Answers

  • The silence is deafening.

     

    For the benefit of any other poor souls who may encounter this problem, here is the embarrassingly simple solution:

     

    In the sample above, change this:

    keyinfo.pwszContainerName = NULL;
    keyinfo.pwszProvName = NULL;


    To this:

    keyinfo.pwszContainerName = UNISP_NAME;
    keyinfo.pwszProvName = MS_DEF_RSA_SCHANNEL_PROV;
     

    Ironically, I solved the problem after perusing the Wine code. The SChannel sample that's mentioned in several on-line MSDN documents doesn't seem to be in the latest Windows SDK, and I never could find an old version of it.

     

    Apparently just specifying PROV_RSA_SCHANNEL for the CSP type isn't enough to allow Win2k8 to select the correct CSP. Based on information in the post linked below, my best guess is that Win2k8 stores the private key using a different CSP than previous version of Windows when the CSP name isn't explicitly provided.

    Helpful Post:

    http://groups.google.co.in/group/microsoft.public.platformsdk.security/browse_thread/thread/6dc1ec51df8bde89/7fd4dab1e4398898?lnk=st&q=Acquirecredentialshandle&rnum=27&hl=en#7fd4dab1e4398898

     

    • Marked as answer by Scott_BC Tuesday, May 12, 2009 12:07 AM
    Tuesday, May 12, 2009 12:07 AM

All replies

  • I've created a small test program to reproduce the problem. The code is below. This program works on my XP dev machine, but fails with SEC_E_UNKNOWN_CREDENTIALS on my Win2k8 server.

    Here's the code:
    #include <windows.h>
    #include <wincrypt.h>
    #include <Security.h>
    #include <Schnlsp.h>
    
    #include <stdio.h>
    
    #define CERT_SUBJECT L"cn=myTestCert"
    
    /** The dispatch table for security functions */
    SecurityFunctionTable *nfp;
    
    /** Map each symbol to the appropriate entry in the dispatch table */
    #define g_pFreeCredentialsHandle nfp->FreeCredentialsHandle
    #define g_pAcquireCredentialsHandle nfp->AcquireCredentialsHandle
    #define g_pQuerySecurityPackageInfo nfp->QuerySecurityPackageInfo
    #define g_pFreeContextBuffer nfp->FreeContextBuffer
    #define g_pInitializeSecurityContext nfp->InitializeSecurityContext
    #define g_pAcceptSecurityContext nfp->AcceptSecurityContext
    #define g_pCompleteAuthToken nfp->CompleteAuthToken
    #define g_pEnumerateSecurityPackages nfp->EnumerateSecurityPackages
    #define g_pQueryCredentialsAttributes nfp->QueryCredentialsAttributes
    #define g_pQueryContextAttributes nfp->QueryContextAttributes
    #define g_pImpersonateSecurityContext nfp->ImpersonateSecurityContext
    #define g_pRevertSecurityContext nfp->RevertSecurityContext
    #define g_pDeleteSecurityContext nfp->DeleteSecurityContext
    #define g_pDecryptMessage nfp->DecryptMessage
    #define g_pEncryptMessage nfp->EncryptMessage
    
    //==========================================================================
    int initSSPI()
    {
        int err = 0;
    	ULONG count;
    	PSecPkgInfo packages;
        ULONG i;
    
    	err = EnumerateSecurityPackages(&count, &packages);
        if(err != SEC_E_OK)
        {
            printf("Error 0x%x from EnumerateSecurityPackages\n", err);
            goto ERROR_EXIT;
        }
    	
    	for(i = 0; i < count; ++i) 
        {
    		printf(
                "%d: 0x%x %d %d %d %S %S\n", 
    			i, 
                packages[i].fCapabilities, 
                packages[i].wVersion, 
                packages[i].wRPCID, 
    			packages[i].cbMaxToken, 
                packages[i].Name, 
                packages[i].Comment);
        }
    
    	nfp = InitSecurityInterface();
    
        if(g_pFreeCredentialsHandle == NULL
    		|| g_pAcquireCredentialsHandle == NULL
            || g_pQuerySecurityPackageInfo == NULL
            || g_pAcquireCredentialsHandle == NULL
            || g_pFreeContextBuffer == NULL
            || g_pInitializeSecurityContext == NULL
            || g_pAcceptSecurityContext == NULL
            || g_pEnumerateSecurityPackages == NULL
            || g_pQueryCredentialsAttributes == NULL
            || g_pQueryContextAttributes == NULL
            || g_pImpersonateSecurityContext == NULL
            || g_pRevertSecurityContext == NULL
            || g_pDeleteSecurityContext == NULL
    		|| g_pDecryptMessage == NULL
    		|| g_pEncryptMessage == NULL)
        {
            // One of the functions pointers didn't get assigned!
            printf("Unable to get all Windows SSPI interface's entry points");
            err = -1;
        }
    
    ERROR_EXIT:
    
        return err;
    }
    
    //==========================================================================
    int main(
        int argc,
        char *argv[])
    {
        int err = 0;
        PCCERT_CONTEXT pCertContext = NULL;
        CERT_NAME_BLOB nameBlob;
        CRYPT_KEY_PROV_INFO keyinfo;
        CRYPT_ALGORITHM_IDENTIFIER signAlg;
        SCHANNEL_CRED schannelCred;
        TimeStamp tsExpiry;
        CredHandle hCreds;
        int hasCreds = 0;
    
    
    	nameBlob.pbData = NULL;
    	nameBlob.cbData = 0;
    
        if((err = initSSPI()))
        {
            goto ERROR_EXIT;
        }
    
    	// Find out how big output needs to be (this sucks)
    	CertStrToName(X509_ASN_ENCODING, CERT_SUBJECT, CERT_X500_NAME_STR, NULL, NULL, &nameBlob.cbData, NULL);
    	nameBlob.pbData = (BYTE*)malloc(nameBlob.cbData);
    
    	if(!CertStrToName(X509_ASN_ENCODING, CERT_SUBJECT, CERT_OID_NAME_STR, NULL, nameBlob.pbData, &nameBlob.cbData, NULL)) 
        {
    		err = GetLastError();
            goto ERROR_EXIT;
    	}
    	
        // Initialize CRYPT_KEY_PROV_INFO structure
        ZeroMemory(&keyinfo, sizeof(keyinfo));
    	keyinfo.pwszContainerName = NULL;
    	keyinfo.pwszProvName = NULL;
    	keyinfo.dwProvType = PROV_RSA_SCHANNEL;
    	keyinfo.dwKeySpec = AT_KEYEXCHANGE;
    	
    
    	signAlg.pszObjId = szOID_RSA_SHA1RSA;
    	signAlg.Parameters.cbData = 0;
    
    	// Create a self-signed certificate
    
    	pCertContext = CertCreateSelfSignCertificate(
    	    (HCRYPTPROV)NULL,   // default provider
    	    &nameBlob,	    	// us
    	    0,				    // default flags,
    	    &keyinfo,		    // Use SCHANNEL keys
    	    &signAlg,		    // SHA1RSA signature
    	    NULL,			    // valid now
    	    NULL,			    // default expiry
    	    NULL);			    // no extensions
    
    
        ZeroMemory(&schannelCred, sizeof(schannelCred));
    
        schannelCred.dwVersion = SCHANNEL_CRED_VERSION;
    
        schannelCred.cCreds = 1;
        schannelCred.paCred = &pCertContext;
    
        schannelCred.grbitEnabledProtocols = 0;	
    
        err = g_pAcquireCredentialsHandle(
            NULL,                   // Name of principal
            UNISP_NAME,             // Name of package
            SECPKG_CRED_INBOUND,    // Flags indicating use
            NULL,                   // Pointer to logon ID
            &schannelCred,          // Package specific data
            NULL,                   // Pointer to GetKey() func
            NULL,                   // Value to pass to GetKey()
            &hCreds,                // (out) Cred Handle
            &tsExpiry);             // (out) Lifetime (optional)
    
        if(err != SEC_E_OK)
        {
            printf("AcquireCredentialsHandle failed: 0x%x\n", err);
            goto ERROR_EXIT;
        }
    
        hasCreds = 1;
    
    ERROR_EXIT:
    
        if(nameBlob.pbData){free(nameBlob.pbData);}
    
        if(hasCreds && g_pFreeCredentialsHandle)
        {
            g_pFreeCredentialsHandle(&hCreds);
        }
    
        return err;
    }
    Wednesday, May 6, 2009 5:21 PM
  • The silence is deafening.

     

    For the benefit of any other poor souls who may encounter this problem, here is the embarrassingly simple solution:

     

    In the sample above, change this:

    keyinfo.pwszContainerName = NULL;
    keyinfo.pwszProvName = NULL;


    To this:

    keyinfo.pwszContainerName = UNISP_NAME;
    keyinfo.pwszProvName = MS_DEF_RSA_SCHANNEL_PROV;
     

    Ironically, I solved the problem after perusing the Wine code. The SChannel sample that's mentioned in several on-line MSDN documents doesn't seem to be in the latest Windows SDK, and I never could find an old version of it.

     

    Apparently just specifying PROV_RSA_SCHANNEL for the CSP type isn't enough to allow Win2k8 to select the correct CSP. Based on information in the post linked below, my best guess is that Win2k8 stores the private key using a different CSP than previous version of Windows when the CSP name isn't explicitly provided.

    Helpful Post:

    http://groups.google.co.in/group/microsoft.public.platformsdk.security/browse_thread/thread/6dc1ec51df8bde89/7fd4dab1e4398898?lnk=st&q=Acquirecredentialshandle&rnum=27&hl=en#7fd4dab1e4398898

     

    • Marked as answer by Scott_BC Tuesday, May 12, 2009 12:07 AM
    Tuesday, May 12, 2009 12:07 AM