2017-02-24 22 views
0

我已经编写了一个凭证提供程序和一个密钥存储提供程序,以通过证书登录到Windows。由于这方面的文档非常含糊,我使用了微软的不同样本来让事情顺利进行。由NCRYPT_KEY_HANDLE指向的证书的结构

我想我已经快到了,但登录行为不可预测。有时我会遇到kerberos服务器(它抱怨证书),有时该进程失败,0x80090029没有任何信息,有时会崩溃。由于这些崩溃都必须处理访问冲突或空指针,并发生在各种地方(kerberos.dll,Windows.UI.Logon.dll,...)我认为这与我的关键结构有关,我将给定的NCRYT_KEY_HANDLE指向我的OpenKey实现中。

CNG-Kit中的KeyStorageProviderSample有一个示例,但依赖于存储在%AppData%中的RSA密钥。我没有可用的私钥,因为它被保存在安全的硬件,我只是有公共部分(即公证书),我通过下面的代码从另一个设备和进口阅读:

SECURITY_STATUS WINAPI KeyHandler::ReadPemCert(__inout KSP_KEY *keyHandle) 
{ 
    LOG_FUNCTION; 

    CERT_CONTEXT certContext = {}; 
    DWORD readLength = 0; 

    LOG("Fetch certificate"); 
    const int maxSizeInBytes = 4096; 
    char pemCertificateAsBytes[maxSizeInBytes]; 
    BluetoothClient bluetoothClient = BluetoothClient(); 
    bluetoothClient.getCertificate((PBYTE)pemCertificateAsBytes, readLength); 

    DWORD certAsDerLen = readLength; 
    BYTE* certAsDer = new BYTE[certAsDerLen]; 
    LOG("convert PEM to DER"); 
    if (!CryptStringToBinaryA(pemCertificateAsBytes, 0, CRYPT_STRING_BASE64, certAsDer, &certAsDerLen, NULL, NULL)) 
    { 
     LOG_LAST_ERROR("CryptStringToBinary failed. Err:"); 
    } 
    LOG_BYTES_AS_HEX("DER-Zertifikat", certAsDer, certAsDerLen); 

    PCCERT_CONTEXT pcCertContext = CertCreateCertificateContext(X509_ASN_ENCODING, certAsDer, certAsDerLen); 
    certContext->pCertInfo = pcCertContext->pCertInfo; 
    certContext->cbCertEncoded = pcCertContext->cbCertEncoded; 
    certContext->pbCertEncoded = pcCertContext->pbCertEncoded; 
    certContext->dwCertEncodingType = pcCertContext->dwCertEncodingType; 

    CERT_INFO *certInfo; 
    certInfo = certContext.pCertInfo; 

    CERT_PUBLIC_KEY_INFO pubKeyInfo = certInfo->SubjectPublicKeyInfo; 

    LOG("Aquire cryptocontext"); 
    HCRYPTPROV hProv = 0; 
    if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) 
    { 
     { 
      LOG_LAST_ERROR("CryptAcquireContext failed. Err:"); 
      return -1; 
     } 
    } 

    LOG("Importing public key"); 
    NCRYPT_KEY_HANDLE publicKeyHandle = NULL; 
    if (!CryptImportPublicKeyInfo(hProv, X509_ASN_ENCODING, &pubKeyInfo, &publicKeyHandle)) 
    { 
     LOG_LAST_ERROR("CryptImportPublicKeyInfo failed. Err:"); 
     return -1; 
    } 

    keyHandle->fFinished = TRUE; 
    keyHandle->hPublicKey = (BCRYPT_KEY_HANDLE)publicKeyHandle; 
    keyHandle->pszKeyBlobType = BCRYPT_RSAPUBLIC_BLOB; 

    LocalFree(certInfo); 

    return ERROR_SUCCESS; 
} 

关键结构以这种方式初始化:

