From: Benoît Legat <benoit.legat@gmail.com> --- dlls/crypt32/cert.c | 51 ++++++++++++++++++++++- dlls/crypt32/tests/cert.c | 88 +++++++++++++++++++++++++++++++++++++++ include/wincrypt.h | 1 + 3 files changed, 138 insertions(+), 2 deletions(-) diff --git a/dlls/crypt32/cert.c b/dlls/crypt32/cert.c index 11e436b48c7..08517456112 100644 --- a/dlls/crypt32/cert.c +++ b/dlls/crypt32/cert.c @@ -652,8 +652,38 @@ BOOL WINAPI CertGetCertificateContextProperty(PCCERT_CONTEXT pCertContext, ret = CertContext_GetProperty(cert, CERT_KEY_CONTEXT_PROP_ID, &keyContext, &size); if (ret) - ret = CertContext_CopyParam(pvData, pcbData, &keyContext.hCryptProv, - sizeof(keyContext.hCryptProv)); + { + if (keyContext.dwKeySpec == CERT_NCRYPT_KEY_SPEC) + { + SetLastError(CRYPT_E_NOT_FOUND); + ret = FALSE; + } + else + ret = CertContext_CopyParam(pvData, pcbData, &keyContext.hCryptProv, + sizeof(keyContext.hCryptProv)); + } + break; + } + case CERT_NCRYPT_KEY_HANDLE_PROP_ID: + { + CERT_KEY_CONTEXT keyContext; + DWORD size = sizeof(keyContext); + + ret = CertContext_GetProperty(cert, + CERT_KEY_CONTEXT_PROP_ID, &keyContext, &size); + if (ret) + { + if (keyContext.dwKeySpec != CERT_NCRYPT_KEY_SPEC) + { + SetLastError(CRYPT_E_NOT_FOUND); + ret = FALSE; + } + else + ret = CertContext_CopyParam(pvData, pcbData, &keyContext.hCryptProv, + sizeof(keyContext.hCryptProv)); + } + else + SetLastError(CRYPT_E_NOT_FOUND); break; } case CERT_KEY_PROV_INFO_PROP_ID: @@ -838,6 +868,23 @@ static BOOL CertContext_SetProperty(cert_t *cert, DWORD dwPropId, 0, &keyContext); break; } + case CERT_NCRYPT_KEY_HANDLE_PROP_ID: + { + CERT_KEY_CONTEXT keyContext; + + if (!pvData) + { + ContextPropertyList_RemoveProperty(cert->base.properties, + CERT_KEY_CONTEXT_PROP_ID); + ret = TRUE; + break; + } + keyContext.cbSize = sizeof(keyContext); + keyContext.hCryptProv = *(const HCRYPTPROV *)pvData; + keyContext.dwKeySpec = CERT_NCRYPT_KEY_SPEC; + ret = CertContext_SetKeyContextProperty(cert->base.properties, &keyContext); + break; + } default: FIXME("%ld: stub\n", dwPropId); ret = FALSE; diff --git a/dlls/crypt32/tests/cert.c b/dlls/crypt32/tests/cert.c index aeab4c02713..a82b83f54be 100644 --- a/dlls/crypt32/tests/cert.c +++ b/dlls/crypt32/tests/cert.c @@ -529,6 +529,94 @@ static void testCertProperties(void) GetLastError()); ok(keyContext.hCryptProv == 0, "Expected no hCryptProv\n"); + /* Remove the key context so we start fresh for NCrypt tests */ + ret = CertSetCertificateContextProperty(context, CERT_KEY_CONTEXT_PROP_ID, + 0, NULL); + ok(ret, "CertSetCertificateContextProperty failed: %08lx\n", GetLastError()); + + /* Test CERT_NCRYPT_KEY_HANDLE_PROP_ID (78) */ + /* Getting it when no key context is set should fail */ + size = sizeof(HCRYPTPROV); + SetLastError(0xdeadbeef); + ret = CertGetCertificateContextProperty(context, + CERT_NCRYPT_KEY_HANDLE_PROP_ID, NULL, &size); + ok(!ret && GetLastError() == CRYPT_E_NOT_FOUND, + "Expected CRYPT_E_NOT_FOUND, got %08lx\n", GetLastError()); + + /* Set an NCrypt key handle via property 78 */ + { + HCRYPTPROV_OR_NCRYPT_KEY_HANDLE ncryptHandle = 0xBEEF1234; + HCRYPTPROV_OR_NCRYPT_KEY_HANDLE retrievedHandle = 0; + + ret = CertSetCertificateContextProperty(context, + CERT_NCRYPT_KEY_HANDLE_PROP_ID, 0, &ncryptHandle); + ok(ret, "CertSetCertificateContextProperty failed: %08lx\n", GetLastError()); + + /* Verify the key context was set with CERT_NCRYPT_KEY_SPEC */ + size = sizeof(keyContext); + ret = CertGetCertificateContextProperty(context, + CERT_KEY_CONTEXT_PROP_ID, &keyContext, &size); + ok(ret, "CertGetCertificateContextProperty failed: %08lx\n", GetLastError()); + ok(keyContext.hCryptProv == 0xBEEF1234, + "Expected hCryptProv 0xBEEF1234, got %Ix\n", keyContext.hCryptProv); + ok(keyContext.dwKeySpec == CERT_NCRYPT_KEY_SPEC, + "Expected dwKeySpec CERT_NCRYPT_KEY_SPEC, got %lx\n", keyContext.dwKeySpec); + + /* Get the NCrypt handle back via property 78 */ + size = sizeof(retrievedHandle); + ret = CertGetCertificateContextProperty(context, + CERT_NCRYPT_KEY_HANDLE_PROP_ID, &retrievedHandle, &size); + ok(ret, "CertGetCertificateContextProperty failed: %08lx\n", GetLastError()); + ok(retrievedHandle == 0xBEEF1234, + "Expected handle 0xBEEF1234, got %Ix\n", retrievedHandle); + ok(size == sizeof(retrievedHandle), + "Expected size %Iu, got %lu\n", sizeof(retrievedHandle), size); + + /* Getting CERT_KEY_PROV_HANDLE_PROP_ID should fail since this is an NCrypt key */ + size = sizeof(retrievedHandle); + SetLastError(0xdeadbeef); + ret = CertGetCertificateContextProperty(context, + CERT_KEY_PROV_HANDLE_PROP_ID, &retrievedHandle, &size); + ok(!ret && GetLastError() == CRYPT_E_NOT_FOUND, + "Expected CRYPT_E_NOT_FOUND for CAPI handle on NCrypt key, got %08lx\n", + GetLastError()); + + /* Delete the NCrypt key handle by setting property 78 to NULL */ + ret = CertSetCertificateContextProperty(context, + CERT_NCRYPT_KEY_HANDLE_PROP_ID, 0, NULL); + ok(ret, "CertSetCertificateContextProperty failed: %08lx\n", GetLastError()); + + /* Verify it was deleted */ + size = sizeof(retrievedHandle); + SetLastError(0xdeadbeef); + ret = CertGetCertificateContextProperty(context, + CERT_NCRYPT_KEY_HANDLE_PROP_ID, &retrievedHandle, &size); + ok(!ret && GetLastError() == CRYPT_E_NOT_FOUND, + "Expected CRYPT_E_NOT_FOUND after delete, got %08lx\n", GetLastError()); + } + + /* Test that CERT_NCRYPT_KEY_HANDLE_PROP_ID fails for non-NCrypt key contexts */ + keyContext.cbSize = sizeof(keyContext); + keyContext.hCryptProv = 0xCAFE; + keyContext.dwKeySpec = AT_KEYEXCHANGE; + ret = CertSetCertificateContextProperty(context, CERT_KEY_CONTEXT_PROP_ID, + 0, &keyContext); + ok(ret, "CertSetCertificateContextProperty failed: %08lx\n", GetLastError()); + { + HCRYPTPROV_OR_NCRYPT_KEY_HANDLE retrievedHandle = 0xdeadbeef; + + size = sizeof(retrievedHandle); + SetLastError(0xdeadbeef); + ret = CertGetCertificateContextProperty(context, + CERT_NCRYPT_KEY_HANDLE_PROP_ID, &retrievedHandle, &size); + ok(!ret && GetLastError() == CRYPT_E_NOT_FOUND, + "Expected CRYPT_E_NOT_FOUND for CAPI key, got %08lx\n", GetLastError()); + } + /* Clean up */ + ret = CertSetCertificateContextProperty(context, CERT_KEY_CONTEXT_PROP_ID, + 0, NULL); + ok(ret, "CertSetCertificateContextProperty failed: %08lx\n", GetLastError()); + /* According to MSDN the subject key id can be stored as a property, * as a subject key extension, or as the SHA1 hash of the public key, * but this cert has none of them: diff --git a/include/wincrypt.h b/include/wincrypt.h index 9c42586700f..6ee077cd552 100644 --- a/include/wincrypt.h +++ b/include/wincrypt.h @@ -1942,6 +1942,7 @@ static const WCHAR MS_ENH_RSA_AES_PROV_XP_W[] = { 'M','i','c','r','o','s','o','f /* Key Specs*/ #define AT_KEYEXCHANGE 1 #define AT_SIGNATURE 2 +#define CERT_NCRYPT_KEY_SPEC 0xffffffff /* Provider Types */ #define PROV_RSA_FULL 1 -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10521