Sign certificate with private key and logon domain - cryptography

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:
Install the template certificate Kerberos that has been issued from ADCS to local machine store.
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.
The flow custom KSP looks like this:
SampleKSPOpenProvider -> SampleKSPOpenKey-> SampleKSPGetKeyProperty -> SampleKSPSignHash.
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.

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.

Related

Custom CNG KSP 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:
Install the template certificate Kerberos that has been issued from ADCS to local machine store. This is step how I Issue certificates and set up logons. Is there something missing?
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.
The flow custom KSP looks like this:
SampleKSPOpenProvider() -> SampleKSPOpenKey()->
SampleKSPGetKeyProperty() -> SampleKSPSignHash()
At SampleKSPSignHash(), My signing data is a buffer that contains the encoded certificate ( pbCertEncoded) and I signed the certificate with the private key and also verified the signature with the public key successfully.
I think the final handling is always in the SampleKSPSignHash () function, and I know that function can be called multiple times so I tried force code to always return the pbSignature signature, but still could not logon the domain.The event log always returns the error code ID 4625. About CAPI2 and Crypto-NCrypt log, Although I have enabled but when deploy logon with custom KSP and my credential provider doesn't output. It seems that has not been called yet, then I tried to logon with the default password window, the log was exported. Currently I only have a Log in Custom KSP developing. Is code handling logic wrong?
Below is the code of SampleKSPSignHash():
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)
{
SECURITY_STATUS Status = NTE_INTERNAL_ERROR;
NTSTATUS ntStatus = STATUS_INTERNAL_ERROR;
SAMPLEKSP_KEY* pKey = NULL;
DWORD cbTmpSig = 0;
DWORD cbTmp = 0;
UNREFERENCED_PARAMETER(hProvider);
DebugPrint("Call function ");
//Start workround
//Add handling to hash data and sign certificate with private key.
char text[4096];
DWORD dwBufferLen = 0, cbKeyBlob = 0;
PBYTE pbBuffer = NULL, pbKeyBlob = NULL;
LPBYTE lpHashData;
DWORD dwHashDataSize;
NTSTATUS status;
BCRYPT_ALG_HANDLE hAlg;
DWORD dwSignatureSize;
PBYTE lpSignature;
BCRYPT_PKCS1_PADDING_INFO padding_PKCS1;
padding_PKCS1.pszAlgId = BCRYPT_SHA1_ALGORITHM;
//Start Regardless of which calls to take, force always signs with the current certificate
//Alway force input pbHashValue = aCertContext->pbCertEncoded
HCERTSTORE hMyCertStore = NULL;
PCCERT_CONTEXT aCertContext = NULL;
LPBYTE pbData = NULL;
DWORD cbData = 0;
DWORD dwKeySpec;
hMyCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM,
X509_ASN_ENCODING,
0,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
L"MY");
if (hMyCertStore == NULL)
{
DebugPrint("Call function -> hMyCertStore is NULL");
}
aCertContext = CertFindCertificateInStore(hMyCertStore,
X509_ASN_ENCODING,
0,
CERT_FIND_SUBJECT_STR_A,
L"test01", // use appropriate subject name
NULL);
if (aCertContext == NULL)
{
DebugPrint("Call function -> Error: aCertContext is NULL");
}
pbHashValue = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, aCertContext->cbCertEncoded);
CopyMemory(pbHashValue, aCertContext->pbCertEncoded, aCertContext->cbCertEncoded);
cbHashValue = aCertContext->cbCertEncoded;
// End force
//Debug printout
DebugPrint("Call function - cbHashValue= %ld", cbHashValue);
DebugPrint("Call function - cbSignaturee= %ld", cbSignaturee);
DebugPrint("Call function - dwFlags= %ld", dwFlags);
for (int i = 0; i < cbHashValue; i++)
{
sprintf((char*)text + (i * 2), "%02X", pbHashValue[i]);
}
DebugPrint("Call function -> pbHashValue: %s", text);
// ------- HARCODE PRIVATE KEY ------ //
//Import the previously exported private key using the pfx file.Use the command below to
//export the private key.
//Command :#openssl pkcs12 -in sample.pfx -nocerts -nodes -out sample.key
// #openssl rsa -in sample.key -out sample_private.key
const char* szPemPrivKeyPass =
"-----BEGIN RSA PRIVATE KEY-----"
"MIIEowIBAAKCAQEA1MtKkDL5RuY7lYwCZy38x1w9kisJLhyb7VkIlodJPLyqkQUZ"
"eDjEvSyKl75ucgB4gzyO4MyYbH/lrttXH2sR830gG40MKz6EnsxyzCsCgYEA4AUC"
"fz5l+q7lW9Fm+hhM9duDFO5EME6RDJp6MIEWH9C0khv2wWhNJCdWNPwwlPgCWIpL"
"7ueaVfhErJkJLzH8V8gIPpb5Hot4YUycTNvZffSeS+RE5AF9kWgzlxcd31fGHgZZ"
"f9W0xn7ieQS3fFnVWlK900drOQ+qkQ8jMKvxDdcCgYEAtUJnGoSFq6undecimpVI"
"qwzzfr+MKpt7Ym+cdDrJ3qts+kYCD35O80lNM6TqqSJqCB76EwV3VmyzKQ+1/bZ9"
"wrb2FPOTew+ytzDh20dOHpAaVt3krCRQ4gBWzjgsWq4NP5cQParfSbvYBlBTkcJX"
"........................................................."
"-----END RSA PRIVATE KEY-----";
DebugPrint("Process Start import private key");
if (!CryptStringToBinaryA(szPemPrivKeyPass, 0, CRYPT_STRING_BASE64HEADER, NULL, &dwBufferLen,
NULL, NULL))
{
DebugPrint("Failed to convert BASE64 private key. Error 0x%.8X\n", GetLastError());
}
pbBuffer = (PBYTE)LocalAlloc(0, dwBufferLen);
if (!CryptStringToBinaryA(szPemPrivKeyPass, 0, CRYPT_STRING_BASE64HEADER, pbBuffer,
&dwBufferLen, NULL, NULL))
{
DebugPrint("Failed to convert BASE64 private key. Error 0x%.8X\n", GetLastError());
}
if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY,
pbBuffer, dwBufferLen, 0, NULL, NULL, &cbKeyBlob))
{
DebugPrint("Failed to parse private key. Error 0x%.8X\n", GetLastError());
}
pbKeyBlob = (PBYTE)LocalAlloc(0, cbKeyBlob);
if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY,
pbBuffer,
dwBufferLen, 0, NULL, pbKeyBlob, &cbKeyBlob))
{
DebugPrint("Failed to parse private key. Error 0x%.8X\n", GetLastError());
}
// ---------START HASH DATA ------------//
DebugPrint("Start Hash the data");
status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RSA_ALGORITHM, NULL, 0);
if (!NT_SUCCESS(status)) {
DebugPrint("Error: BCryptOpenAlgorithmProvider");
return 0;
}
//Import key pair
status = BCryptImportKeyPair(hAlg, NULL, LEGACY_RSAPRIVATE_BLOB, &hKey, (PUCHAR)pbKeyBlob,
cbKeyBlob, 0);
if (!NT_SUCCESS(status)) {
DebugPrint("Error: BCryptImportKeyPair : 0x%.8X\n", GetLastError());
return FALSE;
}
// Hash Data certificate
if (!GetHashData((LPBYTE)pbHashValue, cbHashValue, &lpHashData, &dwHashDataSize)) {
DebugPrint("Error: GetHashData");
return FALSE;
}
//Sign hash certificate
BCryptSignHash(hKey, &padding_PKCS1, (LPBYTE)lpHashData, dwHashDataSize, NULL, 0,
&dwSignatureSize, BCRYPT_PAD_PKCS1);
pbSignature = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, dwSignatureSize);
status = BCryptSignHash(hKey, &padding_PKCS1, (LPBYTE)lpHashData, dwHashDataSize,
pbSignature, dwSignatureSize, pcbResult, BCRYPT_PAD_PKCS1);
//Debug print
DebugPrint("Call function - dwHashDataSize= %ld", dwHashDataSize);
DebugPrint("Call function - pcbResult= %ld", *pcbResult);
DebugPrint("Call function - dwSignatureSize= %ld", dwSignatureSize);
if (!NT_SUCCESS(status)) {
DebugPrint("Error: BCryptSignHash= %X", status);
HeapFree(GetProcessHeap(), 0, lpHashData);
HeapFree(GetProcessHeap(), 0, pbSignature);
return FALSE;
}
// Print the Signature data.
char textPn[4096];
for (int i = 0; i < dwSignatureSize; i++){
sprintf((char*)textPn + (i * 2), "%02X", pbSignature[i]);
}
DebugPrint("pbSignature: %s", textPn);
// Verify the signature with the public key
if (!VerifySign(pbSignature, dwSignatureSize)) {
DebugPrint("Error signature");
return FALSE;
}
DebugPrint("Verify the signature success", );
Status = ERROR_SUCCESS;
//End workround
cleanup:
return Status;
}
BOOL VerifySign(
__in_bcount(cbSignaturee) PBYTE pbSignature,
__in DWORD cbSignaturee)
{
HCERTSTORE hMyCertStore = NULL;
PCCERT_CONTEXT aCertContext = NULL;
LPBYTE pbData = NULL;
DWORD cbData = 0;
DWORD dwKeySpec;
PBYTE pbOutput = NULL;
PBYTE vHashData;
DWORD vHashDataSize;
NTSTATUS status;
BCRYPT_PKCS1_PADDING_INFO padding_PKCS1;
padding_PKCS1.pszAlgId = BCRYPT_SHA1_ALGORITHM;
DebugPrint("Start VerifySign");
hMyCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM,
X509_ASN_ENCODING,
0,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
L"MY");
if (hMyCertStore == NULL)
{
DebugPrint("Call function -> hMyCertStore is NULL");
return FALSE;
}
aCertContext = CertFindCertificateInStore(hMyCertStore,
X509_ASN_ENCODING,
0,
CERT_FIND_SUBJECT_STR_A,
L"test01", // use appropriate subject name
NULL
);
if (aCertContext == NULL)
{
DebugPrint("Call function -> Error: aCertContext is NULL");
return FALSE;
}
PCCERT_CONTEXT pcCertContext = CertCreateCertificateContext(X509_ASN_ENCODING, aCertContext-
>pbCertEncoded, aCertContext->cbCertEncoded);
if (!pcCertContext)
{
DebugPrint("ERROR: pcCertContext");
return FALSE;
}
BCRYPT_KEY_HANDLE publicKeyHandle = NULL;
if (!CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, &pcCertContext->pCertInfo-
>SubjectPublicKeyInfo, 0, NULL, &publicKeyHandle))
{
DebugPrint("CryptImportPublicKeyInfoEx2 failed");
return FALSE;
}
pbOutput = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, aCertContext->cbCertEncoded);
CopyMemory(pbOutput, aCertContext->pbCertEncoded, aCertContext->cbCertEncoded);
if (!GetHashData((LPBYTE)pbOutput, aCertContext->cbCertEncoded, &vHashData, &vHashDataSize))
{
DebugPrint("GetHashData failed.");
return FALSE;
}
status = BCryptVerifySignature(publicKeyHandle, &padding_PKCS1, vHashData, vHashDataSize,
pbSignature, cbSignaturee, BCRYPT_PAD_PKCS1);
if (!NT_SUCCESS(status)) {
DebugPrint("Error: BCryptSignHash= %X", status);
return FALSE;
}
DebugPrint("Verify Sign OK");
return TRUE;
}
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;
DebugPrint("Call function ");
status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM, NULL, 0);
if (!NT_SUCCESS(status)) {
DebugPrint("Error: BCryptOpenAlgorithmProvider 0x%.8X\n", GetLastError());
return FALSE;
}
DebugPrint("Call function ");
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)) {
DebugPrint("Error: BCryptCreateHash 0x%.8X\n", GetLastError());
DebugPrint("Error: status= %X\n", status);
HeapFree(GetProcessHeap(), 0, lpHashObject);
BCryptCloseAlgorithmProvider(hAlg, 0);
return FALSE;
}
DebugPrint("Call function ");
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);
DebugPrint("Call function ");
return TRUE;
}
I also wonder if this Issue has anything to do with the my Credential Provider?
Below is my current Credential Provider code:
//Build the authentication data used by LsaLogonUser
void ConstructAuthInfo(LPBYTE* ppbAuthInfo, ULONG* pulAuthInfoLen)
{
DebugPrint("call function");
WCHAR szCardName[] = L"";
WCHAR szContainerName[] = L"";
WCHAR szReaderName[] = L"";
WCHAR szCspName[] = L"Microsoft Sample Key Storage Provider";
WCHAR szPin[] = L"1234";
ULONG ulPinByteLen = wcslen(szPin) * sizeof(WCHAR);
WCHAR szUserName[] = L"test01";
ULONG ulUserByteLen = wcslen(szUserName) * sizeof(WCHAR);
WCHAR szDomainName[] = L"xyz.co";
ULONG ulDomainByteLen = wcslen(szDomainName) * sizeof(WCHAR);
LPBYTE pbAuthInfo = NULL;
ULONG ulAuthInfoLen = 0;
KERB_CERTIFICATE_LOGON* pKerbCertLogon;
KERB_SMARTCARD_CSP_INFO* pKerbCspInfo;
LPBYTE pbDomainBuffer, pbUserBuffer, pbPinBuffer;
LPBYTE pbCspData;
LPBYTE pbCspDataContent;
ULONG ulCspDataLen = sizeof(KERB_SMARTCARD_CSP_INFO) -
sizeof(TCHAR) + (wcslen(szCardName) + 1) * sizeof(WCHAR) +
(wcslen(szCspName) + 1) * sizeof(WCHAR) +
(wcslen(szContainerName) + 1) * sizeof(WCHAR) +
(wcslen(szReaderName) + 1) * sizeof(WCHAR);
ulAuthInfoLen = sizeof(KERB_CERTIFICATE_LOGON) +
ulDomainByteLen + sizeof(WCHAR) + ulUserByteLen + sizeof(WCHAR) +
ulPinByteLen + sizeof(WCHAR) + ulCspDataLen;
pbAuthInfo = (LPBYTE)CoTaskMemAlloc(ulAuthInfoLen);
ZeroMemory(pbAuthInfo, ulAuthInfoLen);
pbDomainBuffer = pbAuthInfo + sizeof(KERB_CERTIFICATE_LOGON);
pbUserBuffer = pbDomainBuffer + ulDomainByteLen + sizeof(WCHAR);
pbPinBuffer = pbUserBuffer + ulUserByteLen + sizeof(WCHAR);
pbCspData = pbPinBuffer + ulPinByteLen + sizeof(WCHAR);
memcpy(pbDomainBuffer, szDomainName, ulDomainByteLen);
memcpy(pbUserBuffer, szUserName, ulUserByteLen);
memcpy(pbPinBuffer, szPin, ulPinByteLen);
pKerbCertLogon = (KERB_CERTIFICATE_LOGON*)pbAuthInfo;
pKerbCertLogon->MessageType = KerbCertificateLogon;
pKerbCertLogon->DomainName.Length = (USHORT)ulDomainByteLen;
pKerbCertLogon->DomainName.MaximumLength = (USHORT)(ulDomainByteLen
+ sizeof(WCHAR));
pKerbCertLogon->DomainName.Buffer = (PWSTR)(pbDomainBuffer - pbAuthInfo);
pKerbCertLogon->UserName.Length = (USHORT)ulUserByteLen;
pKerbCertLogon->UserName.MaximumLength = (USHORT)(ulUserByteLen + sizeof(WCHAR));
pKerbCertLogon->UserName.Buffer = (PWSTR)(pbUserBuffer - pbAuthInfo);
pKerbCertLogon->Pin.Length = (USHORT)ulPinByteLen;
pKerbCertLogon->Pin.MaximumLength = (USHORT)(ulPinByteLen + sizeof(WCHAR));
pKerbCertLogon->Pin.Buffer = (PWSTR)(pbPinBuffer - pbAuthInfo);
pKerbCertLogon->CspDataLength = ulCspDataLen;
pKerbCertLogon->CspData = (PUCHAR)(pbCspData - pbAuthInfo);
pKerbCspInfo = (KERB_SMARTCARD_CSP_INFO*)pbCspData;
pKerbCspInfo->dwCspInfoLen = ulCspDataLen;
pKerbCspInfo->MessageType = 1;
pKerbCspInfo->KeySpec = CERT_NCRYPT_KEY_SPEC;
//pKerbCspInfo->KeySpec = AT_KEYEXCHANGE;
pKerbCspInfo->nCardNameOffset = 0;
pKerbCspInfo->nReaderNameOffset = pKerbCspInfo->nCardNameOffset + wcslen(szCardName) + 1;
pKerbCspInfo->nContainerNameOffset = pKerbCspInfo->nReaderNameOffset + wcslen(szReaderName) + 1;
pKerbCspInfo->nCSPNameOffset = pKerbCspInfo->nContainerNameOffset + wcslen(szContainerName) + 1;
pbCspDataContent = pbCspData + sizeof(KERB_SMARTCARD_CSP_INFO) - sizeof(TCHAR);
memcpy(pbCspDataContent + (pKerbCspInfo->nCardNameOffset * sizeof(WCHAR)), szCardName, wcslen(szCardName) * sizeof(WCHAR));
memcpy(pbCspDataContent + (pKerbCspInfo->nReaderNameOffset * sizeof(WCHAR)), szReaderName, wcslen(szReaderName) * sizeof(WCHAR));
memcpy(pbCspDataContent + (pKerbCspInfo->nContainerNameOffset * sizeof(WCHAR)), szContainerName, wcslen(szContainerName) * sizeof(WCHAR));
memcpy(pbCspDataContent + (pKerbCspInfo->nCSPNameOffset * sizeof(WCHAR)), szCspName, wcslen(szCspName) * sizeof(WCHAR));
*ppbAuthInfo = pbAuthInfo;
*pulAuthInfoLen = ulAuthInfoLen;
DebugPrint("call function -> 12");
}
HRESULT CSampleCredential::GetSerialization(
_Out_ CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE *pcpgsr,
_Out_ CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *pcpcs,
_Outptr_result_maybenull_ PWSTR *ppwszOptionalStatusText,
_Out_ CREDENTIAL_PROVIDER_STATUS_ICON *pcpsiOptionalStatusIcon)
{
if (DEVELOPING) PrintLn("Credential::GetSerialization");
HRESULT hr = E_UNEXPECTED;
*pcpgsr = CPGSR_NO_CREDENTIAL_NOT_FINISHED;
*ppwszOptionalStatusText = nullptr;
*pcpsiOptionalStatusIcon = CPSI_NONE;
ZeroMemory(pcpcs, sizeof(*pcpcs));
//Start add connect to KSP
if (DEVELOPING) DebugPrint("Start test login");
ULONG ulAuthPackage;
hr = RetrieveNegotiateAuthPackage(&ulAuthPackage);
ConstructAuthInfo(&pcpcs->rgbSerialization, &pcpcs->cbSerialization);
if (SUCCEEDED(hr))
{
DebugPrint("Start test SUCCEEDED");
pcpcs->ulAuthenticationPackage = ulAuthPackage;
pcpcs->clsidCredentialProvider = CLSID_CSample;
*pcpgsr = CPGSR_RETURN_CREDENTIAL_FINISHED;
DebugPrint("End test SUCCEEDED");
}
return hr;
}
HRESULT RetrieveNegotiateAuthPackage(ULONG* pulAuthPackage)
{
HRESULT hr = S_OK;
HANDLE hLsa = NULL;
NTSTATUS status = LsaConnectUntrusted(&hLsa);
if (SUCCEEDED(HRESULT_FROM_NT(status)))
{
ULONG ulAuthPackage;
LSA_STRING lsaszKerberosName;
_LsaInitString(&lsaszKerberosName, MICROSOFT_KERBEROS_NAME_A);//NEGOSSP_NAME_A or MICROSOFT_KERBEROS_NAME_A
status = LsaLookupAuthenticationPackage(hLsa, &lsaszKerberosName, &ulAuthPackage);
if (SUCCEEDED(HRESULT_FROM_NT(status)))
{
*pulAuthPackage = ulAuthPackage;
hr = S_OK;
}
else
{
hr = HRESULT_FROM_NT(status);
}
LsaDeregisterLogonProcess(hLsa);
}
else
{
hr = HRESULT_FROM_NT(status);
}
return hr;
}
This is all my code SampleKSP.c. Thank in advance.

