Reputation: 11
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:
#openssl pkcs12 -in sample.pfx -nocerts -nodes -out sample.key.
#openssl rsa -in sample.key -out sample_private.key.
SampleKSPOpenProvider -> SampleKSPOpenKey-> SampleKSPGetKeyProperty -> SampleKSPSignHash.
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
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