From: Benoît Legat <benoit.legat@gmail.com> PFXImportCertStore's import_key opened a PROV_RSA_FULL / MS_ENHANCED_PROV context. That provider predates SHA-2 and rejects CryptCreateHash(CALG_SHA_256, ...) with NTE_BAD_ALGID. Any code path that later signs with the PFX-imported key using SHA-256 (which is every modern TLS 1.2+ handshake, all TLS 1.3, and every default .NET 8 signature) then fails at signature time. Open MS_ENH_RSA_AES_PROV / PROV_RSA_AES instead. It is a strict superset (same RSA, plus AES, plus SHA-2) and is what current Windows uses for PFX-imported keys. --- dlls/crypt32/pfx.c | 2 +- dlls/crypt32/tests/store.c | 52 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/dlls/crypt32/pfx.c b/dlls/crypt32/pfx.c index a4a493e41ba..81f0faebd2b 100644 --- a/dlls/crypt32/pfx.c +++ b/dlls/crypt32/pfx.c @@ -51,7 +51,7 @@ static HCRYPTPROV import_key( cert_store_data_t data, DWORD flags ) if (CRYPT32_CALL( import_store_key, ¶ms ) != STATUS_BUFFER_TOO_SMALL) goto done; acquire_flags = (flags & CRYPT_MACHINE_KEYSET) | CRYPT_NEWKEYSET; - if (!CryptAcquireContextW( &prov, container, MS_ENHANCED_PROV_W, PROV_RSA_FULL, acquire_flags )) + if (!CryptAcquireContextW( &prov, container, MS_ENH_RSA_AES_PROV_W, PROV_RSA_AES, acquire_flags )) { WARN( "CryptAcquireContextW failed %08lx\n", GetLastError() ); goto done; diff --git a/dlls/crypt32/tests/store.c b/dlls/crypt32/tests/store.c index 81da1fad4e4..4240fd339a3 100644 --- a/dlls/crypt32/tests/store.c +++ b/dlls/crypt32/tests/store.c @@ -3596,6 +3596,57 @@ done: ; } +static void test_PFXImportCertStore_sha256_signing(void) +{ + CRYPT_DATA_BLOB pfx = { sizeof(pfxdata), (BYTE *)pfxdata }; + BYTE buf[512]; + CRYPT_KEY_PROV_INFO *info = (CRYPT_KEY_PROV_INFO *)buf; + HCERTSTORE store; + const CERT_CONTEXT *cert; + HCRYPTPROV prov; + HCRYPTHASH hash; + DWORD size; + BOOL ret; + + store = PFXImportCertStore( &pfx, NULL, CRYPT_EXPORTABLE | CRYPT_USER_KEYSET ); + ok( store != NULL, "PFXImportCertStore failed: %lu\n", GetLastError() ); + if (!store) return; + + cert = CertFindCertificateInStore( store, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL ); + ok( cert != NULL, "no cert in store: %08lx\n", GetLastError() ); + if (!cert) goto done_close; + + size = sizeof(buf); + ret = CertGetCertificateContextProperty( cert, CERT_KEY_PROV_INFO_PROP_ID, info, &size ); + ok( ret, "cert has no KEY_PROV_INFO: %08lx\n", GetLastError() ); + if (!ret) goto done_cert; + + ok( info->dwProvType == PROV_RSA_AES, + "PFX-imported key sits in provider type %lu, expected PROV_RSA_AES (%u) so that " + "SHA-256 signing works; PROV_RSA_FULL (1) can only hash MD5 and SHA-1\n", + info->dwProvType, PROV_RSA_AES ); + + if (info->dwProvType == PROV_RSA_AES) + { + ret = CryptAcquireContextW( &prov, info->pwszContainerName, info->pwszProvName, + info->dwProvType, 0 ); + ok( ret, "CryptAcquireContextW(PROV_RSA_AES) failed: %08lx\n", GetLastError() ); + if (ret) + { + ret = CryptCreateHash( prov, CALG_SHA_256, 0, 0, &hash ); + ok( ret, "CryptCreateHash(CALG_SHA_256) failed on PFX-imported key's CSP: %08lx " + "— this is what NTE_BAD_ALGID looks like from userspace\n", GetLastError() ); + if (ret) CryptDestroyHash( hash ); + CryptReleaseContext( prov, 0 ); + } + } + +done_cert: + CertFreeCertificateContext( cert ); +done_close: + CertCloseStore( store, 0 ); +} + static void test_PFXExportCertStoreEx(void) { HCERTSTORE store, store2; @@ -3825,6 +3876,7 @@ START_TEST(store) test_I_UpdateStore(); test_PFXImportCertStore(); test_PFXImportCertStore_unique_containers(); + test_PFXImportCertStore_sha256_signing(); test_PFXExportCertStoreEx(); test_CryptQueryObject(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11317