TLS Session Resumption with OpenSSL server and SChannel client

I must use the RFC5077 TLS session resumption. My Client use Windows SChannel and server usually uses OpenSSL. In my test, following result.
OpenSSL 1.1.0 (or later) and SChannel: Always session reused, SChannel send previous Session Ticket.
OpenSSL 1.0.2 (any revision) and Schannel: Always new session, SChannel does not send Session Ticket.
OpenSSL and OpenSSL: Always session reused.
So I want to know that
Why Schannel don't use TLS session resumption only for OpenSSL 1.0.2?
The difference between 1.0.2 and 1.1.0.
How to use TLS session resumption at OpenSSL 1.0.2 and SChannel?
Server code: Simple TLS Server
Client code: Windows C++
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define SECURITY_WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <WinSock2.h>
#include <sspi.h>
#include <schannel.h>
#include <stdio.h>
#include <vector>
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "Secur32.lib")
struct WSA {
WSA() {
WSADATA wsaData;
if (auto result = WSAStartup(WINSOCK_VERSION, &wsaData))
throw result;
}
~WSA() {
WSACleanup();
}
};
struct Credential : CredHandle {
Credential() {
SCHANNEL_CRED cred = { .dwVersion = SCHANNEL_CRED_VERSION, .dwFlags = SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION };
if (auto ss = AcquireCredentialsHandleW(nullptr, UNISP_NAME_W, SECPKG_CRED_OUTBOUND, nullptr, &cred, nullptr, nullptr, this, nullptr); ss != SEC_E_OK)
throw ss;
}
~Credential() {
FreeCredentialsHandle(this);
}
};
struct Socket {
SOCKET s;
Socket(const char* target, int port) {
SOCKADDR_IN addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(target);
s = socket(AF_INET, SOCK_STREAM, 0);
if (s == INVALID_SOCKET)
throw WSAGetLastError();
if (connect(s, reinterpret_cast<const SOCKADDR*>(&addr), sizeof addr))
throw WSAGetLastError();
u_long val = 1;
ioctlsocket(s, FIONBIO, &val);
}
~Socket() {
closesocket(s);
}
auto Read() {
for (std::vector<unsigned char> result;;) {
char buffer[2048];
if (auto read = recv(s, buffer, sizeof buffer, 0); read == 0)
return result;
else if (read == SOCKET_ERROR) {
if (auto lastError = WSAGetLastError(); lastError != WSAEWOULDBLOCK)
throw lastError;
if (!empty(result))
return result;
Sleep(0);
} else
result.insert(end(result), buffer, buffer + read);
}
}
void Write(void* data, int length) {
for (auto p = reinterpret_cast<const char*>(data); 0 < length;) {
auto sent = send(s, p, length, 0);
if (sent == 0)
throw 0;
else if (sent == SOCKET_ERROR)
throw WSAGetLastError();
p += sent;
length -= sent;
}
}
};
int main() {
WSA wsa;
Credential credential;
for (int i = 0; i < 5; i++) {
Socket socket{ "127.0.0.1", 4433 };
std::vector<unsigned char> read;
auto first = true;
CtxtHandle context;
for (SECURITY_STATUS ss = SEC_I_CONTINUE_NEEDED; ss == SEC_I_CONTINUE_NEEDED;) {
SecBuffer inbuf[] = {
{ .BufferType = SECBUFFER_EMPTY },
{ .BufferType = SECBUFFER_EMPTY },
};
if (!first) {
auto data = socket.Read();
read.insert(end(read), begin(data), end(data));
inbuf[0] = { static_cast<unsigned long>(read.size()), SECBUFFER_TOKEN, read.data() };
}
SecBufferDesc indesc = { SECBUFFER_VERSION, 2, inbuf };
SecBuffer outbuf = { .BufferType = SECBUFFER_TOKEN };
SecBufferDesc outdesc = { SECBUFFER_VERSION, 1, &outbuf };
unsigned long attr = 0;
ss = InitializeSecurityContextW(&credential, first ? nullptr : &context, L"localhost", ISC_REQ_ALLOCATE_MEMORY, 0, SECURITY_NETWORK_DREP, &indesc, 0, &context, &outdesc, &attr, nullptr);
if (FAILED(ss))
throw ss;
first = false;
read.erase(begin(read), end(read) - (inbuf[1].BufferType == SECBUFFER_EXTRA ? inbuf[1].cbBuffer : 0));
if (outbuf.cbBuffer != 0) {
socket.Write(outbuf.pvBuffer, outbuf.cbBuffer);
FreeContextBuffer(outbuf.pvBuffer);
}
}
for (;;) {
SecBuffer buffer[] = {
{ static_cast<unsigned long>(read.size()), SECBUFFER_DATA, read.data() },
{ .BufferType = SECBUFFER_EMPTY },
{ .BufferType = SECBUFFER_EMPTY },
{ .BufferType = SECBUFFER_EMPTY },
};
SecBufferDesc desc{ SECBUFFER_VERSION, 4, buffer };
if (auto ss = DecryptMessage(&context, &desc, 0, nullptr); ss == SEC_I_CONTEXT_EXPIRED)
break;
else if (ss == SEC_E_OK) {
if (buffer[1].BufferType == SECBUFFER_DATA && 0 < buffer[1].cbBuffer && buffer[1].pvBuffer)
printf("%.*s", buffer[1].cbBuffer, reinterpret_cast<const char*>(buffer[1].pvBuffer));
read.erase(begin(read), end(read) - (buffer[3].BufferType == SECBUFFER_EXTRA ? buffer[3].cbBuffer : 0));
} else if (ss != SEC_E_INCOMPLETE_MESSAGE)
throw ss;
if (auto data = socket.Read(); empty(data))
break;
else
read.insert(end(read), begin(data), end(data));
}
if (auto ss = DeleteSecurityContext(&context); ss != SEC_E_OK)
throw ss;
}
}
I'm a maintainer of ftp client. Some ftps server requires that DATA connection must reuse the TLS session of CONTROL connection for security.
At Windows Update 2019/10, RFC7627 Extended Master Secret was enabled. SChannel requires RFC7627 EMS support when RFC5077 TLS Session Resumption.
OpenSSL suport RFC7627 extended master secret from 1.1.0. So SChannel cannot reuse TLS session with OpenSSL 1.0.2.

