sliva
sliva

Reputation: 11

Sign certificate with private key and logon domain

I'm implementing the program related to logon domain with certificate by custom KSP and my credential provider. I have successfully interacted from my credential provider to custom KSP. I'm in the process of implementing custom KSP. The steps I perform handling in custom KSP are as follows:

  1. Install the template certificate Kerberos that has been issued from ADCS to local machine store.
  2. Export the private key from the file (.pfx) that has been issued from ADCS via the command:
#openssl pkcs12 -in sample.pfx -nocerts -nodes -out sample.key.
#openssl rsa -in sample.key -out sample_private.key.
  1. The flow custom KSP looks like this:
SampleKSPOpenProvider -> SampleKSPOpenKey-> SampleKSPGetKeyProperty -> SampleKSPSignHash.
  1. In SampleKSPSignHash, I read the private key and imported the key, then implemented the functions BCryptCreateHash, BCryptHashData, BCryptFinishHash, and finally BCryptSignHash. The data hash will be taken from SampleKSPGetKeyProperty by reading the certificate from the local machine store(CertContext->pbCertEncoded).But I'm having trouble with the hash data and there was an error during BCryptSignHash.Below is the code of SampleKSPGetKeyProperty:
SECURITY_STATUS
WINAPI
SampleKSPGetKeyProperty(
__in NCRYPT_PROV_HANDLE hProvider,
__in NCRYPT_KEY_HANDLE hKey,
__in LPCWSTR pszProperty,
__out_bcount_part_opt(cbOutput, *pcbResult) PBYTE pbOutput,
__in DWORD cbOutput,
__out DWORD * pcbResult,
__in DWORD dwFlags)
{
    ....
    ....
    else if (wcscmp(pszProperty, NCRYPT_CERTIFICATE_PROPERTY) == 0) {
        if (pbOutput == NULL) // get the certificate size {
            *pcbResult = aCertContext->cbCertEncoded;
        }
        else
        {
            if (aCertContext->cbCertEncoded < *pcbResult)
            {
                DebugPrint("ERROR", "Buffer too small!");
                Status = NTE_BUFFER_TOO_SMALL;
                goto cleanup;
            }

            DebugPrint("INFO Returning certificate payload...");
            *pcbResult = aCertContext->cbCertEncoded;
        
            CopyMemory(pbOutput, aCertContext->pbCertEncoded, aCertContext-    
            >cbCertEncoded);
    
            //Debug print the output certEncoded
            char text[4096];
            for (int i = 0; i < aCertContext->cbCertEncoded; i++)
            {
                sprintf((char*)text + (i), "%02X", pbOutput[i]);
            }
            DebugPrint("Call function -> pbOutput: %s", text);

            // There should handle call SampleKSPSignHash directly here ?
            PBYTE pbSignature = NULL;
            DWORD cbSignaturee = 0;

            SampleKSPSignHash(hProvider,hKey,NULL, pbOutput, aCertContext-    
            >cbCertEncoded, pbSignature, pbSignature,0,0);

         }
    }
    ....

}

Next is the code of SampleKSPSignHash, When calling BCryptSignHash, it failed:

SECURITY_STATUS
WINAPI
SampleKSPSignHash(
__in NCRYPT_PROV_HANDLE hProvider,
__in NCRYPT_KEY_HANDLE hKey,
__in_opt VOID *pPaddingInfo,
__in_bcount(cbHashValue) PBYTE pbHashValue,
__in DWORD cbHashValue,
__out_bcount_part_opt(cbSignaturee, *pcbResult) PBYTE pbSignature,
__in DWORD cbSignaturee,
__out DWORD * pcbResult,
__in DWORD dwFlags)
{
    DWORD dwBufferLen = 0, cbKeyBlob = 0;
    PBYTE pbBuffer = NULL, pbKeyBlob = NULL;
    LPBYTE   lpHashData;
    DWORD    dwHashDataSize;
    NTSTATUS status;
    BCRYPT_ALG_HANDLE  hAlg;
    DWORD    dwSignatureSize;
    PBYTE   lpSignature;

    const char* szPemPrivKeyPass = 
    "-----BEGIN RSA PRIVATE KEY-----"
    "MIIEpAIBAAKCAQEAn5JrYEBEC8Yy3cbCzZnu89MyLNsFnuRlWQzKx2toE9xZCuUf"
    ".....
    "eSfelLMqp94Ia//VwTFTnj5jKJCcTkQ4L7M0I2tm3PAM7PUzCxKHgw=="
    "-----END RSA PRIVATE KEY-----";

    if (!CryptStringToBinaryA(szPemPrivKeyPass, 0, CRYPT_STRING_BASE64HEADER, 
        NULL, &dwBufferLen, NULL, NULL))
    {
        return FALSE;
    }
    
    pbBuffer = (PBYTE)LocalAlloc(0, dwBufferLen);
    if (!CryptStringToBinaryA(szPemPrivKeyPass, 0, CRYPT_STRING_BASE64HEADER, 
        pbBuffer, &dwBufferLen, NULL, NULL))
    {
         return FALSE;
    }
    if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,         
       PKCS_RSA_PRIVATE_KEY, pbBuffer, dwBufferLen, 0, NULL, NULL, 
       &cbKeyBlob))
    {
         return FALSE;
    }

    pbKeyBlob = (PBYTE)LocalAlloc(0, cbKeyBlob);
    if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,     
        PKCS_RSA_PRIVATE_KEY, pbBuffer, dwBufferLen, 0, NULL, pbKeyBlob,     
        &cbKeyBlob))
    {
         return FALSE;
    }

    // -------------START HASH DATA ------------//
    status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RSA_ALGORITHM, NULL, 
    0);

    if (!NT_SUCCESS(status)) {
        return FALSE;
    }

    status = BCryptImportKeyPair(hAlg, NULL, LEGACY_RSAPRIVATE_BLOB, &hKey, 
        (PUCHAR)pbKeyBlob, cbKeyBlob, 0);
    if (!NT_SUCCESS(status)) {
        return FALSE;
    }
    if (!GetHashData((PBYTE)pbHashValue, cbHashValue, &lpHashData, 
        &dwHashDataSize)) {
        return FALSE;
    }
    BCryptSignHash(hKey, NULL, (PBYTE)lpHashData, dwHashDataSize, NULL, 0, 
    &dwSignatureSize, 0);

    pbSignature = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, dwSignatureSize);

    //----I have failed here---//
    status = BCryptSignHash(hKey, NULL, (PBYTE)lpHashData, dwHashDataSize, 
    pbSignature, dwSignatureSize, &dwSignatureSize, 0);
    if (!NT_SUCCESS(status)) {
        HeapFree(GetProcessHeap(), 0, lpHashData);
        HeapFree(GetProcessHeap(), 0, pbSignature);
        return FALSE; //I have failed here
    }    
}

