le comportement de l'API MS Crypto sous Windows XP vs Vista / 7
-
12-10-2019 - |
Question
Je suis en train de comprendre comment obtenir une clé publique importée du format PEM (échantillon inclus dans le code ci-dessous) dans XP, Vista et Windows 7. Le code échantillon importera la clé sur XP et Windows Vista / 7 , mais pas de la même manière.
Sous Windows XP, la chaîne "(Prototype)" est nécessaire au nom du fournisseur cryptographique et permet de passer l'appel à CryptImportPublicKeyInfo.
Sur Windows 7, le "(Prototype)" fournisseur est apparemment présent, mais ne supporte pas l'appel à CryptImportPublicKeyInfo, qui est source de confusion.
Que pourrait regarder de mise en œuvre correcte comme entre ces systèmes d'exploitation? Est-il nécessaire de détecter XP et demander le nom avec « (Prototype) » , et sans pour d'autres systèmes d'exploitation? Est-il possible que cela encore échouer sur certains systèmes XP?
Ou, est-il un moyen de détecter ce comportement déroutant et sélectionnez selon le fournisseur cryptographique soutiendra l'appel nécessaire?
Sortie sur 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****.
Sortie sur 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.
code source C qui produit de cette sortie: (nécessite 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;
}
La solution
La raison du problème qui vous décrit est très simple: Microsoft renommé fournisseur AES Cryptographic
-
"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)"
dans Windows XP à -
"Microsoft Enhanced RSA and AES Cryptographic Provider"
dans les versions ultérieures des systèmes d'exploitation.
Dans WinCrypt.h
sont définies les constantes correspondantes comme MS_ENH_RSA_AES_PROV
et MS_ENH_RSA_AES_PROV_XP
que vous pouvez utiliser.
Si vous ne voulez pas tester la version du système d'exploitation, vous pouvez simplement utiliser CryptAcquireContext avec NULL
comme pszProvider
(et continuer à utiliser PROV_RSA_AES
comme dwProvType
). Dans votre code, vous pouvez inclure analyzeCryptographicSupport(NULL);
.
Vous pouvez également examiner la valeur « Nom » de la clé de Registre
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults\Provider Types\Type 024
pour voir le nom du fournisseur de PROV_RSA_AES
par défaut.
Autres conseils
Je pense que je me souviens avoir lu quelque part que Microsoft gaffé sur le nom et il faut « (Prototype) » pour être présent sur XP et Vista absent et au-dessus. Je pense que vous devrez détecter la plate-forme lors de l'exécution et utiliser la chaîne appropriée.