OpenLDAP - Enabling CRL check for LDAP TLS connections

I have a client that connects to LDAP server using TLS. For this connection, I want to enable CRL check and reject the connection only if any server/client certificates are revoked.
In special cases (like CRL missing, CRL expired) I want to ignore the error and establish the connection.
So I though to overwrite the default SSL verify call back to ignore the specific errors.
But the call back is not called at all. Always only default call-back is called.
Here is my call back:
static int verify_callback(int ok, X509_STORE_CTX *ctx)
{
X509* cert = X509_STORE_CTX_get_current_cert(ctx);
if (ok)
return ok;
int sslRet = X509_STORE_CTX_get_error(ctx);
const char* err = NULL;
switch (sslRet)
{
case X509_V_ERR_UNABLE_TO_GET_CRL:
case X509_V_ERR_CRL_HAS_EXPIRED:
case X509_V_ERR_CRL_NOT_YET_VALID:
printf( "CRL: Verification failed... but ignored : %d\n", sslRet);
return 1;
default:
err = X509_verify_cert_error_string(sslRet);
if (err)
printf( "CRL: Failed to verify : %s\n",err);
return 0;
}
return sslRet;
}
Default verify call-back is overwritten using the ldap call-back set option:
void ldap_tls_cb(LDAP * ld, SSL * ssl, SSL_CTX * ctx, void * arg)
{
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER , verify_callback);
printf("verify call back is set...\n");
return;
}
Main Program:
int main( int argc, char **argv )
{
LDAP *ldap;
int auth_method = LDAP_AUTH_SIMPLE; //LDAP_AUTH_SASL
int ldap_version = LDAP_VERSION3;
char *ldap_host = "10.104.40.35";
int ldap_port = 389;
if ( (ldap = ldap_init(ldap_host, ldap_port)) == NULL ) {
perror( "ldap_init failed" );
return( EXIT_FAILURE );
}
int result = ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
if (result != LDAP_OPT_SUCCESS ) {
ldap_perror(ldap, "ldap_set_option failed!");
return(EXIT_FAILURE);
}
int requireCert = LDAP_OPT_X_TLS_DEMAND;
result = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &requireCert);
if (result != LDAP_OPT_SUCCESS ) {
ldap_perror(ldap, "ldap_set_option - req cert -failed!");
return(EXIT_FAILURE);
}
result = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, "/etc/certs/Cert.pem");
if (result != LDAP_OPT_SUCCESS ) {
ldap_perror(ldap, "ldap_set_option - cert file - failed!");
return(EXIT_FAILURE);
}
int crlvalue = LDAP_OPT_X_TLS_CRL_ALL;
result =ldap_set_option(NULL, LDAP_OPT_X_TLS_CRLCHECK, &crlvalue);
if (result != LDAP_OPT_SUCCESS ) {
ldap_perror(ldap, "ldap_set_option failed!");
return(EXIT_FAILURE);
}
int debug = 7;
ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &debug);
result = ldap_set_option(ldap, LDAP_OPT_X_TLS_CONNECT_CB, (void *)ldap_tls_cb);
if (result != LDAP_SUCCESS) {
fprintf(stderr, "ldap_set_option(LDAP_OPT_X_TLS_CONNECT_CB): %s\n", ldap_err2string(result));
return(1);
}
int msgidp = 0;
result = ldap_start_tls(ldap,NULL,NULL,&msgidp);
if (result != LDAP_OPT_SUCCESS ) {
ldap_perror(ldap, "start tls failed!");
return result;
} else {
printf("Start tls success.\n");
}
LDAPMessage *resultm;
struct timeval timeout;
result = ldap_result(ldap, msgidp, 0, &timeout, &resultm );
if ( result == -1 || result == 0 ) {
printf("ldap_result failed;retC=%d \n", result);
return result;
}
result = ldap_parse_extended_result(ldap, resultm, NULL, NULL, 0 );
if ( result == LDAP_SUCCESS ) {
result = ldap_install_tls (ldap);
printf("installing tls... %s\n", ldap_err2string(result));
}
int request_id = 0;
result = ldap_sasl_bind(ldap, "", LDAP_SASL_SIMPLE, NULL, 0, 0, &request_id);
if ( result != LDAP_SUCCESS ) {
fprintf(stderr, "ldap_x_bind_s: %s\n", ldap_err2string(result));
printf("LDAP bind error .. %d\n", result);
return(EXIT_FAILURE);
} else {
printf("LDAP connection successful.\n");
}
ldap_unbind(ldap);
return(EXIT_SUCCESS);
}
can someone help to check why my verify call-back is not called?
I think you need to set the callback on the SSL object directly instead of the context, so
void ldap_tls_cb(LDAP * ld, SSL * ssl, SSL_CTX * ctx, void * arg)
{
SSL_set_verify(ssl, SSL_VERIFY_PEER, verify_callback);
printf("verify call back is set...\n");
return;
}
The reason for this is that the SSL handle has already been initialised by the time your connect callback is called (see the OpenLDAP code), and
it's too late to set this callback through the context at that point:
If no special callback was set before, the default callback for the underlying ctx is used, that was valid at the time ssl was created with SSL_new(3).
OpenLDAP can be built with GnuTLS, so you may need to check that it's using OpenSSL before setting the callback. The LDAP_OPT_X_TLS_PACKAGE option could be used for this (note that I haven't tested this code):
char* package = NULL;
int result = ldap_get_option(NULL, LDAP_OPT_X_TLS_PACKAGE, (void *)&package);
if (result != LDAP_OPT_SUCCESS) {
ldap_perror(ldap, "ldap_get_option failed!");
return(EXIT_FAILURE);
} else {
if (strcmp(package, "OpenSSL") == 0) {
// Set your callback
}
ldap_memfree(package);
}

SugarSync API get user's info: Error 401 unauthorized

I'm trying to get a request for the user's information using WinHttp functions. The REST API call for SugarSync is referenced here.
My code is as follows:
DWORD dwBytesWritten = 0;
DWORD dwStatusCode = 0;
DWORD dwSize = sizeof(DWORD);
BOOL bResults = FALSE;
string data;
HINTERNET hSession = NULL,
hConnect = NULL,
hRequest = NULL;
WINHTTP_PROXY_INFO proxyInfo;
winhttpintObj.setProxyInfo(proxyInfo);
HINTERNET hSession = WinHttpOpen( L"A WinHTTP Example Program/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
HINTERNET hConnect = WinHttpConnect( hSession, L"api.sugarsync.com",
INTERNET_DEFAULT_HTTPS_PORT, 0);
HINTERNET hRequest = WinHttpOpenRequest( hConnect,
L"GET", L"/user/<userID>",
NULL,
WINHTTP_NO_REFERER,
0,
WINHTTP_FLAG_SECURE);
string header("Authorization: ");
header += (accessToken);
header += "\n";
wstring ws = wstring(header.begin(), header.end());
WinHttpAddRequestHeaders( hRequest, ws.c_str(), (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD );
WinHttpSendRequest( hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS,
0,
WINHTTP_NO_REQUEST_DATA,
0,
0,
0 );
WinHttpReceiveResponse( hRequest, NULL);
WinHttpQueryHeaders( hRequest,
WINHTTP_QUERY_STATUS_CODE |
WINHTTP_QUERY_FLAG_NUMBER,
NULL,
&dwStatusCode,
&dwSize,
NULL );
cout << "dwStatusCode: " << dwStatusCode << endl;
The accessToken is https://api.sugarsync.com/authorization/....... The status code returned is 401: Authorization required. However, I've already specified it in the header. May I know where has the problem occurred and what is the possible way to resolve it? Thank you.

Readfile and Writefile in win32 fails with error code 1

Code :
hHCDev = CreateFileA(completeDeviceName,
//"F:\\test.txt",
GENERIC_WRITE|GENERIC_READ,
FILE_SHARE_WRITE|FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL);
if (hHCDev == INVALID_HANDLE_VALUE)
{
CloseHandle(hHCDev);
}
else
{
char bufRead[256] = {0};
DWORD countRead = 0;
BOOL result ;
result = ReadFile(hHCDev, bufRead, 5, &countRead, NULL) ;
if(!result)
{
printf("Reading file error %d\n", GetLastError());
}
char bufWrite[] = {'7', '8', '9', ' '};
DWORD countWritten = 0;
result = WriteFile(hHCDev, bufWrite, 3, &countWritten, NULL) ;
if(!result)
{
printf("Writing file error %d\n", GetLastError());
}
else
{
printf("sucess");
}
CloseHandle(hHCDev);
}
memset(completeDeviceName,0,256) ;
Description:
We tried to open the USB device connected using createfile(). Using Readfile and Writefile() calls we tried to communicate with the device. But these calls returned with error code 1. What might be the reason??
Your Help would be highly appreciated.
Best Regards
Suren
Try to run your application with administrator rights.