2010-12-21 72 views
9

我試圖瞭解如何在XP,Vista和Windows 7上從PEM格式(下面的代碼中包含樣本)導入公鑰。示例代碼將在XP和Windows Vista/7上導入密鑰,但方式不同。Windows XP和Vista/7上的MS Crypto API行爲

在Windows XP上,密碼提供程序名稱中需要字符串「(原型)」,並允許調用CryptImportPublicKeyInfo來傳遞。

在Windows 7上,「(原型)」提供程序顯然存在,但不支持對CryptImportPublicKeyInfo的調用,這很令人困惑。

這些操作系統之間可能有什麼正確的實現?是否有必要檢測XP並請求「(Prototype)」的名稱,並且在其他操作系統上沒有該名稱?有可能在某些XP系統上仍會失敗嗎?

或者,有沒有辦法來檢測這種令人困惑的行爲,並選擇哪個加密提供程序將支持必要的調用?在Windows 7

輸出:

ANALYZING CRYPTOGRAPHIC SUPPORT FOR: 
    "Microsoft Enhanced RSA and AES Cryptographic Provider" 
    CryptAcquireContext success. 
    CryptAcquireContext.1 success. 
    CryptStringToBinary.2 success. 
    CryptDecodeObjectEx success. 
    CryptImportPublicKeyInfo success. 
    SUCCESS. 
ANALYZING CRYPTOGRAPHIC SUPPORT FOR: 
    "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" 
    CryptAcquireContext success. 
    CryptAcquireContext.1 success. 
    CryptStringToBinary.2 success. 
    CryptDecodeObjectEx success. 
    CryptImportPublicKeyInfo FAILED****. 

在Windows XP輸出:其產生輸出

ANALYZING CRYPTOGRAPHIC SUPPORT FOR: 
    "Microsoft Enhanced RSA and AES Cryptographic Provider" 
    CryptAcquireContext success. 
    CryptAcquireContext.1 success. 
    CryptStringToBinary.2 success. 
    CryptDecodeObjectEx success. 
    CryptImportPublicKeyInfo FAILED****. 
ANALYZING CRYPTOGRAPHIC SUPPORT FOR: 
    "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" 
    CryptAcquireContext success. 
    CryptAcquireContext.1 success. 
    CryptStringToBinary.2 success. 
    CryptDecodeObjectEx success. 
    CryptImportPublicKeyInfo success. 
    SUCCESS. 

C++源代碼:(需要crypt32.lib)

#include <stdio.h> 
#include <tchar.h> 
#include <windows.h> 
#include <wincrypt.h> 

bool windowsAcquireProviderContext(HCRYPTPROV *pHandleProv, LPCTSTR pProviderName); 
bool analyzeCryptographicSupport(LPCTSTR pProviderName); 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    analyzeCryptographicSupport(MS_ENH_RSA_AES_PROV); 
    analyzeCryptographicSupport(L"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)"); 
    return 0; 
} 

bool windowsAcquireProviderContext(HCRYPTPROV *pHandleProv, LPCTSTR pProviderName) { 
    WCHAR *pContainerName = L"blah blah blah"; 
    if(!CryptAcquireContext(pHandleProv, pContainerName, pProviderName, PROV_RSA_AES, CRYPT_SILENT)) { 
     if(GetLastError() == NTE_BAD_KEYSET) { 
      if(CryptAcquireContext(pHandleProv, pContainerName, pProviderName, PROV_RSA_AES, CRYPT_NEWKEYSET|CRYPT_SILENT)) { 
       return true; 
      } 
     } 
    } 
    return true; 
} 

LPCWSTR pwszPemPublicKey = 
    L"-----BEGIN PUBLIC KEY-----\r\n" 
    L"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6GUVcbn92bahlwOskKi8XkG9q\r\n" 
    L"Vq863+C4cOWC6HzJojc011pJFFIBu8/pG1EI8FZJdBmTrFaJTriYw1/SpbOH0QqE\r\n" 
    L"eHanT8qWn+S5m9xgDJoWTBJKcnu3OHOvJJU3c8jOHQQnRWLfghJH4vnwStdiwUUY\r\n" 
    L"SMWpwuHObsNelGBgEQIDAQAB\r\n" 
    L"-----END PUBLIC KEY-----\r\n"; 
int pemPublicKeySize = wcslen(pwszPemPublicKey); 

