2010-12-21 11 views
9

Estoy intentando comprender cómo obtener una clave pública importada del formato PEM (muestra incluida en el código a continuación) en XP, Vista y Windows 7. El código de muestra se mostrará importe la clave tanto en XP como en Windows Vista/7, pero no de la misma manera.Comportamiento de MS Crypto API en Windows XP vs Vista/7

En Windows XP, se requiere la cadena "(Prototipo)" en el nombre del proveedor de cifrado, y permite pasar la llamada a CryptImportPublicKeyInfo.

En Windows 7, el proveedor "(Prototype)" está aparentemente presente, pero no admite la llamada a CryptImportPublicKeyInfo, lo cual es confuso.

¿Cómo se vería una implementación correcta entre estos sistemas operativos? ¿Es necesario detectar XP y solicitar el nombre con "(Prototipo)", y sin él para otros sistemas operativos? ¿Es posible que eso aún falle en algunos sistemas XP?

O, ¿hay alguna manera de detectar este comportamiento confuso y seleccionar el proveedor criptográfico que admita la llamada necesaria?

de salida en 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****. 

de salida en 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ódigo C++ fuente que produce que la salida: (requiere 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; 
} 

Respuesta

10

La razón del problema que se describe es muy fácil: Microsoft renombró AES Cryptographic Provider de

  • "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" en Windows XP para
  • "Microsoft Enhanced RSA and AES Cryptographic Provider" en las últimas versiones de los sistemas operativos.

En WinCrypt.h se definen las constantes correspondientes como MS_ENH_RSA_AES_PROV y MS_ENH_RSA_AES_PROV_XP que se puede utilizar.

Si no desea comprobar la versión del sistema operativo sólo se puede utilizar con CryptAcquireContextNULL como pszProvider (y seguir utilizando PROV_RSA_AES como el dwProvType). En su código puede incluir analyzeCryptographicSupport(NULL);.

También puede examinar el valor "Nombre" de la clave de registro

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

para ver el nombre del proveedor predeterminado PROV_RSA_AES.

+1

Parece que la respuesta corta para mi caso es detectar XP y usar la cadena de proveedor adecuada ... desafortunada, pero no es un gran problema. – g01d

+0

@ g01d, eso funciona bien, pero la forma más "correcta" es detectar no la versión del sistema operativo, sino el nombre del cifrado en el registro y manejarlo desde allí. Dividir pelos, lo sé ... –

+0

@ Prof.Falken: De la causa uno puede detectar el sistema operativo. Escribí que uno puede usar 'pszProvider = NULL' y' dwProvType = PROV_RSA_AES' alternativamente. Intenté * explicar * en mi respuesta que el verdadero origen del problema es la constante de uso 'MS_ENH_RSA_AES_PROV' que es solo la cadena del registro. Supongo que Microsoft cometió un error en Windows XP con el uso de texto incorrecto con el sufijo "Prototipo" innecesario en el registro. Entonces uno "corrigió" el error introduciendo 'MS_ENH_RSA_AES_PROV_XP'. Si uno entiende ** el motivo ** del problema, puede elegir la mejor solución. – Oleg

0

Creo que rem Leyendo en alguna parte que Microsoft se metió en el nombre y requiere que "(Prototipo)" esté presente en XP y ausente en Vista y más arriba. Creo que deberás detectar la plataforma en tiempo de ejecución y usar la cadena apropiada.

Cuestiones relacionadas