我正在通过自定义 KSP 和我的凭据提供程序使用证书实现与登录域相关的程序。我已成功从我的凭据提供程序交互到自定义 KSP。我正在实施自定义 KSP。我在自定义 KSP 中执行处理的步骤如下:
- 安装已从 ADCS 颁发到本地计算机存储的模板证书 Kerberos。
- 通过以下命令从 ADCS 发出的文件 (.pfx) 导出私钥:
#openssl pkcs12 -in sample.pfx -nocerts -nodes -out sample.key.
#openssl rsa -in sample.key -out sample_private.key.
- 流自定义 KSP 如下所示:
SampleKSPOpenProvider -> SampleKSPOpenKey-> SampleKSPGetKeyProperty -> SampleKSPSignHash.
- 在
SampleKSPSignHash
中,我读取私钥并导入密钥,然后实现BCryptCreateHash
、BCryptHashData
、BCryptFinishHash
,最后BCryptSignHash
等函数。数据哈希将通过从本地计算机读取证书store(CertContext->pbCertEncoded)
从SampleKSPGetKeyProperty
中获取。但是我在哈希数据方面遇到问题,并且在BCryptSignHash
期间出现错误。下面是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);
}
}
....
}
接下来是SampleKSPSignHash的代码,调用BCryptSignHash时失败:
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%.8Xn",
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;
}
我认为在执行这样的过程并调用凭据提供程序后,将登录到域。我理解正确吗?- 提前感谢。
我已经很久没有写过这样的东西了,但是如果我没记错的话,你需要调用BCryptSignHash两次。第一次获取签名的预期大小,第二次实际进行签名。
铅输出
用于接收由此生成的签名的缓冲区的地址 功能。cbOutput 参数包含此缓冲区的大小。
如果此参数为 NULL,则此函数将计算大小 签名需要并返回所指位置的尺寸 通过 pcbResult 参数。
即使我已经知道大小并将其交给功能,它仍然抱怨STATUS_INVALID_PARAMETER这是0xC000000D的翻译。只有在我叫了两次之后,事情才开始起作用。请务必仔细阅读Windows加密API的文档,因为其中有一些问题。;-)
编辑
仔细观察您的示例,我发现您在调用 BCryptSignHash 时将 0 作为最后一个参数。 根据文档,这应该是0x00000002(PKCS1)或0x00000008(PSS):
dwFlags
一组修改此函数行为的标志。允许的 标志集取决于 hKey 指定的键的类型 参数。
这可以是以下值之一。
BCRYPT_PAD_PKCS1 使用 PKCS1 填充方案。pPaddingInfo 参数是指向BCRYPT_PKCS1_PADDING_INFO结构的指针。
BCRYPT_PAD_PSS 使用概率签名方案 (PSS) 填充 方案。pPaddingInfo 参数是指向 BCRYPT_PSS_PADDING_INFO结构。