BOOL GetHashData(PBYTE lpData, DWORD dwDataSize, PBYTE* lplpHashData, 
LPDWORD 
lpdwHashDataSize){
BCRYPT_ALG_HANDLE  hAlg;
BCRYPT_HASH_HANDLE hHash;
DWORD              dwResult;
DWORD              dwHashObjectSize;
PBYTE             lpHashObject;
NTSTATUS           status;
    status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM, NULL, 
    0);
    if (!NT_SUCCESS(status)) {
        DebugPrint("Error: BCryptOpenAlgorithmProvider 0x%.8X\n", 
        GetLastError());
        return FALSE;
    }
    BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PBYTE)&dwHashObjectSize,     
    sizeof(DWORD), &dwResult, 0);

    lpHashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, dwHashObjectSize);

    status = BCryptCreateHash(hAlg, &hHash, lpHashObject, dwHashObjectSize, 
    NULL, 0, 0);
    if (!NT_SUCCESS(status)) {
        HeapFree(GetProcessHeap(), 0, lpHashObject);
        BCryptCloseAlgorithmProvider(hAlg, 0);
        return FALSE;
    }
    BCryptHashData(hHash, lpData, dwDataSize, 0);
    BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH, (PBYTE)lpdwHashDataSize, 
    sizeof(DWORD), &dwResult, 0);
   *lplpHashData = (PBYTE)HeapAlloc(GetProcessHeap(), 0, *lpdwHashDataSize);
    BCryptFinishHash(hHash, *lplpHashData, *lpdwHashDataSize, 0);
    HeapFree(GetProcessHeap(), 0, lpHashObject);
    BCryptDestroyHash(hHash);
    BCryptCloseAlgorithmProvider(hAlg, 0);
    return TRUE;
}   

I think after performing such a process and calling the credential provider will login to the domain. Do I understand that correctly? - Thanks in advance.

Upvotes: 0

Views: 595

Answers (1)

Frank
Frank

Reputation: 2066

It's been a long time since I wrote something like that, but if I remember correctly you need to call BCryptSignHash two times. The first time to get the expected size of the signature and the second time to actually do the signing.

pbOutput

The address of a buffer to receive the signature produced by this function. The cbOutput parameter contains the size of this buffer.

If this parameter is NULL, this function will calculate the size required for the signature and return the size in the location pointed to by the pcbResult parameter.

Even when I already knew the size and handed it over to the function it still complained with STATUS_INVALID_PARAMETER which is the translation of 0xC000000D. Only after I called it twice things started to work. Be sure to read the documentation of the windows crypto API carefully as there are some catches in it. ;-)

EDIT

Looking closer at your example I see that you have 0 as the last parameter in your call to BCryptSignHash. According to the documentation this should be 0x00000002 (PKCS1) or 0x00000008 (PSS):

dwFlags

A set of flags that modify the behavior of this function. The allowed set of flags depends on the type of key specified by the hKey parameter.

This can be one of the following values.

BCRYPT_PAD_PKCS1 Use the PKCS1 padding scheme. The pPaddingInfo parameter is a pointer to a BCRYPT_PKCS1_PADDING_INFO structure.

BCRYPT_PAD_PSS Use the Probabilistic Signature Scheme (PSS) padding scheme. The pPaddingInfo parameter is a pointer to a BCRYPT_PSS_PADDING_INFO structure.

Upvotes: 0

Related Questions