bool analyzeCryptographicSupport(LPCTSTR pProviderName) { 

    printf("ANALYZING CRYPTOGRAPHIC SUPPORT FOR:\r\n"); 
    wprintf(L"\t \"%s\"\r\n", pProviderName); 

    HCRYPTPROV hProv; 
    if(!windowsAcquireProviderContext(&hProv, pProviderName)) { 
     wprintf(L"\t CryptAcquireContext FAILED.\r\n"); 
     return false; 
    } 
    wprintf(L"\t CryptAcquireContext success.\r\n"); 

    DWORD blobSize; 

    if(!CryptStringToBinary(pwszPemPublicKey, pemPublicKeySize, CRYPT_STRING_BASE64_ANY, NULL, &blobSize, NULL, NULL)) { 
     CryptReleaseContext(hProv, 0); 
     wprintf(L"\t CryptStringToBinary.1 FAILED****.\r\n"); 
     return false; 
    } 
    wprintf(L"\t CryptAcquireContext.1 success.\r\n"); 

    BYTE *pBlob = (BYTE *)malloc(blobSize); 

    if(!CryptStringToBinary(pwszPemPublicKey, pemPublicKeySize, CRYPT_STRING_BASE64_ANY, pBlob, &blobSize, NULL, NULL)) { 
     free(pBlob); 
     CryptReleaseContext(hProv, 0); 
     wprintf(L"\t CryptStringToBinary.2 FAILED****.\r\n"); 
     return false; 
    } 
    wprintf(L"\t CryptStringToBinary.2 success.\r\n"); 

    CERT_PUBLIC_KEY_INFO *publicKeyInfo; 
    DWORD publicKeyInfoLen; 
    HCRYPTKEY hPublicKey; 

    if(!CryptDecodeObjectEx(X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pBlob, blobSize, CRYPT_DECODE_ALLOC_FLAG, NULL, &publicKeyInfo, &publicKeyInfoLen)) { 
     free(pBlob); 
     CryptReleaseContext(hProv, 0); 
     wprintf(L"\t CryptDecodeObjectEx FAILED****.\r\n"); 
     return false; 
    } 
    wprintf(L"\t CryptDecodeObjectEx success.\r\n"); 

    if(!CryptImportPublicKeyInfo(hProv, X509_ASN_ENCODING|PKCS_7_ASN_ENCODING, publicKeyInfo, &hPublicKey)) { 
     LocalFree(publicKeyInfo); 
     free(pBlob); 
     CryptReleaseContext(hProv, 0); 
     wprintf(L"\t CryptImportPublicKeyInfo FAILED****.\r\n"); 
     return false; 
    } 
    wprintf(L"\t CryptImportPublicKeyInfo success.\r\n"); 

    CryptDestroyKey(hPublicKey); 
    LocalFree(publicKeyInfo); 
    free(pBlob); 
    CryptReleaseContext(hProv, 0); 

    wprintf(L"\t SUCCESS.\r\n"); 
    return true; 
} 

回答

10

,你能說明問題的原因很簡單:微軟在操作系統的更新版本改名爲AES加密提供

  • "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)"在Windows XP中
  • "Microsoft Enhanced RSA and AES Cryptographic Provider"

WinCrypt.h定義相應的常量作爲MS_ENH_RSA_AES_PROVMS_ENH_RSA_AES_PROV_XP它可以使用。

如果你不想要測試的操作系統的版本,你可以只使用CryptAcquireContextNULLpszProvider(並繼續使用PROV_RSA_AESdwProvType)。在你的代碼中,你可以包括analyzeCryptographicSupport(NULL);

您還可以檢查註冊表項

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults\Provider Types\Type 024 

的「名稱」值,看看默認PROV_RSA_AES供應商的名稱。

+1

看我的情況的簡短答案是檢測XP並使用適當的提供程序字符串...不幸的,但不是一個非常大的交易。 – g01d 2011-03-02 21:38:47

+0

@ g01d,運行良好,但更「正確」的方法是檢測操作系統版本,但註冊表中的加密名稱,如果從那裏處理。分裂毛髮,我知道... – 2013-08-09 12:04:28

+0

@ Prof.Falken:因爲人們可以檢測操作系統。我寫道,可以使用'pszProvider = NULL'和'dwProvType = PROV_RSA_AES'。我在我的答案中嘗試*解釋*,問題的真正來源是使用'MS_ENH_RSA_AES_PROV'常量,它只是註冊表中的字符串。我猜想微軟在Windows XP中犯了一個錯誤,在註冊表中使用了不需要的「(原型)」後綴的錯誤文本。所以通過引入'MS_ENH_RSA_AES_PROV_XP'來「糾正」錯誤。如果理解**問題的原因**可以選擇最佳解決方案。 – Oleg 2013-08-11 13:42:01

0

我想我rem ember閱讀某處微軟在名字上冒充的地方,它需要「(原型)」出現在XP上,而在Vista和以上版本中則缺席。我想你必須在運行時檢測平臺並使用適當的字符串。