Some applications (including .NET Core) specify the PKCS12_ALWAYS_CNG_KSP flag when calling PFXImportCertStore. According to Microsoft's documentation, this flag indicates that the CNG key storage provider should always be used, but if it is not available the import will not fail.
Wine does not implement a CNG KSP, so instead of failing we simply ignore the flag and continue with the existing import path. A WARN trace is printed for visibility.
Notably, .NET Core automatically adds PKCS12_ALWAYS_CNG_KSP when the ephemeral key storage flag (X509KeyStorageFlags.EphemeralKeySet) is used. Without support for this flag, certificate import fails in ASP.NET Core applications using Kestrel with ephemeral server certificates.
This improves compatibility with .NET and other applications expecting this flag to be accepted without error.
-- v2: Address reviewer feedback: FIXME instead of WARN, changed tests.
From: Christian Tinauer christian.tinauer@gmail.com
Some applications (including .NET Core) specify the PKCS12_ALWAYS_CNG_KSP flag when calling PFXImportCertStore. According to Microsoft's documentation, this flag indicates that the CNG key storage provider should always be used, but if it is not available the import will not fail.
Wine does not implement a CNG KSP, so instead of failing we simply ignore the flag and continue with the existing import path. A WARN trace is printed for visibility.
Notably, .NET Core automatically adds PKCS12_ALWAYS_CNG_KSP when the ephemeral key storage flag (X509KeyStorageFlags.EphemeralKeySet) is used. Without support for this flag, certificate import fails in ASP.NET Core applications using Kestrel with ephemeral server certificates.
This improves compatibility with .NET and other applications expecting this flag to be accepted without error. --- dlls/crypt32/pfx.c | 6 +++++- dlls/crypt32/tests/store.c | 2 +- dlls/secur32/tests/schannel.c | 2 +- dlls/winhttp/tests/winhttp.c | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/dlls/crypt32/pfx.c b/dlls/crypt32/pfx.c index 54eb7f25e1b..4d6cc7d5160 100644 --- a/dlls/crypt32/pfx.c +++ b/dlls/crypt32/pfx.c @@ -152,11 +152,15 @@ HCERTSTORE WINAPI PFXImportCertStore( CRYPT_DATA_BLOB *pfx, const WCHAR *passwor SetLastError( ERROR_INVALID_PARAMETER ); return NULL; } - if (flags & ~(CRYPT_EXPORTABLE|CRYPT_USER_KEYSET|CRYPT_MACHINE_KEYSET|PKCS12_NO_PERSIST_KEY)) + if (flags & ~(CRYPT_EXPORTABLE|CRYPT_USER_KEYSET|CRYPT_MACHINE_KEYSET|PKCS12_NO_PERSIST_KEY|PKCS12_ALWAYS_CNG_KSP)) { FIXME( "flags %08lx not supported\n", flags ); return NULL; } + if (flags & PKCS12_ALWAYS_CNG_KSP) + { + WARN( "flag PKCS12_ALWAYS_CNG_KSP ignored\n" ); + } if (CRYPT32_CALL( open_cert_store, &open_params )) return NULL;
prov = import_key( data, flags ); diff --git a/dlls/crypt32/tests/store.c b/dlls/crypt32/tests/store.c index 3c0b6a166e1..7a3bdf3df0f 100644 --- a/dlls/crypt32/tests/store.c +++ b/dlls/crypt32/tests/store.c @@ -3312,7 +3312,7 @@ static void test_PFXImportCertStore(void)
pfx.pbData = (BYTE *)pfxdata; pfx.cbData = sizeof(pfxdata); - store = PFXImportCertStore( &pfx, NULL, CRYPT_EXPORTABLE|CRYPT_USER_KEYSET|PKCS12_NO_PERSIST_KEY ); + store = PFXImportCertStore( &pfx, NULL, CRYPT_EXPORTABLE|CRYPT_USER_KEYSET|PKCS12_NO_PERSIST_KEY|PKCS12_ALWAYS_CNG_KSP ); ok( store != NULL, "got %lu\n", GetLastError() ); count = countCertsInStore( store ); ok( count == 1, "got %lu\n", count ); diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c index 2f693c6f871..f68db49da98 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -1395,7 +1395,7 @@ static void test_communication(void)
pfx.pbData = (BYTE *)pfxdata; pfx.cbData = sizeof(pfxdata); - store = PFXImportCertStore(&pfx, NULL, CRYPT_EXPORTABLE|CRYPT_USER_KEYSET|PKCS12_NO_PERSIST_KEY); + store = PFXImportCertStore(&pfx, NULL, CRYPT_EXPORTABLE|CRYPT_USER_KEYSET|PKCS12_NO_PERSIST_KEY|PKCS12_ALWAYS_CNG_KSP); ok(store != NULL, "got %lu\n", GetLastError());
cert = CertFindCertificateInStore(store, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL); diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c index 77b75fb55d6..082295e9081 100644 --- a/dlls/winhttp/tests/winhttp.c +++ b/dlls/winhttp/tests/winhttp.c @@ -6190,7 +6190,7 @@ static void test_client_cert_authentication(void)
pfx.pbData = (BYTE *)pfxdata; pfx.cbData = sizeof(pfxdata); - store = PFXImportCertStore( &pfx, NULL, CRYPT_EXPORTABLE|CRYPT_USER_KEYSET|PKCS12_NO_PERSIST_KEY ); + store = PFXImportCertStore( &pfx, NULL, CRYPT_EXPORTABLE|CRYPT_USER_KEYSET|PKCS12_NO_PERSIST_KEY|PKCS12_ALWAYS_CNG_KSP ); ok( store != NULL, "got %lu\n", GetLastError() );
cert = CertFindCertificateInStore( store, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL );
From: Christian Tinauer christian.tinauer@gmail.com
--- dlls/crypt32/pfx.c | 2 +- dlls/crypt32/tests/store.c | 12 +++++++++++- dlls/secur32/tests/schannel.c | 2 +- dlls/winhttp/tests/winhttp.c | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/dlls/crypt32/pfx.c b/dlls/crypt32/pfx.c index 4d6cc7d5160..a5a5e4b9087 100644 --- a/dlls/crypt32/pfx.c +++ b/dlls/crypt32/pfx.c @@ -159,7 +159,7 @@ HCERTSTORE WINAPI PFXImportCertStore( CRYPT_DATA_BLOB *pfx, const WCHAR *passwor } if (flags & PKCS12_ALWAYS_CNG_KSP) { - WARN( "flag PKCS12_ALWAYS_CNG_KSP ignored\n" ); + FIXME( "flag PKCS12_ALWAYS_CNG_KSP ignored\n" ); } if (CRYPT32_CALL( open_cert_store, &open_params )) return NULL;
diff --git a/dlls/crypt32/tests/store.c b/dlls/crypt32/tests/store.c index 7a3bdf3df0f..1a899d1f1cd 100644 --- a/dlls/crypt32/tests/store.c +++ b/dlls/crypt32/tests/store.c @@ -3312,7 +3312,7 @@ static void test_PFXImportCertStore(void)
pfx.pbData = (BYTE *)pfxdata; pfx.cbData = sizeof(pfxdata); - store = PFXImportCertStore( &pfx, NULL, CRYPT_EXPORTABLE|CRYPT_USER_KEYSET|PKCS12_NO_PERSIST_KEY|PKCS12_ALWAYS_CNG_KSP ); + store = PFXImportCertStore( &pfx, NULL, CRYPT_EXPORTABLE|CRYPT_USER_KEYSET|PKCS12_NO_PERSIST_KEY ); ok( store != NULL, "got %lu\n", GetLastError() ); count = countCertsInStore( store ); ok( count == 1, "got %lu\n", count ); @@ -3370,6 +3370,16 @@ static void test_PFXImportCertStore(void)
CertFreeCertificateContext( cert ); CertCloseStore( store, 0 ); + + /* PKCS12_NO_PERSIST_KEY|PKCS12_ALWAYS_CNG_KSP */ + store = PFXImportCertStore( &pfx, NULL, PKCS12_NO_PERSIST_KEY|PKCS12_ALWAYS_CNG_KSP ); + ok( store != NULL, "got %lu\n", GetLastError() ); + + cert = CertFindCertificateInStore( store, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL ); + ok( cert != NULL, "got %08lx\n", GetLastError() ); + + CertFreeCertificateContext( cert ); + CertCloseStore( store, 0 ); }
static void test_CryptQueryObject(void) diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c index f68db49da98..2f693c6f871 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -1395,7 +1395,7 @@ static void test_communication(void)
pfx.pbData = (BYTE *)pfxdata; pfx.cbData = sizeof(pfxdata); - store = PFXImportCertStore(&pfx, NULL, CRYPT_EXPORTABLE|CRYPT_USER_KEYSET|PKCS12_NO_PERSIST_KEY|PKCS12_ALWAYS_CNG_KSP); + store = PFXImportCertStore(&pfx, NULL, CRYPT_EXPORTABLE|CRYPT_USER_KEYSET|PKCS12_NO_PERSIST_KEY); ok(store != NULL, "got %lu\n", GetLastError());
cert = CertFindCertificateInStore(store, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL); diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c index 082295e9081..77b75fb55d6 100644 --- a/dlls/winhttp/tests/winhttp.c +++ b/dlls/winhttp/tests/winhttp.c @@ -6190,7 +6190,7 @@ static void test_client_cert_authentication(void)
pfx.pbData = (BYTE *)pfxdata; pfx.cbData = sizeof(pfxdata); - store = PFXImportCertStore( &pfx, NULL, CRYPT_EXPORTABLE|CRYPT_USER_KEYSET|PKCS12_NO_PERSIST_KEY|PKCS12_ALWAYS_CNG_KSP ); + store = PFXImportCertStore( &pfx, NULL, CRYPT_EXPORTABLE|CRYPT_USER_KEYSET|PKCS12_NO_PERSIST_KEY ); ok( store != NULL, "got %lu\n", GetLastError() );
cert = CertFindCertificateInStore( store, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL );