Поведение MS Crypto API в Windows XP против Vista/7

Я пытаюсь понять, как получить открытый ключ, импортированный из формата PEM (пример включен в код ниже) через XP, Vista и Windows 7. Пример кода импортирует ключ как на XP, так и на Windows Vista/7, но не одинаково.

в Windows XP строка "(прототип)" требуется в имени поставщика криптографии и позволяет передавать вызов CryptImportPublicKeyInfo.

в Windows 7 "(прототип)" поставщик, по-видимому, присутствует, но не поддерживает вызов CryptImportPublicKeyInfo, что сбивает с толку.

как может выглядеть правильная реализация между этими операционными системами? Необходимо ли обнаружить XP и запросить имя с помощью "(прототип)", и без него для других операционных систем? Возможно ли, что это все равно не удастся на некоторых системах 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-----rn"
    L"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6GUVcbn92bahlwOskKi8XkG9qrn"
    L"Vq863+C4cOWC6HzJojc011pJFFIBu8/pG1EI8FZJdBmTrFaJTriYw1/SpbOH0QqErn"
    L"eHanT8qWn+S5m9xgDJoWTBJKcnu3OHOvJJU3c8jOHQQnRWLfghJH4vnwStdiwUUYrn"
    L"SMWpwuHObsNelGBgEQIDAQABrn"
    L"-----END PUBLIC KEY-----rn";
int pemPublicKeySize = wcslen(pwszPemPublicKey);

bool analyzeCryptographicSupport(LPCTSTR pProviderName) {

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

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

    DWORD blobSize;

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

    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****.rn");
        return false;
    }
    wprintf(L"t CryptStringToBinary.2 success.rn");

    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****.rn");
        return false;
    }
    wprintf(L"t CryptDecodeObjectEx success.rn");

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

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

    wprintf(L"t SUCCESS.rn");
    return true;
}

2 ответов


причина проблемы, которую вы описываете, очень проста:Microsoft переименовала поставщика шифрования AES С

  • "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" в Windows XP до
  • "Microsoft Enhanced RSA and AES Cryptographic Provider" в более поздних версиях операционных систем.

на WinCrypt.h определены соответствующие константы как MS_ENH_RSA_AES_PROV и MS_ENH_RSA_AES_PROV_XP, который вы можете использовать.

если вы не хотите, чтобы проверить версию операционной системы, вы можете просто использовать CryptAcquireContext С NULL as pszProvider (и продолжают использовать PROV_RSA_AES как dwProvType). В свой код вы можете включить analyzeCryptographicSupport(NULL);.

вы также можете проверить значение " имя " раздела реестра

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

чтобы увидеть имя по умолчанию PROV_RSA_AES провайдер.


кажется, я где-то читал, что Microsoft облажалась с именем, и для этого требуется "(прототип)" присутствовать на XP и отсутствовать на Vista и выше. Я думаю, вам придется обнаружить платформу во время выполнения и использовать соответствующую строку.