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;
}
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
@ 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é ... –
@ 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