SECURITY_STATUS 
WINAPI 
KeyHandler::CreateNewKeyObject(
    __in_opt LPCWSTR pszKeyName, 
    __deref_out KSP_KEY **ppKey) 
{ 
    LOG_FUNCTION; 

    KSP_KEY *pKey = NULL; 
    DWORD cbKeyName = 0; 
    SECURITY_STATUS Status = NTE_INTERNAL_ERROR; 
    NTSTATUS   ntStatus = STATUS_INTERNAL_ERROR; 

    pKey = (KSP_KEY *)HeapAlloc(GetProcessHeap(), 0, sizeof(KSP_KEY)); 
    if (pKey == NULL) 
    { 
     return NTE_NO_MEMORY; 
    } 
    pKey->cbLength = sizeof(KSP_KEY); 
    pKey->dwMagic = KSP_KEY_MAGIC; 
    pKey->dwAlgID = KSP_RSA_ALGID; 
    pKey->pszKeyFilePath = NULL; 
    pKey->pszKeyBlobType = NULL; 
    pKey->dwKeyBitLength = 0; 
    pKey->fFinished = FALSE; 

    //Copy the keyname into the key struct. 
    if (pszKeyName != NULL) 
    { 
     cbKeyName = (DWORD)(wcslen(pszKeyName) + 1) * sizeof(WCHAR); 
     pKey->pszKeyName = (LPWSTR)HeapAlloc(
      GetProcessHeap(), 
      0, 
      cbKeyName + sizeof(WCHAR)); 
     if (pKey->pszKeyName == NULL) 
     { 
      return NTE_NO_MEMORY; 
     } 
     CopyMemory(pKey->pszKeyName, pszKeyName, cbKeyName); 
     pKey->pszKeyName[cbKeyName/sizeof(WCHAR)] = L'\0'; 
    } 
    else 
    { 
     pKey->pszKeyName = NULL; 
    } 

    if (globalRSAProviderHandle == NULL) 
    { 
     ntStatus = BCryptOpenAlgorithmProvider(
      &globalRSAProviderHandle, 
      BCRYPT_RSA_ALGORITHM, 
      NULL, 
      0); 
     if (!NT_SUCCESS(ntStatus)) 
     { 
      return NormalizeNteStatus(ntStatus); 
     } 

    } 
    pKey->hProvider = globalRSAProviderHandle; 

    pKey->pbKeyFile = NULL; 
    pKey->cbKeyFile = 0; 

    pKey->pbPrivateKey = NULL; 
    pKey->cbPrivateKey = 0; 

    pKey->hPublicKey = NULL; 
    pKey->hPrivateKey = NULL; 

    pKey->dwExportPolicy = NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG; 

    pKey->dwKeyUsagePolicy = NCRYPT_ALLOW_DECRYPT_FLAG | NCRYPT_ALLOW_SIGNING_FLAG; 

    pKey->pbSecurityDescr = NULL; 
    pKey->cbSecurityDescr = 0; 

    InitializeListHead(&pKey->PropertyList); 
    *ppKey = pKey; 
    pKey = NULL; 
    return ERROR_SUCCESS; 
} 

某处存在导致各种内存错误的错误。但是因为我对Windows编程和c/C++颇为陌生,所以我只是无法发现这一点,并且找不到有关Windows对NCRYTP_KEY_HANDLE期望的数据结构的任何文档。 有人知道更多关于这个结构吗?

+0

我认为由于这个问题的深度它属于堆栈交换网络中的另一个站点:https://crypto.stackexchange.com –

回答

1

NCRYPT_KEY_HANDLE只是指向您定义的结构的指针。 Windows本身并不关心这个结构,并期望你的提供者知道如何使用它。

KeyHandler::ReadPemCert您混合了传统的CryptoAPI和CNG API。由于您正在实施KSP,因此只能使用CNG API(CryptImportPublicKeyInfoEx2)。

DWORD error = NTE_FAIL; 
BCRYPT_KEY_HANDLE hKey = NULL; 

... 

PCCERT_CONTEXT pcCertContext = CertCreateCertificateContext(X509_ASN_ENCODING, certAsDer, certAsDerLen); 
if(!pcCertContext) 
{ 
    goto Exit; 
} 


if (!CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, &pcCertContext->pCertInfo->SubjectPublicKeyInfo, 0, nullptr, &hKey)) 
{ 
    goto Exit; 
} 

/* Also you can export key and print out the result to make sure everything works 

    DWORD temp = 0; 
    status = BCryptExportKey(hKey, 0, BCRYPT_RSAPUBLIC_BLOB, nullptr, 0, &temp, 0); 
    if (status != ERROR_SUCCESS) 
    { 
     goto Exit; 
    } 

    std::vector<BYTE> key(temp); 
    status = BCryptExportKey(hKey, 0, BCRYPT_RSAPUBLIC_BLOB, key.data(), key.size(), &temp, 0); 
    if (status != ERROR_SUCCESS) 
    { 
     goto Exit; 
    } 

    for (auto const& i : key) 
    { 
     std::cout << std::hex << (int)i; 
    } 
} 
*/ 

keyHandle->fFinished = TRUE; 
keyHandle->hPublicKey = hKey; 
keyHandle->pszKeyBlobType = BCRYPT_RSAPUBLIC_BLOB; 

erro = ERROR_SUCCESS; 

Exit: 

if(pcCertContext) 
{ 
    CertFreeCertificateContext(pcCertContext); 
} 

return error;