From 1352f6d316da3a95b73ca3f31eb44190a20adca5 Mon Sep 17 00:00:00 2001 From: Juan Lang Date: Tue, 27 Jan 2009 09:01:06 -0800 Subject: [PATCH] crypt32: Implement CryptFindCertificateKeyProvInfo. --- dlls/crypt32/cert.c | 213 ++++++++++++++++++++++++++++++++++++++ dlls/crypt32/crypt32.spec | 1 + 2 files changed, 214 insertions(+) diff --git a/dlls/crypt32/cert.c b/dlls/crypt32/cert.c index 5c2c786c97e..c6775561254 100644 --- a/dlls/crypt32/cert.c +++ b/dlls/crypt32/cert.c @@ -27,6 +27,7 @@ #include "winnls.h" #include "rpc.h" #include "wine/debug.h" +#include "wine/unicode.h" #include "crypt32_private.h" WINE_DEFAULT_DEBUG_CHANNEL(crypt); @@ -718,6 +719,218 @@ BOOL WINAPI CryptAcquireCertificatePrivateKey(PCCERT_CONTEXT pCert, return ret; } +static BOOL key_prov_info_matches_cert(PCCERT_CONTEXT pCert, + const CRYPT_KEY_PROV_INFO *keyProvInfo) +{ + HCRYPTPROV csp; + BOOL matches = FALSE; + + if (CryptAcquireContextW(&csp, keyProvInfo->pwszContainerName, + keyProvInfo->pwszProvName, keyProvInfo->dwProvType, keyProvInfo->dwFlags)) + { + DWORD size; + + /* Need to sign something to verify the sig. What to sign? Why not + * the certificate itself? + */ + if (CryptSignAndEncodeCertificate(csp, AT_SIGNATURE, + pCert->dwCertEncodingType, X509_CERT_TO_BE_SIGNED, pCert->pCertInfo, + &pCert->pCertInfo->SignatureAlgorithm, NULL, NULL, &size)) + { + BYTE *certEncoded = CryptMemAlloc(size); + + if (certEncoded) + { + if (CryptSignAndEncodeCertificate(csp, AT_SIGNATURE, + pCert->dwCertEncodingType, X509_CERT_TO_BE_SIGNED, + pCert->pCertInfo, &pCert->pCertInfo->SignatureAlgorithm, + NULL, certEncoded, &size)) + { + if (size == pCert->cbCertEncoded && + !memcmp(certEncoded, pCert->pbCertEncoded, size)) + matches = TRUE; + } + CryptMemFree(certEncoded); + } + } + CryptReleaseContext(csp, 0); + } + return matches; +} + +static BOOL container_matches_cert(PCCERT_CONTEXT pCert, LPCSTR container, + CRYPT_KEY_PROV_INFO *keyProvInfo) +{ + CRYPT_KEY_PROV_INFO copy; + WCHAR containerW[MAX_PATH]; + BOOL matches = FALSE; + + MultiByteToWideChar(CP_ACP, 0, container, -1, + containerW, sizeof(containerW) / sizeof(containerW[0])); + /* We make a copy of the CRYPT_KEY_PROV_INFO because the caller expects + * keyProvInfo->pwszContainerName to be NULL or a heap-allocated container + * name. + */ + memcpy(©, keyProvInfo, sizeof(copy)); + copy.pwszContainerName = containerW; + matches = key_prov_info_matches_cert(pCert, ©); + if (matches) + { + keyProvInfo->pwszContainerName = + CryptMemAlloc((strlenW(containerW) + 1) * sizeof(WCHAR)); + if (keyProvInfo->pwszContainerName) + { + strcpyW(keyProvInfo->pwszContainerName, containerW); + keyProvInfo->dwKeySpec = AT_SIGNATURE; + } + else + matches = FALSE; + } + return matches; +} + +/* Searches the provider named keyProvInfo.pwszProvName for a container whose + * private key matches pCert's public key. Upon success, updates keyProvInfo + * with the matching container's info (free keyProvInfo.pwszContainerName upon + * success.) + * Returns TRUE if found, FALSE if not. + */ +static BOOL find_key_prov_info_in_provider(PCCERT_CONTEXT pCert, + CRYPT_KEY_PROV_INFO *keyProvInfo) +{ + HCRYPTPROV defProvider; + BOOL ret, found = FALSE; + char containerA[MAX_PATH]; + + assert(keyProvInfo->pwszContainerName == NULL); + if ((ret = CryptAcquireContextW(&defProvider, NULL, + keyProvInfo->pwszProvName, keyProvInfo->dwProvType, + keyProvInfo->dwFlags | CRYPT_VERIFYCONTEXT))) + { + DWORD enumFlags = keyProvInfo->dwFlags | CRYPT_FIRST; + + while (ret && !found) + { + DWORD size = sizeof(containerA); + + ret = CryptGetProvParam(defProvider, PP_ENUMCONTAINERS, + (BYTE *)containerA, &size, enumFlags); + if (ret) + found = container_matches_cert(pCert, containerA, keyProvInfo); + if (enumFlags & CRYPT_FIRST) + { + enumFlags &= ~CRYPT_FIRST; + enumFlags |= CRYPT_NEXT; + } + } + CryptReleaseContext(defProvider, 0); + } + return found; +} + +static BOOL find_matching_provider(PCCERT_CONTEXT pCert, DWORD dwFlags) +{ + BOOL found = FALSE, ret = TRUE; + DWORD index = 0, cbProvName = 0; + CRYPT_KEY_PROV_INFO keyProvInfo; + + TRACE("(%p, %08x)\n", pCert, dwFlags); + + memset(&keyProvInfo, 0, sizeof(keyProvInfo)); + while (ret && !found) + { + DWORD size = 0; + + ret = CryptEnumProvidersW(index, NULL, 0, &keyProvInfo.dwProvType, + NULL, &size); + if (ret) + { + if (size <= cbProvName) + ret = CryptEnumProvidersW(index, NULL, 0, + &keyProvInfo.dwProvType, keyProvInfo.pwszProvName, &size); + else + { + CryptMemFree(keyProvInfo.pwszProvName); + keyProvInfo.pwszProvName = CryptMemAlloc(size); + if (keyProvInfo.pwszProvName) + { + cbProvName = size; + ret = CryptEnumProvidersW(index, NULL, 0, + &keyProvInfo.dwProvType, keyProvInfo.pwszProvName, &size); + if (ret) + { + if (dwFlags & CRYPT_FIND_SILENT_KEYSET_FLAG) + keyProvInfo.dwFlags |= CRYPT_SILENT; + if (dwFlags & CRYPT_FIND_USER_KEYSET_FLAG || + !(dwFlags & (CRYPT_FIND_USER_KEYSET_FLAG | + CRYPT_FIND_MACHINE_KEYSET_FLAG))) + { + keyProvInfo.dwFlags |= CRYPT_USER_KEYSET; + found = find_key_prov_info_in_provider(pCert, + &keyProvInfo); + } + if (!found) + { + if (dwFlags & CRYPT_FIND_MACHINE_KEYSET_FLAG || + !(dwFlags & (CRYPT_FIND_USER_KEYSET_FLAG | + CRYPT_FIND_MACHINE_KEYSET_FLAG))) + { + keyProvInfo.dwFlags &= ~CRYPT_USER_KEYSET; + keyProvInfo.dwFlags |= CRYPT_MACHINE_KEYSET; + found = find_key_prov_info_in_provider(pCert, + &keyProvInfo); + } + } + } + } + else + ret = FALSE; + } + index++; + } + } + if (found) + CertSetCertificateContextProperty(pCert, CERT_KEY_PROV_INFO_PROP_ID, + 0, &keyProvInfo); + CryptMemFree(keyProvInfo.pwszProvName); + CryptMemFree(keyProvInfo.pwszContainerName); + return found; +} + +static BOOL cert_prov_info_matches_cert(PCCERT_CONTEXT pCert) +{ + BOOL matches = FALSE; + DWORD size; + + if (CertGetCertificateContextProperty(pCert, CERT_KEY_PROV_INFO_PROP_ID, + NULL, &size)) + { + CRYPT_KEY_PROV_INFO *keyProvInfo = CryptMemAlloc(size); + + if (keyProvInfo) + { + if (CertGetCertificateContextProperty(pCert, + CERT_KEY_PROV_INFO_PROP_ID, keyProvInfo, &size)) + matches = key_prov_info_matches_cert(pCert, keyProvInfo); + CryptMemFree(keyProvInfo); + } + } + return matches; +} + +BOOL WINAPI CryptFindCertificateKeyProvInfo(PCCERT_CONTEXT pCert, + DWORD dwFlags, void *pvReserved) +{ + BOOL matches = FALSE; + + TRACE("(%p, %08x, %p)\n", pCert, dwFlags, pvReserved); + + matches = cert_prov_info_matches_cert(pCert); + if (!matches) + matches = find_matching_provider(pCert, dwFlags); + return matches; +} + BOOL WINAPI CertCompareCertificate(DWORD dwCertEncodingType, PCERT_INFO pCertId1, PCERT_INFO pCertId2) { diff --git a/dlls/crypt32/crypt32.spec b/dlls/crypt32/crypt32.spec index ea2b6fadebe..3591d17d234 100644 --- a/dlls/crypt32/crypt32.spec +++ b/dlls/crypt32/crypt32.spec @@ -118,6 +118,7 @@ @ stub CryptExportPKCS8 @ stdcall CryptExportPublicKeyInfo(long long long ptr ptr) @ stdcall CryptExportPublicKeyInfoEx(long long long str long ptr ptr ptr) +@ stdcall CryptFindCertificateKeyProvInfo(ptr long ptr) @ stdcall CryptFindLocalizedName(wstr) @ stdcall CryptFindOIDInfo(long ptr long) @ stdcall CryptFormatObject(long long long ptr str ptr long ptr ptr)