简体   繁体   English

使用私钥和登录域签署证书

[英]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.我正在使用自定义 KSP 和我的凭据提供程序的证书实施与登录域相关的程序。 I have successfully interacted from my credential provider to custom KSP.我已经成功地从我的凭证提供者交互到自定义 KSP。 I'm in the process of implementing custom KSP.我正在实施自定义 KSP。 The steps I perform handling in custom KSP are as follows:我在自定义 KSP 中执行处理的步骤如下:

  1. Install the template certificate Kerberos that has been issued from ADCS to local machine store.将已从 ADCS 颁发的模板证书 Kerberos 安装到本地计算机存储。
  2. Export the private key from the file (.pfx) that has been issued from ADCS via the command:通过以下命令从 ADCS 发布的文件 (.pfx) 中导出私钥:
#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:流程自定义 KSP 如下所示:
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 .SampleKSPSignHash中,我读取了私钥并导入了密钥,然后实现了函数BCryptCreateHashBCryptHashDataBCryptFinishHash ,最后是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 :数据 hash 将从SampleKSPGetKeyProperty获取,方法是从本地机器store(CertContext->pbCertEncoded)读取证书。但是我在使用hash数据时遇到问题,并且在BCryptSignHash示例代码期间出现错误。
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:接下来是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%.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.我已经很久没有写过类似的东西了,但如果我没记错的话,你需要调用 BCryptSignHash 两次。 The first time to get the expected size of the signature and the second time to actually do the signing.第一次得到预期的签名大小,第二次实际做签名。

pbOutput pb输出

The address of a buffer to receive the signature produced by this function.用于接收此 function 生成的签名的缓冲区的地址。 The cbOutput parameter contains the size of this buffer. cbOutput 参数包含此缓冲区的大小。

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.如果这个参数是NULL,这个function会计算签名所需的大小,并在pcbResult参数指向的位置返回大小。

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.即使我已经知道尺寸并将其交给 function,它仍然抱怨 STATUS_INVALID_PARAMETER 是 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.请务必仔细阅读 windows 加密 API 的文档,因为其中有一些问题。 ;-) ;-)

EDIT编辑

Looking closer at your example I see that you have 0 as the last parameter in your call to BCryptSignHash.仔细查看您的示例,我发现您在调用 BCryptSignHash 时将 0 作为最后一个参数。 According to the documentation this should be 0x00000002 (PKCS1) or 0x00000008 (PSS):根据文档,这应该是 0x00000002 (PKCS1) 或 0x00000008 (PSS):

dwFlags dwFlags

A set of flags that modify the behavior of this function.一组修改此 function 行为的标志。 The allowed set of flags depends on the type of key specified by the hKey parameter.允许的标志集取决于 hKey 参数指定的密钥类型。

This can be one of the following values.这可以是以下值之一。

BCRYPT_PAD_PKCS1 Use the PKCS1 padding scheme. BCRYPT_PAD_PKCS1 使用 PKCS1 填充方案。 The pPaddingInfo parameter is a pointer to a BCRYPT_PKCS1_PADDING_INFO structure. pPaddingInfo 参数是一个指向 BCRYPT_PKCS1_PADDING_INFO 结构的指针。

BCRYPT_PAD_PSS Use the Probabilistic Signature Scheme (PSS) padding scheme. BCRYPT_PAD_PSS 使用概率签名方案 (PSS) 填充方案。 The pPaddingInfo parameter is a pointer to a BCRYPT_PSS_PADDING_INFO structure. pPaddingInfo 参数是一个指向 BCRYPT_PSS_PADDING_INFO 结构的指针。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM