2017-02-24 25 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;