/* * Copyright 2004-2006 Juan Lang * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * */ #include #include #define NONAMELESSUNION #include "windef.h" #include "winbase.h" #include "wincrypt.h" #include "winnls.h" #include "rpc.h" #include "wine/debug.h" #include "wine/unicode.h" #include "crypt32_private.h" WINE_DEFAULT_DEBUG_CHANNEL(crypt); /* Internal version of CertGetCertificateContextProperty that gets properties * directly from the context (or the context it's linked to, depending on its * type.) Doesn't handle special-case properties, since they are handled by * CertGetCertificateContextProperty, and are particular to the store in which * the property exists (which is separate from the context.) */ static BOOL CertContext_GetProperty(cert_t *cert, DWORD dwPropId, void *pvData, DWORD *pcbData); /* Internal version of CertSetCertificateContextProperty that sets properties * directly on the context (or the context it's linked to, depending on its * type.) Doesn't handle special cases, since they're handled by * CertSetCertificateContextProperty anyway. */ static BOOL CertContext_SetProperty(cert_t *cert, DWORD dwPropId, DWORD dwFlags, const void *pvData); BOOL WINAPI CertAddEncodedCertificateToStore(HCERTSTORE hCertStore, DWORD dwCertEncodingType, const BYTE *pbCertEncoded, DWORD cbCertEncoded, DWORD dwAddDisposition, PCCERT_CONTEXT *ppCertContext) { PCCERT_CONTEXT cert = CertCreateCertificateContext(dwCertEncodingType, pbCertEncoded, cbCertEncoded); BOOL ret; TRACE("(%p, %08x, %p, %d, %08x, %p)\n", hCertStore, dwCertEncodingType, pbCertEncoded, cbCertEncoded, dwAddDisposition, ppCertContext); if (cert) { ret = CertAddCertificateContextToStore(hCertStore, cert, dwAddDisposition, ppCertContext); CertFreeCertificateContext(cert); } else ret = FALSE; return ret; } BOOL WINAPI CertAddEncodedCertificateToSystemStoreA(LPCSTR pszCertStoreName, const BYTE *pbCertEncoded, DWORD cbCertEncoded) { HCERTSTORE store; BOOL ret = FALSE; TRACE("(%s, %p, %d)\n", debugstr_a(pszCertStoreName), pbCertEncoded, cbCertEncoded); store = CertOpenSystemStoreA(0, pszCertStoreName); if (store) { ret = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, pbCertEncoded, cbCertEncoded, CERT_STORE_ADD_USE_EXISTING, NULL); CertCloseStore(store, 0); } return ret; } BOOL WINAPI CertAddEncodedCertificateToSystemStoreW(LPCWSTR pszCertStoreName, const BYTE *pbCertEncoded, DWORD cbCertEncoded) { HCERTSTORE store; BOOL ret = FALSE; TRACE("(%s, %p, %d)\n", debugstr_w(pszCertStoreName), pbCertEncoded, cbCertEncoded); store = CertOpenSystemStoreW(0, pszCertStoreName); if (store) { ret = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, pbCertEncoded, cbCertEncoded, CERT_STORE_ADD_USE_EXISTING, NULL); CertCloseStore(store, 0); } return ret; } static const context_vtbl_t cert_vtbl; static void Cert_free(context_t *context) { cert_t *cert = (cert_t*)context; CryptMemFree(cert->ctx.pbCertEncoded); LocalFree(cert->ctx.pCertInfo); } static context_t *Cert_clone(context_t *context, WINECRYPT_CERTSTORE *store, BOOL use_link) { cert_t *cert; if(use_link) { cert = (cert_t*)Context_CreateLinkContext(sizeof(CERT_CONTEXT), context, store); if(!cert) return NULL; }else { const cert_t *cloned = (const cert_t*)context; DWORD size = 0; BOOL res; cert = (cert_t*)Context_CreateDataContext(sizeof(CERT_CONTEXT), &cert_vtbl, store); if(!cert) return NULL; Context_CopyProperties(&cert->ctx, &cloned->ctx); cert->ctx.dwCertEncodingType = cloned->ctx.dwCertEncodingType; cert->ctx.pbCertEncoded = CryptMemAlloc(cloned->ctx.cbCertEncoded); memcpy(cert->ctx.pbCertEncoded, cloned->ctx.pbCertEncoded, cloned->ctx.cbCertEncoded); cert->ctx.cbCertEncoded = cloned->ctx.cbCertEncoded; /* FIXME: We don't need to decode the object here, we could just clone cert info. */ res = CryptDecodeObjectEx(cert->ctx.dwCertEncodingType, X509_CERT_TO_BE_SIGNED, cert->ctx.pbCertEncoded, cert->ctx.cbCertEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &cert->ctx.pCertInfo, &size); if(!res) { CertFreeCertificateContext(&cert->ctx); return NULL; } } cert->ctx.hCertStore = store; return &cert->base; } static const context_vtbl_t cert_vtbl = { Cert_free, Cert_clone }; static BOOL add_cert_to_store(WINECRYPT_CERTSTORE *store, const CERT_CONTEXT *cert, DWORD add_disposition, BOOL use_link, PCCERT_CONTEXT *ret_context) { const CERT_CONTEXT *existing = NULL; BOOL ret = TRUE, inherit_props = FALSE; context_t *new_context = NULL; switch (add_disposition) { case CERT_STORE_ADD_ALWAYS: break; case CERT_STORE_ADD_NEW: case CERT_STORE_ADD_REPLACE_EXISTING: case CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES: case CERT_STORE_ADD_USE_EXISTING: case CERT_STORE_ADD_NEWER: case CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES: { BYTE hashToAdd[20]; DWORD size = sizeof(hashToAdd); ret = CertGetCertificateContextProperty(cert, CERT_HASH_PROP_ID, hashToAdd, &size); if (ret) { CRYPT_HASH_BLOB blob = { sizeof(hashToAdd), hashToAdd }; existing = CertFindCertificateInStore(store, cert->dwCertEncodingType, 0, CERT_FIND_SHA1_HASH, &blob, NULL); } break; } default: FIXME("Unimplemented add disposition %d\n", add_disposition); SetLastError(E_INVALIDARG); return FALSE; } switch (add_disposition) { case CERT_STORE_ADD_ALWAYS: break; case CERT_STORE_ADD_NEW: if (existing) { TRACE("found matching certificate, not adding\n"); SetLastError(CRYPT_E_EXISTS); return FALSE; } break; case CERT_STORE_ADD_REPLACE_EXISTING: break; case CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES: if (use_link) FIXME("CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES: semi-stub for links\n"); if (existing) inherit_props = TRUE; break; case CERT_STORE_ADD_USE_EXISTING: if(use_link) FIXME("CERT_STORE_ADD_USE_EXISTING: semi-stub for links\n"); if (existing) { Context_CopyProperties(existing, cert); if (ret_context) *ret_context = CertDuplicateCertificateContext(existing); return TRUE; } break; case CERT_STORE_ADD_NEWER: if (existing && CompareFileTime(&existing->pCertInfo->NotBefore, &cert->pCertInfo->NotBefore) >= 0) { TRACE("existing certificate is newer, not adding\n"); SetLastError(CRYPT_E_EXISTS); return FALSE; } break; case CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES: if (existing) { if (CompareFileTime(&existing->pCertInfo->NotBefore, &cert->pCertInfo->NotBefore) >= 0) { TRACE("existing certificate is newer, not adding\n"); SetLastError(CRYPT_E_EXISTS); return FALSE; } inherit_props = TRUE; } break; } /* FIXME: We have tests that this works, but what should we really do in this case? */ if(!store) { if(ret_context) *ret_context = CertDuplicateCertificateContext(cert); return TRUE; } ret = store->vtbl->certs.addContext(store, context_from_ptr(cert), existing ? context_from_ptr(existing) : NULL, (ret_context || inherit_props) ? &new_context : NULL, use_link); if(!ret) return FALSE; if(inherit_props) Context_CopyProperties(context_ptr(new_context), existing); if(ret_context) *ret_context = context_ptr(new_context); else if(new_context) Context_Release(new_context); TRACE("returning %d\n", ret); return ret; } BOOL WINAPI CertAddCertificateContextToStore(HCERTSTORE hCertStore, PCCERT_CONTEXT pCertContext, DWORD dwAddDisposition, PCCERT_CONTEXT *ppStoreContext) { WINECRYPT_CERTSTORE *store = hCertStore; TRACE("(%p, %p, %08x, %p)\n", hCertStore, pCertContext, dwAddDisposition, ppStoreContext); return add_cert_to_store(store, pCertContext, dwAddDisposition, FALSE, ppStoreContext); } BOOL WINAPI CertAddCertificateLinkToStore(HCERTSTORE hCertStore, PCCERT_CONTEXT pCertContext, DWORD dwAddDisposition, PCCERT_CONTEXT *ppCertContext) { static int calls; WINECRYPT_CERTSTORE *store = (WINECRYPT_CERTSTORE*)hCertStore; if (!(calls++)) FIXME("(%p, %p, %08x, %p): semi-stub\n", hCertStore, pCertContext, dwAddDisposition, ppCertContext); if (store->dwMagic != WINE_CRYPTCERTSTORE_MAGIC) return FALSE; if (store->type == StoreTypeCollection) { SetLastError(E_INVALIDARG); return FALSE; } return add_cert_to_store(hCertStore, pCertContext, dwAddDisposition, TRUE, ppCertContext); } PCCERT_CONTEXT WINAPI CertCreateCertificateContext(DWORD dwCertEncodingType, const BYTE *pbCertEncoded, DWORD cbCertEncoded) { cert_t *cert = NULL; BYTE *data = NULL; BOOL ret; PCERT_INFO certInfo = NULL; DWORD size = 0; TRACE("(%08x, %p, %d)\n", dwCertEncodingType, pbCertEncoded, cbCertEncoded); if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING) { SetLastError(E_INVALIDARG); return NULL; } ret = CryptDecodeObjectEx(dwCertEncodingType, X509_CERT_TO_BE_SIGNED, pbCertEncoded, cbCertEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &certInfo, &size); if (!ret) return NULL; cert = (cert_t*)Context_CreateDataContext(sizeof(CERT_CONTEXT), &cert_vtbl, &empty_store); if (!cert) return NULL; data = CryptMemAlloc(cbCertEncoded); if (!data) { Context_Release(&cert->base); return NULL; } memcpy(data, pbCertEncoded, cbCertEncoded); cert->ctx.dwCertEncodingType = dwCertEncodingType; cert->ctx.pbCertEncoded = data; cert->ctx.cbCertEncoded = cbCertEncoded; cert->ctx.pCertInfo = certInfo; cert->ctx.hCertStore = &empty_store; return &cert->ctx; } PCCERT_CONTEXT WINAPI CertDuplicateCertificateContext(PCCERT_CONTEXT pCertContext) { TRACE("(%p)\n", pCertContext); if (!pCertContext) return NULL; Context_AddRef(&cert_from_ptr(pCertContext)->base); return pCertContext; } BOOL WINAPI CertFreeCertificateContext(PCCERT_CONTEXT pCertContext) { TRACE("(%p)\n", pCertContext); if (pCertContext) Context_Release(&cert_from_ptr(pCertContext)->base); return TRUE; } DWORD WINAPI CertEnumCertificateContextProperties(PCCERT_CONTEXT pCertContext, DWORD dwPropId) { cert_t *cert = cert_from_ptr(pCertContext); DWORD ret; TRACE("(%p, %d)\n", pCertContext, dwPropId); if (cert->base.properties) ret = ContextPropertyList_EnumPropIDs(cert->base.properties, dwPropId); else ret = 0; return ret; } static BOOL CertContext_GetHashProp(cert_t *cert, DWORD dwPropId, ALG_ID algID, const BYTE *toHash, DWORD toHashLen, void *pvData, DWORD *pcbData) { BOOL ret = CryptHashCertificate(0, algID, 0, toHash, toHashLen, pvData, pcbData); if (ret && pvData) { CRYPT_DATA_BLOB blob = { *pcbData, pvData }; ret = CertContext_SetProperty(cert, dwPropId, 0, &blob); } return ret; } static BOOL CertContext_CopyParam(void *pvData, DWORD *pcbData, const void *pb, DWORD cb) { BOOL ret = TRUE; if (!pvData) *pcbData = cb; else if (*pcbData < cb) { SetLastError(ERROR_MORE_DATA); *pcbData = cb; ret = FALSE; } else { memcpy(pvData, pb, cb); *pcbData = cb; } return ret; } static BOOL CertContext_GetProperty(cert_t *cert, DWORD dwPropId, void *pvData, DWORD *pcbData) { BOOL ret; CRYPT_DATA_BLOB blob; TRACE("(%p, %d, %p, %p)\n", cert, dwPropId, pvData, pcbData); if (cert->base.properties) ret = ContextPropertyList_FindProperty(cert->base.properties, dwPropId, &blob); else ret = FALSE; if (ret) ret = CertContext_CopyParam(pvData, pcbData, blob.pbData, blob.cbData); else { /* Implicit properties */ switch (dwPropId) { case CERT_SHA1_HASH_PROP_ID: ret = CertContext_GetHashProp(cert, dwPropId, CALG_SHA1, cert->ctx.pbCertEncoded, cert->ctx.cbCertEncoded, pvData, pcbData); break; case CERT_MD5_HASH_PROP_ID: ret = CertContext_GetHashProp(cert, dwPropId, CALG_MD5, cert->ctx.pbCertEncoded, cert->ctx.cbCertEncoded, pvData, pcbData); break; case CERT_SUBJECT_NAME_MD5_HASH_PROP_ID: ret = CertContext_GetHashProp(cert, dwPropId, CALG_MD5, cert->ctx.pCertInfo->Subject.pbData, cert->ctx.pCertInfo->Subject.cbData, pvData, pcbData); break; case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID: ret = CertContext_GetHashProp(cert, dwPropId, CALG_MD5, cert->ctx.pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData, cert->ctx.pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData, pvData, pcbData); break; case CERT_ISSUER_SERIAL_NUMBER_MD5_HASH_PROP_ID: ret = CertContext_GetHashProp(cert, dwPropId, CALG_MD5, cert->ctx.pCertInfo->SerialNumber.pbData, cert->ctx.pCertInfo->SerialNumber.cbData, pvData, pcbData); break; case CERT_SIGNATURE_HASH_PROP_ID: ret = CryptHashToBeSigned(0, cert->ctx.dwCertEncodingType, cert->ctx.pbCertEncoded, cert->ctx.cbCertEncoded, pvData, pcbData); if (ret && pvData) { CRYPT_DATA_BLOB blob = { *pcbData, pvData }; ret = CertContext_SetProperty(cert, dwPropId, 0, &blob); } break; case CERT_KEY_IDENTIFIER_PROP_ID: { PCERT_EXTENSION ext = CertFindExtension( szOID_SUBJECT_KEY_IDENTIFIER, cert->ctx.pCertInfo->cExtension, cert->ctx.pCertInfo->rgExtension); if (ext) { CRYPT_DATA_BLOB value; DWORD size = sizeof(value); ret = CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_SUBJECT_KEY_IDENTIFIER, ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_NOCOPY_FLAG, NULL, &value, &size); if (ret) { ret = CertContext_CopyParam(pvData, pcbData, value.pbData, value.cbData); CertContext_SetProperty(cert, dwPropId, 0, &value); } } else SetLastError(ERROR_INVALID_DATA); break; } default: SetLastError(CRYPT_E_NOT_FOUND); } } TRACE("returning %d\n", ret); return ret; } void CRYPT_FixKeyProvInfoPointers(PCRYPT_KEY_PROV_INFO info) { DWORD i, containerLen, provNameLen; LPBYTE data = (LPBYTE)info + sizeof(CRYPT_KEY_PROV_INFO); info->pwszContainerName = (LPWSTR)data; containerLen = (lstrlenW(info->pwszContainerName) + 1) * sizeof(WCHAR); data += containerLen; info->pwszProvName = (LPWSTR)data; provNameLen = (lstrlenW(info->pwszProvName) + 1) * sizeof(WCHAR); data += provNameLen; info->rgProvParam = (PCRYPT_KEY_PROV_PARAM)data; data += info->cProvParam * sizeof(CRYPT_KEY_PROV_PARAM); for (i = 0; i < info->cProvParam; i++) { info->rgProvParam[i].pbData = data; data += info->rgProvParam[i].cbData; } } BOOL WINAPI CertGetCertificateContextProperty(PCCERT_CONTEXT pCertContext, DWORD dwPropId, void *pvData, DWORD *pcbData) { cert_t *cert = cert_from_ptr(pCertContext); BOOL ret; TRACE("(%p, %d, %p, %p)\n", pCertContext, dwPropId, pvData, pcbData); switch (dwPropId) { case 0: case CERT_CERT_PROP_ID: case CERT_CRL_PROP_ID: case CERT_CTL_PROP_ID: SetLastError(E_INVALIDARG); ret = FALSE; break; case CERT_ACCESS_STATE_PROP_ID: ret = CertGetStoreProperty(cert->ctx.hCertStore, dwPropId, pvData, pcbData); break; case CERT_KEY_PROV_HANDLE_PROP_ID: { CERT_KEY_CONTEXT keyContext; DWORD size = sizeof(keyContext); ret = CertContext_GetProperty(cert, CERT_KEY_CONTEXT_PROP_ID, &keyContext, &size); if (ret) ret = CertContext_CopyParam(pvData, pcbData, &keyContext.hCryptProv, sizeof(keyContext.hCryptProv)); break; } case CERT_KEY_PROV_INFO_PROP_ID: ret = CertContext_GetProperty(cert, dwPropId, pvData, pcbData); if (ret && pvData) CRYPT_FixKeyProvInfoPointers(pvData); break; default: ret = CertContext_GetProperty(cert, dwPropId, pvData, pcbData); } TRACE("returning %d\n", ret); return ret; } /* Copies key provider info from from into to, where to is assumed to be a * contiguous buffer of memory large enough for from and all its associated * data, but whose pointers are uninitialized. * Upon return, to contains a contiguous copy of from, packed in the following * order: * - CRYPT_KEY_PROV_INFO * - pwszContainerName * - pwszProvName * - rgProvParam[0]... */ static void CRYPT_CopyKeyProvInfo(PCRYPT_KEY_PROV_INFO to, const CRYPT_KEY_PROV_INFO *from) { DWORD i; LPBYTE nextData = (LPBYTE)to + sizeof(CRYPT_KEY_PROV_INFO); if (from->pwszContainerName) { to->pwszContainerName = (LPWSTR)nextData; lstrcpyW(to->pwszContainerName, from->pwszContainerName); nextData += (lstrlenW(from->pwszContainerName) + 1) * sizeof(WCHAR); } else to->pwszContainerName = NULL; if (from->pwszProvName) { to->pwszProvName = (LPWSTR)nextData; lstrcpyW(to->pwszProvName, from->pwszProvName); nextData += (lstrlenW(from->pwszProvName) + 1) * sizeof(WCHAR); } else to->pwszProvName = NULL; to->dwProvType = from->dwProvType; to->dwFlags = from->dwFlags; to->cProvParam = from->cProvParam; to->rgProvParam = (PCRYPT_KEY_PROV_PARAM)nextData; nextData += to->cProvParam * sizeof(CRYPT_KEY_PROV_PARAM); to->dwKeySpec = from->dwKeySpec; for (i = 0; i < to->cProvParam; i++) { memcpy(&to->rgProvParam[i], &from->rgProvParam[i], sizeof(CRYPT_KEY_PROV_PARAM)); to->rgProvParam[i].pbData = nextData; memcpy(to->rgProvParam[i].pbData, from->rgProvParam[i].pbData, from->rgProvParam[i].cbData); nextData += from->rgProvParam[i].cbData; } } static BOOL CertContext_SetKeyProvInfoProperty(CONTEXT_PROPERTY_LIST *properties, const CRYPT_KEY_PROV_INFO *info) { BOOL ret; LPBYTE buf = NULL; DWORD size = sizeof(CRYPT_KEY_PROV_INFO), i, containerSize, provNameSize; if (info->pwszContainerName) containerSize = (lstrlenW(info->pwszContainerName) + 1) * sizeof(WCHAR); else containerSize = 0; if (info->pwszProvName) provNameSize = (lstrlenW(info->pwszProvName) + 1) * sizeof(WCHAR); else provNameSize = 0; size += containerSize + provNameSize; for (i = 0; i < info->cProvParam; i++) size += sizeof(CRYPT_KEY_PROV_PARAM) + info->rgProvParam[i].cbData; buf = CryptMemAlloc(size); if (buf) { CRYPT_CopyKeyProvInfo((PCRYPT_KEY_PROV_INFO)buf, info); ret = ContextPropertyList_SetProperty(properties, CERT_KEY_PROV_INFO_PROP_ID, buf, size); CryptMemFree(buf); } else ret = FALSE; return ret; } static BOOL CertContext_SetProperty(cert_t *cert, DWORD dwPropId, DWORD dwFlags, const void *pvData) { BOOL ret; TRACE("(%p, %d, %08x, %p)\n", cert, dwPropId, dwFlags, pvData); if (!cert->base.properties) ret = FALSE; else { switch (dwPropId) { case CERT_AUTO_ENROLL_PROP_ID: case CERT_CTL_USAGE_PROP_ID: /* same as CERT_ENHKEY_USAGE_PROP_ID */ case CERT_DESCRIPTION_PROP_ID: case CERT_FRIENDLY_NAME_PROP_ID: case CERT_HASH_PROP_ID: case CERT_KEY_IDENTIFIER_PROP_ID: case CERT_MD5_HASH_PROP_ID: case CERT_NEXT_UPDATE_LOCATION_PROP_ID: case CERT_PUBKEY_ALG_PARA_PROP_ID: case CERT_PVK_FILE_PROP_ID: case CERT_SIGNATURE_HASH_PROP_ID: case CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID: case CERT_SUBJECT_NAME_MD5_HASH_PROP_ID: case CERT_EXTENDED_ERROR_INFO_PROP_ID: case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID: case CERT_ENROLLMENT_PROP_ID: case CERT_CROSS_CERT_DIST_POINTS_PROP_ID: case CERT_OCSP_RESPONSE_PROP_ID: case CERT_RENEWAL_PROP_ID: { if (pvData) { const CRYPT_DATA_BLOB *blob = pvData; ret = ContextPropertyList_SetProperty(cert->base.properties, dwPropId, blob->pbData, blob->cbData); } else { ContextPropertyList_RemoveProperty(cert->base.properties, dwPropId); ret = TRUE; } break; } case CERT_DATE_STAMP_PROP_ID: if (pvData) ret = ContextPropertyList_SetProperty(cert->base.properties, dwPropId, pvData, sizeof(FILETIME)); else { ContextPropertyList_RemoveProperty(cert->base.properties, dwPropId); ret = TRUE; } break; case CERT_KEY_CONTEXT_PROP_ID: { if (pvData) { const CERT_KEY_CONTEXT *keyContext = pvData; if (keyContext->cbSize != sizeof(CERT_KEY_CONTEXT)) { SetLastError(E_INVALIDARG); ret = FALSE; } else ret = ContextPropertyList_SetProperty(cert->base.properties, dwPropId, (const BYTE *)keyContext, keyContext->cbSize); } else { ContextPropertyList_RemoveProperty(cert->base.properties, dwPropId); ret = TRUE; } break; } case CERT_KEY_PROV_INFO_PROP_ID: if (pvData) ret = CertContext_SetKeyProvInfoProperty(cert->base.properties, pvData); else { ContextPropertyList_RemoveProperty(cert->base.properties, dwPropId); ret = TRUE; } break; case CERT_KEY_PROV_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 (!(dwFlags & CERT_STORE_NO_CRYPT_RELEASE_FLAG)) CryptReleaseContext(keyContext.hCryptProv, 0); } keyContext.cbSize = sizeof(keyContext); if (pvData) keyContext.hCryptProv = *(const HCRYPTPROV *)pvData; else { keyContext.hCryptProv = 0; keyContext.dwKeySpec = AT_SIGNATURE; } ret = CertContext_SetProperty(cert, CERT_KEY_CONTEXT_PROP_ID, 0, &keyContext); break; } default: FIXME("%d: stub\n", dwPropId); ret = FALSE; } } TRACE("returning %d\n", ret); return ret; } BOOL WINAPI CertSetCertificateContextProperty(PCCERT_CONTEXT pCertContext, DWORD dwPropId, DWORD dwFlags, const void *pvData) { BOOL ret; TRACE("(%p, %d, %08x, %p)\n", pCertContext, dwPropId, dwFlags, pvData); /* Handle special cases for "read-only"/invalid prop IDs. Windows just * crashes on most of these, I'll be safer. */ switch (dwPropId) { case 0: case CERT_ACCESS_STATE_PROP_ID: case CERT_CERT_PROP_ID: case CERT_CRL_PROP_ID: case CERT_CTL_PROP_ID: SetLastError(E_INVALIDARG); return FALSE; } ret = CertContext_SetProperty(cert_from_ptr(pCertContext), dwPropId, dwFlags, pvData); TRACE("returning %d\n", ret); return ret; } /* Acquires the private key using the key provider info, retrieving info from * the certificate if info is NULL. The acquired provider is returned in * *phCryptProv, and the key spec for the provider is returned in *pdwKeySpec. */ static BOOL CRYPT_AcquirePrivateKeyFromProvInfo(PCCERT_CONTEXT pCert, PCRYPT_KEY_PROV_INFO info, HCRYPTPROV *phCryptProv, DWORD *pdwKeySpec) { DWORD size = 0; BOOL allocated = FALSE, ret = TRUE; if (!info) { ret = CertGetCertificateContextProperty(pCert, CERT_KEY_PROV_INFO_PROP_ID, 0, &size); if (ret) { info = HeapAlloc(GetProcessHeap(), 0, size); if (info) { ret = CertGetCertificateContextProperty(pCert, CERT_KEY_PROV_INFO_PROP_ID, info, &size); allocated = TRUE; } else { SetLastError(ERROR_OUTOFMEMORY); ret = FALSE; } } else SetLastError(CRYPT_E_NO_KEY_PROPERTY); } if (ret) { ret = CryptAcquireContextW(phCryptProv, info->pwszContainerName, info->pwszProvName, info->dwProvType, 0); if (ret) { DWORD i; for (i = 0; i < info->cProvParam; i++) { CryptSetProvParam(*phCryptProv, info->rgProvParam[i].dwParam, info->rgProvParam[i].pbData, info->rgProvParam[i].dwFlags); } *pdwKeySpec = info->dwKeySpec; } else SetLastError(CRYPT_E_NO_KEY_PROPERTY); } if (allocated) HeapFree(GetProcessHeap(), 0, info); return ret; } BOOL WINAPI CryptAcquireCertificatePrivateKey(PCCERT_CONTEXT pCert, DWORD dwFlags, void *pvReserved, HCRYPTPROV_OR_NCRYPT_KEY_HANDLE *phCryptProv, DWORD *pdwKeySpec, BOOL *pfCallerFreeProv) { BOOL ret = FALSE, cache = FALSE; PCRYPT_KEY_PROV_INFO info = NULL; CERT_KEY_CONTEXT keyContext; DWORD size; TRACE("(%p, %08x, %p, %p, %p, %p)\n", pCert, dwFlags, pvReserved, phCryptProv, pdwKeySpec, pfCallerFreeProv); if (dwFlags & CRYPT_ACQUIRE_USE_PROV_INFO_FLAG) { DWORD size = 0; ret = CertGetCertificateContextProperty(pCert, CERT_KEY_PROV_INFO_PROP_ID, 0, &size); if (ret) { info = HeapAlloc(GetProcessHeap(), 0, size); ret = CertGetCertificateContextProperty(pCert, CERT_KEY_PROV_INFO_PROP_ID, info, &size); if (ret) cache = info->dwFlags & CERT_SET_KEY_CONTEXT_PROP_ID; } } else if (dwFlags & CRYPT_ACQUIRE_CACHE_FLAG) cache = TRUE; *phCryptProv = 0; if (cache) { size = sizeof(keyContext); ret = CertGetCertificateContextProperty(pCert, CERT_KEY_CONTEXT_PROP_ID, &keyContext, &size); if (ret) { *phCryptProv = keyContext.hCryptProv; if (pdwKeySpec) *pdwKeySpec = keyContext.dwKeySpec; if (pfCallerFreeProv) *pfCallerFreeProv = !cache; } } if (!*phCryptProv) { ret = CRYPT_AcquirePrivateKeyFromProvInfo(pCert, info, &keyContext.hCryptProv, &keyContext.dwKeySpec); if (ret) { *phCryptProv = keyContext.hCryptProv; if (pdwKeySpec) *pdwKeySpec = keyContext.dwKeySpec; if (cache) { keyContext.cbSize = sizeof(keyContext); if (CertSetCertificateContextProperty(pCert, CERT_KEY_CONTEXT_PROP_ID, 0, &keyContext)) { if (pfCallerFreeProv) *pfCallerFreeProv = FALSE; } } else { if (pfCallerFreeProv) *pfCallerFreeProv = TRUE; } } } HeapFree(GetProcessHeap(), 0, info); 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; MultiByteToWideChar(CP_ACP, 0, container, -1, containerW, ARRAY_SIZE(containerW)); /* 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. */ copy = *keyProvInfo; 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; 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) { BOOL ret; TRACE("(%08x, %p, %p)\n", dwCertEncodingType, pCertId1, pCertId2); ret = CertCompareCertificateName(dwCertEncodingType, &pCertId1->Issuer, &pCertId2->Issuer) && CertCompareIntegerBlob(&pCertId1->SerialNumber, &pCertId2->SerialNumber); TRACE("returning %d\n", ret); return ret; } BOOL WINAPI CertCompareCertificateName(DWORD dwCertEncodingType, PCERT_NAME_BLOB pCertName1, PCERT_NAME_BLOB pCertName2) { BOOL ret; TRACE("(%08x, %p, %p)\n", dwCertEncodingType, pCertName1, pCertName2); if (pCertName1->cbData == pCertName2->cbData) { if (pCertName1->cbData) ret = !memcmp(pCertName1->pbData, pCertName2->pbData, pCertName1->cbData); else ret = TRUE; } else ret = FALSE; TRACE("returning %d\n", ret); return ret; } /* Returns the number of significant bytes in pInt, where a byte is * insignificant if it's a leading 0 for positive numbers or a leading 0xff * for negative numbers. pInt is assumed to be little-endian. */ static DWORD CRYPT_significantBytes(const CRYPT_INTEGER_BLOB *pInt) { DWORD ret = pInt->cbData; while (ret > 1) { if (pInt->pbData[ret - 2] <= 0x7f && pInt->pbData[ret - 1] == 0) ret--; else if (pInt->pbData[ret - 2] >= 0x80 && pInt->pbData[ret - 1] == 0xff) ret--; else break; } return ret; } BOOL WINAPI CertCompareIntegerBlob(PCRYPT_INTEGER_BLOB pInt1, PCRYPT_INTEGER_BLOB pInt2) { BOOL ret; DWORD cb1, cb2; TRACE("(%p, %p)\n", pInt1, pInt2); cb1 = CRYPT_significantBytes(pInt1); cb2 = CRYPT_significantBytes(pInt2); if (cb1 == cb2) { if (cb1) ret = !memcmp(pInt1->pbData, pInt2->pbData, cb1); else ret = TRUE; } else ret = FALSE; TRACE("returning %d\n", ret); return ret; } BOOL WINAPI CertComparePublicKeyInfo(DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pPublicKey1, PCERT_PUBLIC_KEY_INFO pPublicKey2) { BOOL ret; TRACE("(%08x, %p, %p)\n", dwCertEncodingType, pPublicKey1, pPublicKey2); switch (GET_CERT_ENCODING_TYPE(dwCertEncodingType)) { case 0: /* Seems to mean "raw binary bits" */ if (pPublicKey1->PublicKey.cbData == pPublicKey2->PublicKey.cbData && pPublicKey1->PublicKey.cUnusedBits == pPublicKey2->PublicKey.cUnusedBits) { if (pPublicKey2->PublicKey.cbData) ret = !memcmp(pPublicKey1->PublicKey.pbData, pPublicKey2->PublicKey.pbData, pPublicKey1->PublicKey.cbData); else ret = TRUE; } else ret = FALSE; break; default: WARN("Unknown encoding type %08x\n", dwCertEncodingType); /* FALLTHROUGH */ case X509_ASN_ENCODING: { BLOBHEADER *pblob1, *pblob2; DWORD length; ret = FALSE; if (CryptDecodeObject(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB, pPublicKey1->PublicKey.pbData, pPublicKey1->PublicKey.cbData, 0, NULL, &length)) { pblob1 = CryptMemAlloc(length); if (CryptDecodeObject(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB, pPublicKey1->PublicKey.pbData, pPublicKey1->PublicKey.cbData, 0, pblob1, &length)) { if (CryptDecodeObject(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB, pPublicKey2->PublicKey.pbData, pPublicKey2->PublicKey.cbData, 0, NULL, &length)) { pblob2 = CryptMemAlloc(length); if (CryptDecodeObject(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB, pPublicKey2->PublicKey.pbData, pPublicKey2->PublicKey.cbData, 0, pblob2, &length)) { /* The RSAPUBKEY structure directly follows the BLOBHEADER */ RSAPUBKEY *pk1 = (LPVOID)(pblob1 + 1), *pk2 = (LPVOID)(pblob2 + 1); ret = (pk1->bitlen == pk2->bitlen) && (pk1->pubexp == pk2->pubexp) && !memcmp(pk1 + 1, pk2 + 1, pk1->bitlen/8); } CryptMemFree(pblob2); } } CryptMemFree(pblob1); } break; } } return ret; } DWORD WINAPI CertGetPublicKeyLength(DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pPublicKey) { DWORD len = 0; TRACE("(%08x, %p)\n", dwCertEncodingType, pPublicKey); if (GET_CERT_ENCODING_TYPE(dwCertEncodingType) != X509_ASN_ENCODING) { SetLastError(ERROR_FILE_NOT_FOUND); return 0; } if (pPublicKey->Algorithm.pszObjId && !strcmp(pPublicKey->Algorithm.pszObjId, szOID_RSA_DH)) { FIXME("unimplemented for DH public keys\n"); SetLastError(CRYPT_E_ASN1_BADTAG); } else { DWORD size; PBYTE buf; BOOL ret = CryptDecodeObjectEx(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB, pPublicKey->PublicKey.pbData, pPublicKey->PublicKey.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &buf, &size); if (ret) { RSAPUBKEY *rsaPubKey = (RSAPUBKEY *)(buf + sizeof(BLOBHEADER)); len = rsaPubKey->bitlen; LocalFree(buf); } } return len; } typedef BOOL (*CertCompareFunc)(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, const void *pvPara); static BOOL compare_cert_by_md5_hash(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, const void *pvPara) { BOOL ret; BYTE hash[16]; DWORD size = sizeof(hash); ret = CertGetCertificateContextProperty(pCertContext, CERT_MD5_HASH_PROP_ID, hash, &size); if (ret) { const CRYPT_HASH_BLOB *pHash = pvPara; if (size == pHash->cbData) ret = !memcmp(pHash->pbData, hash, size); else ret = FALSE; } return ret; } static BOOL compare_cert_by_sha1_hash(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, const void *pvPara) { BOOL ret; BYTE hash[20]; DWORD size = sizeof(hash); ret = CertGetCertificateContextProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, hash, &size); if (ret) { const CRYPT_HASH_BLOB *pHash = pvPara; if (size == pHash->cbData) ret = !memcmp(pHash->pbData, hash, size); else ret = FALSE; } return ret; } static BOOL compare_cert_by_name(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, const void *pvPara) { CERT_NAME_BLOB *blob = (CERT_NAME_BLOB *)pvPara, *toCompare; BOOL ret; if (dwType & CERT_INFO_SUBJECT_FLAG) toCompare = &pCertContext->pCertInfo->Subject; else toCompare = &pCertContext->pCertInfo->Issuer; ret = CertCompareCertificateName(pCertContext->dwCertEncodingType, toCompare, blob); return ret; } static BOOL compare_cert_by_public_key(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, const void *pvPara) { CERT_PUBLIC_KEY_INFO *publicKey = (CERT_PUBLIC_KEY_INFO *)pvPara; BOOL ret; ret = CertComparePublicKeyInfo(pCertContext->dwCertEncodingType, &pCertContext->pCertInfo->SubjectPublicKeyInfo, publicKey); return ret; } static BOOL compare_cert_by_subject_cert(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, const void *pvPara) { CERT_INFO *pCertInfo = (CERT_INFO *)pvPara; BOOL ret; /* Matching serial number and subject match.. */ ret = CertCompareCertificateName(pCertContext->dwCertEncodingType, &pCertContext->pCertInfo->Subject, &pCertInfo->Issuer); if (ret) ret = CertCompareIntegerBlob(&pCertContext->pCertInfo->SerialNumber, &pCertInfo->SerialNumber); else { /* failing that, if the serial number and issuer match, we match */ ret = CertCompareIntegerBlob(&pCertContext->pCertInfo->SerialNumber, &pCertInfo->SerialNumber); if (ret) ret = CertCompareCertificateName(pCertContext->dwCertEncodingType, &pCertContext->pCertInfo->Issuer, &pCertInfo->Issuer); } TRACE("returning %d\n", ret); return ret; } static BOOL compare_cert_by_cert_id(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, const void *pvPara) { CERT_ID *id = (CERT_ID *)pvPara; BOOL ret; switch (id->dwIdChoice) { case CERT_ID_ISSUER_SERIAL_NUMBER: ret = CertCompareCertificateName(pCertContext->dwCertEncodingType, &pCertContext->pCertInfo->Issuer, &id->u.IssuerSerialNumber.Issuer); if (ret) ret = CertCompareIntegerBlob(&pCertContext->pCertInfo->SerialNumber, &id->u.IssuerSerialNumber.SerialNumber); break; case CERT_ID_SHA1_HASH: ret = compare_cert_by_sha1_hash(pCertContext, dwType, dwFlags, &id->u.HashId); break; case CERT_ID_KEY_IDENTIFIER: { DWORD size = 0; ret = CertGetCertificateContextProperty(pCertContext, CERT_KEY_IDENTIFIER_PROP_ID, NULL, &size); if (ret && size == id->u.KeyId.cbData) { LPBYTE buf = CryptMemAlloc(size); if (buf) { CertGetCertificateContextProperty(pCertContext, CERT_KEY_IDENTIFIER_PROP_ID, buf, &size); ret = !memcmp(buf, id->u.KeyId.pbData, size); CryptMemFree(buf); } else ret = FALSE; } else ret = FALSE; break; } default: ret = FALSE; break; } return ret; } static BOOL compare_existing_cert(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, const void *pvPara) { PCCERT_CONTEXT toCompare = pvPara; return CertCompareCertificate(pCertContext->dwCertEncodingType, pCertContext->pCertInfo, toCompare->pCertInfo); } static BOOL compare_cert_by_signature_hash(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, const void *pvPara) { const CRYPT_HASH_BLOB *hash = pvPara; DWORD size = 0; BOOL ret; ret = CertGetCertificateContextProperty(pCertContext, CERT_SIGNATURE_HASH_PROP_ID, NULL, &size); if (ret && size == hash->cbData) { LPBYTE buf = CryptMemAlloc(size); if (buf) { CertGetCertificateContextProperty(pCertContext, CERT_SIGNATURE_HASH_PROP_ID, buf, &size); ret = !memcmp(buf, hash->pbData, size); CryptMemFree(buf); } else ret = FALSE; } else ret = FALSE; return ret; } static inline PCCERT_CONTEXT cert_compare_certs_in_store(HCERTSTORE store, PCCERT_CONTEXT prev, CertCompareFunc compare, DWORD dwType, DWORD dwFlags, const void *pvPara) { BOOL matches = FALSE; PCCERT_CONTEXT ret; ret = prev; do { ret = CertEnumCertificatesInStore(store, ret); if (ret) matches = compare(ret, dwType, dwFlags, pvPara); } while (ret != NULL && !matches); return ret; } typedef PCCERT_CONTEXT (*CertFindFunc)(HCERTSTORE store, DWORD dwType, DWORD dwFlags, const void *pvPara, PCCERT_CONTEXT prev); static PCCERT_CONTEXT find_cert_any(HCERTSTORE store, DWORD dwType, DWORD dwFlags, const void *pvPara, PCCERT_CONTEXT prev) { return CertEnumCertificatesInStore(store, prev); } static PCCERT_CONTEXT find_cert_by_issuer(HCERTSTORE store, DWORD dwType, DWORD dwFlags, const void *pvPara, PCCERT_CONTEXT prev) { BOOL ret; PCCERT_CONTEXT found = NULL, subject = pvPara; PCERT_EXTENSION ext; DWORD size; if ((ext = CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER, subject->pCertInfo->cExtension, subject->pCertInfo->rgExtension))) { CERT_AUTHORITY_KEY_ID_INFO *info; ret = CryptDecodeObjectEx(subject->dwCertEncodingType, X509_AUTHORITY_KEY_ID, ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, &info, &size); if (ret) { CERT_ID id; if (info->CertIssuer.cbData && info->CertSerialNumber.cbData) { id.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER; memcpy(&id.u.IssuerSerialNumber.Issuer, &info->CertIssuer, sizeof(CERT_NAME_BLOB)); memcpy(&id.u.IssuerSerialNumber.SerialNumber, &info->CertSerialNumber, sizeof(CRYPT_INTEGER_BLOB)); } else if (info->KeyId.cbData) { id.dwIdChoice = CERT_ID_KEY_IDENTIFIER; memcpy(&id.u.KeyId, &info->KeyId, sizeof(CRYPT_HASH_BLOB)); } else ret = FALSE; if (ret) found = cert_compare_certs_in_store(store, prev, compare_cert_by_cert_id, dwType, dwFlags, &id); LocalFree(info); } } else if ((ext = CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER2, subject->pCertInfo->cExtension, subject->pCertInfo->rgExtension))) { CERT_AUTHORITY_KEY_ID2_INFO *info; ret = CryptDecodeObjectEx(subject->dwCertEncodingType, X509_AUTHORITY_KEY_ID2, ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, &info, &size); if (ret) { CERT_ID id; if (info->AuthorityCertIssuer.cAltEntry && info->AuthorityCertSerialNumber.cbData) { PCERT_ALT_NAME_ENTRY directoryName = NULL; DWORD i; for (i = 0; !directoryName && i < info->AuthorityCertIssuer.cAltEntry; i++) if (info->AuthorityCertIssuer.rgAltEntry[i].dwAltNameChoice == CERT_ALT_NAME_DIRECTORY_NAME) directoryName = &info->AuthorityCertIssuer.rgAltEntry[i]; if (directoryName) { id.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER; memcpy(&id.u.IssuerSerialNumber.Issuer, &directoryName->u.DirectoryName, sizeof(CERT_NAME_BLOB)); memcpy(&id.u.IssuerSerialNumber.SerialNumber, &info->AuthorityCertSerialNumber, sizeof(CRYPT_INTEGER_BLOB)); } else { FIXME("no supported name type in authority key id2\n"); ret = FALSE; } } else if (info->KeyId.cbData) { id.dwIdChoice = CERT_ID_KEY_IDENTIFIER; memcpy(&id.u.KeyId, &info->KeyId, sizeof(CRYPT_HASH_BLOB)); } else ret = FALSE; if (ret) found = cert_compare_certs_in_store(store, prev, compare_cert_by_cert_id, dwType, dwFlags, &id); LocalFree(info); } } else found = cert_compare_certs_in_store(store, prev, compare_cert_by_name, CERT_COMPARE_NAME | CERT_COMPARE_SUBJECT_CERT, dwFlags, &subject->pCertInfo->Issuer); return found; } static BOOL compare_cert_by_name_str(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, const void *pvPara) { PCERT_NAME_BLOB name; DWORD len; BOOL ret = FALSE; if (dwType & CERT_INFO_SUBJECT_FLAG) name = &pCertContext->pCertInfo->Subject; else name = &pCertContext->pCertInfo->Issuer; len = CertNameToStrW(pCertContext->dwCertEncodingType, name, CERT_SIMPLE_NAME_STR, NULL, 0); if (len) { LPWSTR str = CryptMemAlloc(len * sizeof(WCHAR)); if (str) { LPWSTR ptr; CertNameToStrW(pCertContext->dwCertEncodingType, name, CERT_SIMPLE_NAME_STR, str, len); for (ptr = str; *ptr; ptr++) *ptr = tolowerW(*ptr); if (strstrW(str, pvPara)) ret = TRUE; CryptMemFree(str); } } return ret; } static PCCERT_CONTEXT find_cert_by_name_str_a(HCERTSTORE store, DWORD dwType, DWORD dwFlags, const void *pvPara, PCCERT_CONTEXT prev) { PCCERT_CONTEXT found = NULL; TRACE("%s\n", debugstr_a(pvPara)); if (pvPara) { int len = MultiByteToWideChar(CP_ACP, 0, pvPara, -1, NULL, 0); LPWSTR str = CryptMemAlloc(len * sizeof(WCHAR)); if (str) { LPWSTR ptr; MultiByteToWideChar(CP_ACP, 0, pvPara, -1, str, len); for (ptr = str; *ptr; ptr++) *ptr = tolowerW(*ptr); found = cert_compare_certs_in_store(store, prev, compare_cert_by_name_str, dwType, dwFlags, str); CryptMemFree(str); } } else found = find_cert_any(store, dwType, dwFlags, NULL, prev); return found; } static PCCERT_CONTEXT find_cert_by_name_str_w(HCERTSTORE store, DWORD dwType, DWORD dwFlags, const void *pvPara, PCCERT_CONTEXT prev) { PCCERT_CONTEXT found = NULL; TRACE("%s\n", debugstr_w(pvPara)); if (pvPara) { DWORD len = strlenW(pvPara); LPWSTR str = CryptMemAlloc((len + 1) * sizeof(WCHAR)); if (str) { LPCWSTR src; LPWSTR dst; for (src = pvPara, dst = str; *src; src++, dst++) *dst = tolowerW(*src); *dst = 0; found = cert_compare_certs_in_store(store, prev, compare_cert_by_name_str, dwType, dwFlags, str); CryptMemFree(str); } } else found = find_cert_any(store, dwType, dwFlags, NULL, prev); return found; } PCCERT_CONTEXT WINAPI CertFindCertificateInStore(HCERTSTORE hCertStore, DWORD dwCertEncodingType, DWORD dwFlags, DWORD dwType, const void *pvPara, PCCERT_CONTEXT pPrevCertContext) { PCCERT_CONTEXT ret; CertFindFunc find = NULL; CertCompareFunc compare = NULL; TRACE("(%p, %08x, %08x, %08x, %p, %p)\n", hCertStore, dwCertEncodingType, dwFlags, dwType, pvPara, pPrevCertContext); switch (dwType >> CERT_COMPARE_SHIFT) { case CERT_COMPARE_ANY: find = find_cert_any; break; case CERT_COMPARE_MD5_HASH: compare = compare_cert_by_md5_hash; break; case CERT_COMPARE_SHA1_HASH: compare = compare_cert_by_sha1_hash; break; case CERT_COMPARE_NAME: compare = compare_cert_by_name; break; case CERT_COMPARE_PUBLIC_KEY: compare = compare_cert_by_public_key; break; case CERT_COMPARE_NAME_STR_A: find = find_cert_by_name_str_a; break; case CERT_COMPARE_NAME_STR_W: find = find_cert_by_name_str_w; break; case CERT_COMPARE_SUBJECT_CERT: compare = compare_cert_by_subject_cert; break; case CERT_COMPARE_CERT_ID: compare = compare_cert_by_cert_id; break; case CERT_COMPARE_ISSUER_OF: find = find_cert_by_issuer; break; case CERT_COMPARE_EXISTING: compare = compare_existing_cert; break; case CERT_COMPARE_SIGNATURE_HASH: compare = compare_cert_by_signature_hash; break; default: FIXME("find type %08x unimplemented\n", dwType); } if (find) ret = find(hCertStore, dwFlags, dwType, pvPara, pPrevCertContext); else if (compare) ret = cert_compare_certs_in_store(hCertStore, pPrevCertContext, compare, dwType, dwFlags, pvPara); else ret = NULL; if (!ret) SetLastError(CRYPT_E_NOT_FOUND); TRACE("returning %p\n", ret); return ret; } PCCERT_CONTEXT WINAPI CertGetSubjectCertificateFromStore(HCERTSTORE hCertStore, DWORD dwCertEncodingType, PCERT_INFO pCertId) { TRACE("(%p, %08x, %p)\n", hCertStore, dwCertEncodingType, pCertId); if (!pCertId) { SetLastError(E_INVALIDARG); return NULL; } return CertFindCertificateInStore(hCertStore, dwCertEncodingType, 0, CERT_FIND_SUBJECT_CERT, pCertId, NULL); } BOOL WINAPI CertVerifySubjectCertificateContext(PCCERT_CONTEXT pSubject, PCCERT_CONTEXT pIssuer, DWORD *pdwFlags) { static const DWORD supportedFlags = CERT_STORE_REVOCATION_FLAG | CERT_STORE_SIGNATURE_FLAG | CERT_STORE_TIME_VALIDITY_FLAG; if (*pdwFlags & ~supportedFlags) { SetLastError(E_INVALIDARG); return FALSE; } if (*pdwFlags & CERT_STORE_REVOCATION_FLAG) { DWORD flags = 0; PCCRL_CONTEXT crl = CertGetCRLFromStore(pSubject->hCertStore, pSubject, NULL, &flags); /* FIXME: what if the CRL has expired? */ if (crl) { if (CertVerifyCRLRevocation(pSubject->dwCertEncodingType, pSubject->pCertInfo, 1, (PCRL_INFO *)&crl->pCrlInfo)) *pdwFlags &= CERT_STORE_REVOCATION_FLAG; } else *pdwFlags |= CERT_STORE_NO_CRL_FLAG; } if (*pdwFlags & CERT_STORE_TIME_VALIDITY_FLAG) { if (0 == CertVerifyTimeValidity(NULL, pSubject->pCertInfo)) *pdwFlags &= ~CERT_STORE_TIME_VALIDITY_FLAG; } if (*pdwFlags & CERT_STORE_SIGNATURE_FLAG) { if (CryptVerifyCertificateSignatureEx(0, pSubject->dwCertEncodingType, CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, (void *)pSubject, CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *)pIssuer, 0, NULL)) *pdwFlags &= ~CERT_STORE_SIGNATURE_FLAG; } return TRUE; } PCCERT_CONTEXT WINAPI CertGetIssuerCertificateFromStore(HCERTSTORE hCertStore, PCCERT_CONTEXT pSubjectContext, PCCERT_CONTEXT pPrevIssuerContext, DWORD *pdwFlags) { PCCERT_CONTEXT ret; TRACE("(%p, %p, %p, %08x)\n", hCertStore, pSubjectContext, pPrevIssuerContext, *pdwFlags); if (!pSubjectContext) { SetLastError(E_INVALIDARG); return NULL; } ret = CertFindCertificateInStore(hCertStore, pSubjectContext->dwCertEncodingType, 0, CERT_FIND_ISSUER_OF, pSubjectContext, pPrevIssuerContext); if (ret) { if (!CertVerifySubjectCertificateContext(pSubjectContext, ret, pdwFlags)) { CertFreeCertificateContext(ret); ret = NULL; } if (CRYPT_IsCertificateSelfSigned(pSubjectContext)) { CertFreeCertificateContext(ret); ret = NULL; SetLastError(CRYPT_E_SELF_SIGNED); } } TRACE("returning %p\n", ret); return ret; } typedef struct _OLD_CERT_REVOCATION_STATUS { DWORD cbSize; DWORD dwIndex; DWORD dwError; DWORD dwReason; } OLD_CERT_REVOCATION_STATUS; typedef BOOL (WINAPI *CertVerifyRevocationFunc)(DWORD, DWORD, DWORD, void **, DWORD, PCERT_REVOCATION_PARA, PCERT_REVOCATION_STATUS); BOOL WINAPI CertVerifyRevocation(DWORD dwEncodingType, DWORD dwRevType, DWORD cContext, PVOID rgpvContext[], DWORD dwFlags, PCERT_REVOCATION_PARA pRevPara, PCERT_REVOCATION_STATUS pRevStatus) { BOOL ret; TRACE("(%08x, %d, %d, %p, %08x, %p, %p)\n", dwEncodingType, dwRevType, cContext, rgpvContext, dwFlags, pRevPara, pRevStatus); if (pRevStatus->cbSize != sizeof(OLD_CERT_REVOCATION_STATUS) && pRevStatus->cbSize != sizeof(CERT_REVOCATION_STATUS)) { SetLastError(E_INVALIDARG); return FALSE; } if (cContext) { static HCRYPTOIDFUNCSET set = NULL; DWORD size; if (!set) set = CryptInitOIDFunctionSet(CRYPT_OID_VERIFY_REVOCATION_FUNC, 0); ret = CryptGetDefaultOIDDllList(set, dwEncodingType, NULL, &size); if (ret) { if (size == 1) { /* empty list */ SetLastError(CRYPT_E_NO_REVOCATION_DLL); ret = FALSE; } else { LPWSTR dllList = CryptMemAlloc(size * sizeof(WCHAR)), ptr; if (dllList) { ret = CryptGetDefaultOIDDllList(set, dwEncodingType, dllList, &size); if (ret) { for (ptr = dllList; ret && *ptr; ptr += lstrlenW(ptr) + 1) { CertVerifyRevocationFunc func; HCRYPTOIDFUNCADDR hFunc; ret = CryptGetDefaultOIDFunctionAddress(set, dwEncodingType, ptr, 0, (void **)&func, &hFunc); if (ret) { ret = func(dwEncodingType, dwRevType, cContext, rgpvContext, dwFlags, pRevPara, pRevStatus); CryptFreeOIDFunctionAddress(hFunc, 0); } } } CryptMemFree(dllList); } else { SetLastError(ERROR_OUTOFMEMORY); ret = FALSE; } } } } else ret = TRUE; return ret; } PCRYPT_ATTRIBUTE WINAPI CertFindAttribute(LPCSTR pszObjId, DWORD cAttr, CRYPT_ATTRIBUTE rgAttr[]) { PCRYPT_ATTRIBUTE ret = NULL; DWORD i; TRACE("%s %d %p\n", debugstr_a(pszObjId), cAttr, rgAttr); if (!cAttr) return NULL; if (!pszObjId) { SetLastError(ERROR_INVALID_PARAMETER); return NULL; } for (i = 0; !ret && i < cAttr; i++) if (rgAttr[i].pszObjId && !strcmp(pszObjId, rgAttr[i].pszObjId)) ret = &rgAttr[i]; return ret; } PCERT_EXTENSION WINAPI CertFindExtension(LPCSTR pszObjId, DWORD cExtensions, CERT_EXTENSION rgExtensions[]) { PCERT_EXTENSION ret = NULL; DWORD i; TRACE("%s %d %p\n", debugstr_a(pszObjId), cExtensions, rgExtensions); if (!cExtensions) return NULL; if (!pszObjId) { SetLastError(ERROR_INVALID_PARAMETER); return NULL; } for (i = 0; !ret && i < cExtensions; i++) if (rgExtensions[i].pszObjId && !strcmp(pszObjId, rgExtensions[i].pszObjId)) ret = &rgExtensions[i]; return ret; } PCERT_RDN_ATTR WINAPI CertFindRDNAttr(LPCSTR pszObjId, PCERT_NAME_INFO pName) { PCERT_RDN_ATTR ret = NULL; DWORD i, j; TRACE("%s %p\n", debugstr_a(pszObjId), pName); if (!pszObjId) { SetLastError(ERROR_INVALID_PARAMETER); return NULL; } for (i = 0; !ret && i < pName->cRDN; i++) for (j = 0; !ret && j < pName->rgRDN[i].cRDNAttr; j++) if (pName->rgRDN[i].rgRDNAttr[j].pszObjId && !strcmp(pszObjId, pName->rgRDN[i].rgRDNAttr[j].pszObjId)) ret = &pName->rgRDN[i].rgRDNAttr[j]; return ret; } static BOOL find_matching_rdn_attr(DWORD dwFlags, const CERT_NAME_INFO *name, const CERT_RDN_ATTR *attr) { DWORD i, j; BOOL match = FALSE; for (i = 0; !match && i < name->cRDN; i++) { for (j = 0; j < name->rgRDN[i].cRDNAttr; j++) { if (!strcmp(name->rgRDN[i].rgRDNAttr[j].pszObjId, attr->pszObjId) && name->rgRDN[i].rgRDNAttr[j].dwValueType == attr->dwValueType) { if (dwFlags & CERT_UNICODE_IS_RDN_ATTRS_FLAG) { LPCWSTR nameStr = (LPCWSTR)name->rgRDN[i].rgRDNAttr[j].Value.pbData; LPCWSTR attrStr = (LPCWSTR)attr->Value.pbData; if (attr->Value.cbData != name->rgRDN[i].rgRDNAttr[j].Value.cbData) match = FALSE; else if (dwFlags & CERT_CASE_INSENSITIVE_IS_RDN_ATTRS_FLAG) match = !strncmpiW(nameStr, attrStr, attr->Value.cbData / sizeof(WCHAR)); else match = !strncmpW(nameStr, attrStr, attr->Value.cbData / sizeof(WCHAR)); TRACE("%s : %s => %d\n", debugstr_wn(nameStr, attr->Value.cbData / sizeof(WCHAR)), debugstr_wn(attrStr, attr->Value.cbData / sizeof(WCHAR)), match); } else { LPCSTR nameStr = (LPCSTR)name->rgRDN[i].rgRDNAttr[j].Value.pbData; LPCSTR attrStr = (LPCSTR)attr->Value.pbData; if (attr->Value.cbData != name->rgRDN[i].rgRDNAttr[j].Value.cbData) match = FALSE; else if (dwFlags & CERT_CASE_INSENSITIVE_IS_RDN_ATTRS_FLAG) match = !strncasecmp(nameStr, attrStr, attr->Value.cbData); else match = !strncmp(nameStr, attrStr, attr->Value.cbData); TRACE("%s : %s => %d\n", debugstr_an(nameStr, attr->Value.cbData), debugstr_an(attrStr, attr->Value.cbData), match); } } } } return match; } BOOL WINAPI CertIsRDNAttrsInCertificateName(DWORD dwCertEncodingType, DWORD dwFlags, PCERT_NAME_BLOB pCertName, PCERT_RDN pRDN) { CERT_NAME_INFO *name; LPCSTR type; DWORD size; BOOL ret; TRACE("(%08x, %08x, %p, %p)\n", dwCertEncodingType, dwFlags, pCertName, pRDN); type = dwFlags & CERT_UNICODE_IS_RDN_ATTRS_FLAG ? X509_UNICODE_NAME : X509_NAME; if ((ret = CryptDecodeObjectEx(dwCertEncodingType, type, pCertName->pbData, pCertName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &name, &size))) { DWORD i; for (i = 0; ret && i < pRDN->cRDNAttr; i++) ret = find_matching_rdn_attr(dwFlags, name, &pRDN->rgRDNAttr[i]); if (!ret) SetLastError(CRYPT_E_NO_MATCH); LocalFree(name); } return ret; } LONG WINAPI CertVerifyTimeValidity(LPFILETIME pTimeToVerify, PCERT_INFO pCertInfo) { FILETIME fileTime; LONG ret; if (!pTimeToVerify) { GetSystemTimeAsFileTime(&fileTime); pTimeToVerify = &fileTime; } if ((ret = CompareFileTime(pTimeToVerify, &pCertInfo->NotBefore)) >= 0) { ret = CompareFileTime(pTimeToVerify, &pCertInfo->NotAfter); if (ret < 0) ret = 0; } return ret; } BOOL WINAPI CertVerifyValidityNesting(PCERT_INFO pSubjectInfo, PCERT_INFO pIssuerInfo) { TRACE("(%p, %p)\n", pSubjectInfo, pIssuerInfo); return CertVerifyTimeValidity(&pSubjectInfo->NotBefore, pIssuerInfo) == 0 && CertVerifyTimeValidity(&pSubjectInfo->NotAfter, pIssuerInfo) == 0; } BOOL WINAPI CryptHashCertificate(HCRYPTPROV_LEGACY hCryptProv, ALG_ID Algid, DWORD dwFlags, const BYTE *pbEncoded, DWORD cbEncoded, BYTE *pbComputedHash, DWORD *pcbComputedHash) { BOOL ret = TRUE; HCRYPTHASH hHash = 0; TRACE("(%08lx, %d, %08x, %p, %d, %p, %p)\n", hCryptProv, Algid, dwFlags, pbEncoded, cbEncoded, pbComputedHash, pcbComputedHash); if (!hCryptProv) hCryptProv = CRYPT_GetDefaultProvider(); if (!Algid) Algid = CALG_SHA1; if (ret) { ret = CryptCreateHash(hCryptProv, Algid, 0, 0, &hHash); if (ret) { ret = CryptHashData(hHash, pbEncoded, cbEncoded, 0); if (ret) ret = CryptGetHashParam(hHash, HP_HASHVAL, pbComputedHash, pcbComputedHash, 0); CryptDestroyHash(hHash); } } return ret; } BOOL WINAPI CryptHashPublicKeyInfo(HCRYPTPROV_LEGACY hCryptProv, ALG_ID Algid, DWORD dwFlags, DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pInfo, BYTE *pbComputedHash, DWORD *pcbComputedHash) { BOOL ret = TRUE; HCRYPTHASH hHash = 0; TRACE("(%08lx, %d, %08x, %d, %p, %p, %p)\n", hCryptProv, Algid, dwFlags, dwCertEncodingType, pInfo, pbComputedHash, pcbComputedHash); if (!hCryptProv) hCryptProv = CRYPT_GetDefaultProvider(); if (!Algid) Algid = CALG_MD5; if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING) { SetLastError(ERROR_FILE_NOT_FOUND); return FALSE; } if (ret) { BYTE *buf; DWORD size = 0; ret = CRYPT_AsnEncodePubKeyInfoNoNull(dwCertEncodingType, X509_PUBLIC_KEY_INFO, pInfo, CRYPT_ENCODE_ALLOC_FLAG, NULL, (LPBYTE)&buf, &size); if (ret) { ret = CryptCreateHash(hCryptProv, Algid, 0, 0, &hHash); if (ret) { ret = CryptHashData(hHash, buf, size, 0); if (ret) ret = CryptGetHashParam(hHash, HP_HASHVAL, pbComputedHash, pcbComputedHash, 0); CryptDestroyHash(hHash); } LocalFree(buf); } } return ret; } BOOL WINAPI CryptHashToBeSigned(HCRYPTPROV_LEGACY hCryptProv, DWORD dwCertEncodingType, const BYTE *pbEncoded, DWORD cbEncoded, BYTE *pbComputedHash, DWORD *pcbComputedHash) { BOOL ret; CERT_SIGNED_CONTENT_INFO *info; DWORD size; TRACE("(%08lx, %08x, %p, %d, %p, %d)\n", hCryptProv, dwCertEncodingType, pbEncoded, cbEncoded, pbComputedHash, *pcbComputedHash); ret = CryptDecodeObjectEx(dwCertEncodingType, X509_CERT, pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size); if (ret) { PCCRYPT_OID_INFO oidInfo; HCRYPTHASH hHash; if (!hCryptProv) hCryptProv = CRYPT_GetDefaultProvider(); oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, info->SignatureAlgorithm.pszObjId, 0); if (!oidInfo) { SetLastError(NTE_BAD_ALGID); ret = FALSE; } else { ret = CryptCreateHash(hCryptProv, oidInfo->u.Algid, 0, 0, &hHash); if (ret) { ret = CryptHashData(hHash, info->ToBeSigned.pbData, info->ToBeSigned.cbData, 0); if (ret) ret = CryptGetHashParam(hHash, HP_HASHVAL, pbComputedHash, pcbComputedHash, 0); CryptDestroyHash(hHash); } } LocalFree(info); } return ret; } BOOL WINAPI CryptSignCertificate(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProv, DWORD dwKeySpec, DWORD dwCertEncodingType, const BYTE *pbEncodedToBeSigned, DWORD cbEncodedToBeSigned, PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, const void *pvHashAuxInfo, BYTE *pbSignature, DWORD *pcbSignature) { BOOL ret; PCCRYPT_OID_INFO info; HCRYPTHASH hHash; TRACE("(%08lx, %d, %d, %p, %d, %p, %p, %p, %p)\n", hCryptProv, dwKeySpec, dwCertEncodingType, pbEncodedToBeSigned, cbEncodedToBeSigned, pSignatureAlgorithm, pvHashAuxInfo, pbSignature, pcbSignature); info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, pSignatureAlgorithm->pszObjId, 0); if (!info) { SetLastError(NTE_BAD_ALGID); return FALSE; } if (info->dwGroupId == CRYPT_HASH_ALG_OID_GROUP_ID) { if (!hCryptProv) hCryptProv = CRYPT_GetDefaultProvider(); ret = CryptCreateHash(hCryptProv, info->u.Algid, 0, 0, &hHash); if (ret) { ret = CryptHashData(hHash, pbEncodedToBeSigned, cbEncodedToBeSigned, 0); if (ret) ret = CryptGetHashParam(hHash, HP_HASHVAL, pbSignature, pcbSignature, 0); CryptDestroyHash(hHash); } } else { if (!hCryptProv) { SetLastError(ERROR_INVALID_PARAMETER); ret = FALSE; } else { ret = CryptCreateHash(hCryptProv, info->u.Algid, 0, 0, &hHash); if (ret) { ret = CryptHashData(hHash, pbEncodedToBeSigned, cbEncodedToBeSigned, 0); if (ret) ret = CryptSignHashW(hHash, dwKeySpec, NULL, 0, pbSignature, pcbSignature); CryptDestroyHash(hHash); } } } return ret; } BOOL WINAPI CryptSignAndEncodeCertificate(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProv, DWORD dwKeySpec, DWORD dwCertEncodingType, LPCSTR lpszStructType, const void *pvStructInfo, PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, const void *pvHashAuxInfo, BYTE *pbEncoded, DWORD *pcbEncoded) { BOOL ret; DWORD encodedSize, hashSize; TRACE("(%08lx, %d, %d, %s, %p, %p, %p, %p, %p)\n", hCryptProv, dwKeySpec, dwCertEncodingType, debugstr_a(lpszStructType), pvStructInfo, pSignatureAlgorithm, pvHashAuxInfo, pbEncoded, pcbEncoded); ret = CryptEncodeObject(dwCertEncodingType, lpszStructType, pvStructInfo, NULL, &encodedSize); if (ret) { PBYTE encoded = CryptMemAlloc(encodedSize); if (encoded) { ret = CryptEncodeObject(dwCertEncodingType, lpszStructType, pvStructInfo, encoded, &encodedSize); if (ret) { ret = CryptSignCertificate(hCryptProv, dwKeySpec, dwCertEncodingType, encoded, encodedSize, pSignatureAlgorithm, pvHashAuxInfo, NULL, &hashSize); if (ret) { PBYTE hash = CryptMemAlloc(hashSize); if (hash) { ret = CryptSignCertificate(hCryptProv, dwKeySpec, dwCertEncodingType, encoded, encodedSize, pSignatureAlgorithm, pvHashAuxInfo, hash, &hashSize); if (ret) { CERT_SIGNED_CONTENT_INFO info = { { 0 } }; info.ToBeSigned.cbData = encodedSize; info.ToBeSigned.pbData = encoded; info.SignatureAlgorithm = *pSignatureAlgorithm; info.Signature.cbData = hashSize; info.Signature.pbData = hash; info.Signature.cUnusedBits = 0; ret = CryptEncodeObject(dwCertEncodingType, X509_CERT, &info, pbEncoded, pcbEncoded); } CryptMemFree(hash); } else ret = FALSE; } } CryptMemFree(encoded); } else ret = FALSE; } return ret; } BOOL WINAPI CryptVerifyCertificateSignature(HCRYPTPROV_LEGACY hCryptProv, DWORD dwCertEncodingType, const BYTE *pbEncoded, DWORD cbEncoded, PCERT_PUBLIC_KEY_INFO pPublicKey) { CRYPT_DATA_BLOB blob = { cbEncoded, (BYTE *)pbEncoded }; return CryptVerifyCertificateSignatureEx(hCryptProv, dwCertEncodingType, CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB, &blob, CRYPT_VERIFY_CERT_SIGN_ISSUER_PUBKEY, pPublicKey, 0, NULL); } static BOOL CRYPT_VerifyCertSignatureFromPublicKeyInfo(HCRYPTPROV_LEGACY hCryptProv, DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pubKeyInfo, const CERT_SIGNED_CONTENT_INFO *signedCert) { BOOL ret; HCRYPTKEY key; PCCRYPT_OID_INFO info; ALG_ID pubKeyID, hashID; info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, signedCert->SignatureAlgorithm.pszObjId, 0); if (!info || info->dwGroupId != CRYPT_SIGN_ALG_OID_GROUP_ID) { SetLastError(NTE_BAD_ALGID); return FALSE; } hashID = info->u.Algid; if (info->ExtraInfo.cbData >= sizeof(ALG_ID)) pubKeyID = *(ALG_ID *)info->ExtraInfo.pbData; else pubKeyID = hashID; /* Load the default provider if necessary */ if (!hCryptProv) hCryptProv = CRYPT_GetDefaultProvider(); ret = CryptImportPublicKeyInfoEx(hCryptProv, dwCertEncodingType, pubKeyInfo, pubKeyID, 0, NULL, &key); if (ret) { HCRYPTHASH hash; ret = CryptCreateHash(hCryptProv, hashID, 0, 0, &hash); if (ret) { ret = CryptHashData(hash, signedCert->ToBeSigned.pbData, signedCert->ToBeSigned.cbData, 0); if (ret) ret = CryptVerifySignatureW(hash, signedCert->Signature.pbData, signedCert->Signature.cbData, key, NULL, 0); CryptDestroyHash(hash); } CryptDestroyKey(key); } return ret; } BOOL WINAPI CryptVerifyCertificateSignatureEx(HCRYPTPROV_LEGACY hCryptProv, DWORD dwCertEncodingType, DWORD dwSubjectType, void *pvSubject, DWORD dwIssuerType, void *pvIssuer, DWORD dwFlags, void *pvReserved) { BOOL ret = TRUE; CRYPT_DATA_BLOB subjectBlob; TRACE("(%08lx, %d, %d, %p, %d, %p, %08x, %p)\n", hCryptProv, dwCertEncodingType, dwSubjectType, pvSubject, dwIssuerType, pvIssuer, dwFlags, pvReserved); switch (dwSubjectType) { case CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB: { PCRYPT_DATA_BLOB blob = pvSubject; subjectBlob.pbData = blob->pbData; subjectBlob.cbData = blob->cbData; break; } case CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT: { PCERT_CONTEXT context = pvSubject; subjectBlob.pbData = context->pbCertEncoded; subjectBlob.cbData = context->cbCertEncoded; break; } case CRYPT_VERIFY_CERT_SIGN_SUBJECT_CRL: { PCRL_CONTEXT context = pvSubject; subjectBlob.pbData = context->pbCrlEncoded; subjectBlob.cbData = context->cbCrlEncoded; break; } default: SetLastError(E_INVALIDARG); ret = FALSE; } if (ret) { PCERT_SIGNED_CONTENT_INFO signedCert = NULL; DWORD size = 0; ret = CryptDecodeObjectEx(dwCertEncodingType, X509_CERT, subjectBlob.pbData, subjectBlob.cbData, CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, &signedCert, &size); if (ret) { switch (dwIssuerType) { case CRYPT_VERIFY_CERT_SIGN_ISSUER_PUBKEY: ret = CRYPT_VerifyCertSignatureFromPublicKeyInfo(hCryptProv, dwCertEncodingType, pvIssuer, signedCert); break; case CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT: ret = CRYPT_VerifyCertSignatureFromPublicKeyInfo(hCryptProv, dwCertEncodingType, &((PCCERT_CONTEXT)pvIssuer)->pCertInfo->SubjectPublicKeyInfo, signedCert); break; case CRYPT_VERIFY_CERT_SIGN_ISSUER_CHAIN: FIXME("CRYPT_VERIFY_CERT_SIGN_ISSUER_CHAIN: stub\n"); ret = FALSE; break; case CRYPT_VERIFY_CERT_SIGN_ISSUER_NULL: if (pvIssuer) { SetLastError(E_INVALIDARG); ret = FALSE; } else { FIXME("unimplemented for NULL signer\n"); SetLastError(E_INVALIDARG); ret = FALSE; } break; default: SetLastError(E_INVALIDARG); ret = FALSE; } LocalFree(signedCert); } } return ret; } BOOL WINAPI CertGetIntendedKeyUsage(DWORD dwCertEncodingType, PCERT_INFO pCertInfo, BYTE *pbKeyUsage, DWORD cbKeyUsage) { PCERT_EXTENSION ext; BOOL ret = FALSE; TRACE("(%08x, %p, %p, %d)\n", dwCertEncodingType, pCertInfo, pbKeyUsage, cbKeyUsage); ext = CertFindExtension(szOID_KEY_USAGE, pCertInfo->cExtension, pCertInfo->rgExtension); if (ext) { CRYPT_BIT_BLOB usage; DWORD size = sizeof(usage); ret = CryptDecodeObjectEx(dwCertEncodingType, X509_BITS, ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_NOCOPY_FLAG, NULL, &usage, &size); if (ret) { if (cbKeyUsage < usage.cbData) ret = FALSE; else { memcpy(pbKeyUsage, usage.pbData, usage.cbData); if (cbKeyUsage > usage.cbData) memset(pbKeyUsage + usage.cbData, 0, cbKeyUsage - usage.cbData); } } } else SetLastError(0); return ret; } BOOL WINAPI CertGetEnhancedKeyUsage(PCCERT_CONTEXT pCertContext, DWORD dwFlags, PCERT_ENHKEY_USAGE pUsage, DWORD *pcbUsage) { PCERT_ENHKEY_USAGE usage = NULL; DWORD bytesNeeded; BOOL ret = TRUE; if (!pCertContext || !pcbUsage) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } TRACE("(%p, %08x, %p, %d)\n", pCertContext, dwFlags, pUsage, *pcbUsage); if (!(dwFlags & CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG)) { DWORD propSize = 0; if (CertGetCertificateContextProperty(pCertContext, CERT_ENHKEY_USAGE_PROP_ID, NULL, &propSize)) { LPBYTE buf = CryptMemAlloc(propSize); if (buf) { if (CertGetCertificateContextProperty(pCertContext, CERT_ENHKEY_USAGE_PROP_ID, buf, &propSize)) { ret = CryptDecodeObjectEx(pCertContext->dwCertEncodingType, X509_ENHANCED_KEY_USAGE, buf, propSize, CRYPT_ENCODE_ALLOC_FLAG, NULL, &usage, &bytesNeeded); } CryptMemFree(buf); } } } if (!usage && !(dwFlags & CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG)) { PCERT_EXTENSION ext = CertFindExtension(szOID_ENHANCED_KEY_USAGE, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension); if (ext) { ret = CryptDecodeObjectEx(pCertContext->dwCertEncodingType, X509_ENHANCED_KEY_USAGE, ext->Value.pbData, ext->Value.cbData, CRYPT_ENCODE_ALLOC_FLAG, NULL, &usage, &bytesNeeded); } } if (!usage) { /* If a particular location is specified, this should fail. Otherwise * it should succeed with an empty usage. (This is true on Win2k and * later, which we emulate.) */ if (dwFlags) { SetLastError(CRYPT_E_NOT_FOUND); ret = FALSE; } else bytesNeeded = sizeof(CERT_ENHKEY_USAGE); } if (ret) { if (!pUsage) *pcbUsage = bytesNeeded; else if (*pcbUsage < bytesNeeded) { SetLastError(ERROR_MORE_DATA); *pcbUsage = bytesNeeded; ret = FALSE; } else { *pcbUsage = bytesNeeded; if (usage) { DWORD i; LPSTR nextOID = (LPSTR)((LPBYTE)pUsage + sizeof(CERT_ENHKEY_USAGE) + usage->cUsageIdentifier * sizeof(LPSTR)); pUsage->cUsageIdentifier = usage->cUsageIdentifier; pUsage->rgpszUsageIdentifier = (LPSTR *)((LPBYTE)pUsage + sizeof(CERT_ENHKEY_USAGE)); for (i = 0; i < usage->cUsageIdentifier; i++) { pUsage->rgpszUsageIdentifier[i] = nextOID; strcpy(nextOID, usage->rgpszUsageIdentifier[i]); nextOID += strlen(nextOID) + 1; } } else pUsage->cUsageIdentifier = 0; } } if (usage) LocalFree(usage); TRACE("returning %d\n", ret); return ret; } BOOL WINAPI CertSetEnhancedKeyUsage(PCCERT_CONTEXT pCertContext, PCERT_ENHKEY_USAGE pUsage) { BOOL ret; TRACE("(%p, %p)\n", pCertContext, pUsage); if (pUsage) { CRYPT_DATA_BLOB blob = { 0, NULL }; ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE, pUsage, CRYPT_ENCODE_ALLOC_FLAG, NULL, &blob.pbData, &blob.cbData); if (ret) { ret = CertSetCertificateContextProperty(pCertContext, CERT_ENHKEY_USAGE_PROP_ID, 0, &blob); LocalFree(blob.pbData); } } else ret = CertSetCertificateContextProperty(pCertContext, CERT_ENHKEY_USAGE_PROP_ID, 0, NULL); return ret; } BOOL WINAPI CertAddEnhancedKeyUsageIdentifier(PCCERT_CONTEXT pCertContext, LPCSTR pszUsageIdentifier) { BOOL ret; DWORD size; TRACE("(%p, %s)\n", pCertContext, debugstr_a(pszUsageIdentifier)); if (CertGetEnhancedKeyUsage(pCertContext, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, NULL, &size)) { PCERT_ENHKEY_USAGE usage = CryptMemAlloc(size); if (usage) { ret = CertGetEnhancedKeyUsage(pCertContext, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, usage, &size); if (ret) { DWORD i; BOOL exists = FALSE; /* Make sure usage doesn't already exist */ for (i = 0; !exists && i < usage->cUsageIdentifier; i++) { if (!strcmp(usage->rgpszUsageIdentifier[i], pszUsageIdentifier)) exists = TRUE; } if (!exists) { PCERT_ENHKEY_USAGE newUsage = CryptMemAlloc(size + sizeof(LPSTR) + strlen(pszUsageIdentifier) + 1); if (newUsage) { LPSTR nextOID; newUsage->rgpszUsageIdentifier = (LPSTR *) ((LPBYTE)newUsage + sizeof(CERT_ENHKEY_USAGE)); nextOID = (LPSTR)((LPBYTE)newUsage->rgpszUsageIdentifier + (usage->cUsageIdentifier + 1) * sizeof(LPSTR)); for (i = 0; i < usage->cUsageIdentifier; i++) { newUsage->rgpszUsageIdentifier[i] = nextOID; strcpy(nextOID, usage->rgpszUsageIdentifier[i]); nextOID += strlen(nextOID) + 1; } newUsage->rgpszUsageIdentifier[i] = nextOID; strcpy(nextOID, pszUsageIdentifier); newUsage->cUsageIdentifier = i + 1; ret = CertSetEnhancedKeyUsage(pCertContext, newUsage); CryptMemFree(newUsage); } else ret = FALSE; } } CryptMemFree(usage); } else ret = FALSE; } else { PCERT_ENHKEY_USAGE usage = CryptMemAlloc(sizeof(CERT_ENHKEY_USAGE) + sizeof(LPSTR) + strlen(pszUsageIdentifier) + 1); if (usage) { usage->rgpszUsageIdentifier = (LPSTR *)((LPBYTE)usage + sizeof(CERT_ENHKEY_USAGE)); usage->rgpszUsageIdentifier[0] = (LPSTR)((LPBYTE)usage + sizeof(CERT_ENHKEY_USAGE) + sizeof(LPSTR)); strcpy(usage->rgpszUsageIdentifier[0], pszUsageIdentifier); usage->cUsageIdentifier = 1; ret = CertSetEnhancedKeyUsage(pCertContext, usage); CryptMemFree(usage); } else ret = FALSE; } return ret; } BOOL WINAPI CertRemoveEnhancedKeyUsageIdentifier(PCCERT_CONTEXT pCertContext, LPCSTR pszUsageIdentifier) { BOOL ret; DWORD size; CERT_ENHKEY_USAGE usage; TRACE("(%p, %s)\n", pCertContext, debugstr_a(pszUsageIdentifier)); size = sizeof(usage); ret = CertGetEnhancedKeyUsage(pCertContext, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, &usage, &size); if (!ret && GetLastError() == ERROR_MORE_DATA) { PCERT_ENHKEY_USAGE pUsage = CryptMemAlloc(size); if (pUsage) { ret = CertGetEnhancedKeyUsage(pCertContext, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, pUsage, &size); if (ret) { if (pUsage->cUsageIdentifier) { DWORD i; BOOL found = FALSE; for (i = 0; i < pUsage->cUsageIdentifier; i++) { if (!strcmp(pUsage->rgpszUsageIdentifier[i], pszUsageIdentifier)) found = TRUE; if (found && i < pUsage->cUsageIdentifier - 1) pUsage->rgpszUsageIdentifier[i] = pUsage->rgpszUsageIdentifier[i + 1]; } pUsage->cUsageIdentifier--; /* Remove the usage if it's empty */ if (pUsage->cUsageIdentifier) ret = CertSetEnhancedKeyUsage(pCertContext, pUsage); else ret = CertSetEnhancedKeyUsage(pCertContext, NULL); } } CryptMemFree(pUsage); } else ret = FALSE; } else { /* it fit in an empty usage, therefore there's nothing to remove */ ret = TRUE; } return ret; } struct BitField { DWORD cIndexes; DWORD *indexes; }; #define BITS_PER_DWORD (sizeof(DWORD) * 8) static void CRYPT_SetBitInField(struct BitField *field, DWORD bit) { DWORD indexIndex = bit / BITS_PER_DWORD; if (indexIndex + 1 > field->cIndexes) { if (field->cIndexes) field->indexes = CryptMemRealloc(field->indexes, (indexIndex + 1) * sizeof(DWORD)); else field->indexes = CryptMemAlloc(sizeof(DWORD)); if (field->indexes) { field->indexes[indexIndex] = 0; field->cIndexes = indexIndex + 1; } } if (field->indexes) field->indexes[indexIndex] |= 1 << (bit % BITS_PER_DWORD); } static BOOL CRYPT_IsBitInFieldSet(const struct BitField *field, DWORD bit) { BOOL set; DWORD indexIndex = bit / BITS_PER_DWORD; assert(field->cIndexes); set = field->indexes[indexIndex] & (1 << (bit % BITS_PER_DWORD)); return set; } BOOL WINAPI CertGetValidUsages(DWORD cCerts, PCCERT_CONTEXT *rghCerts, int *cNumOIDs, LPSTR *rghOIDs, DWORD *pcbOIDs) { BOOL ret = TRUE; DWORD i, cbOIDs = 0; BOOL allUsagesValid = TRUE; CERT_ENHKEY_USAGE validUsages = { 0, NULL }; TRACE("(%d, %p, %d, %p, %d)\n", cCerts, rghCerts, *cNumOIDs, rghOIDs, *pcbOIDs); for (i = 0; i < cCerts; i++) { CERT_ENHKEY_USAGE usage; DWORD size = sizeof(usage); ret = CertGetEnhancedKeyUsage(rghCerts[i], 0, &usage, &size); /* Success is deliberately ignored: it implies all usages are valid */ if (!ret && GetLastError() == ERROR_MORE_DATA) { PCERT_ENHKEY_USAGE pUsage = CryptMemAlloc(size); allUsagesValid = FALSE; if (pUsage) { ret = CertGetEnhancedKeyUsage(rghCerts[i], 0, pUsage, &size); if (ret) { if (!validUsages.cUsageIdentifier) { DWORD j; cbOIDs = pUsage->cUsageIdentifier * sizeof(LPSTR); validUsages.cUsageIdentifier = pUsage->cUsageIdentifier; for (j = 0; j < validUsages.cUsageIdentifier; j++) cbOIDs += lstrlenA(pUsage->rgpszUsageIdentifier[j]) + 1; validUsages.rgpszUsageIdentifier = CryptMemAlloc(cbOIDs); if (validUsages.rgpszUsageIdentifier) { LPSTR nextOID = (LPSTR) ((LPBYTE)validUsages.rgpszUsageIdentifier + validUsages.cUsageIdentifier * sizeof(LPSTR)); for (j = 0; j < validUsages.cUsageIdentifier; j++) { validUsages.rgpszUsageIdentifier[j] = nextOID; lstrcpyA(validUsages.rgpszUsageIdentifier[j], pUsage->rgpszUsageIdentifier[j]); nextOID += lstrlenA(nextOID) + 1; } } } else { struct BitField validIndexes = { 0, NULL }; DWORD j, k, numRemoved = 0; /* Merge: build a bitmap of all the indexes of * validUsages.rgpszUsageIdentifier that are in pUsage. */ for (j = 0; j < pUsage->cUsageIdentifier; j++) { for (k = 0; k < validUsages.cUsageIdentifier; k++) { if (!strcmp(pUsage->rgpszUsageIdentifier[j], validUsages.rgpszUsageIdentifier[k])) { CRYPT_SetBitInField(&validIndexes, k); break; } } } /* Merge by removing from validUsages those that are * not in the bitmap. */ for (j = 0; j < validUsages.cUsageIdentifier; j++) { if (!CRYPT_IsBitInFieldSet(&validIndexes, j)) { if (j < validUsages.cUsageIdentifier - 1) { memmove(&validUsages.rgpszUsageIdentifier[j], &validUsages.rgpszUsageIdentifier[j + numRemoved + 1], (validUsages.cUsageIdentifier - numRemoved - j - 1) * sizeof(LPSTR)); cbOIDs -= lstrlenA( validUsages.rgpszUsageIdentifier[j]) + 1 + sizeof(LPSTR); validUsages.cUsageIdentifier--; numRemoved++; } else validUsages.cUsageIdentifier--; } } CryptMemFree(validIndexes.indexes); } } CryptMemFree(pUsage); } } } ret = TRUE; if (allUsagesValid) { *cNumOIDs = -1; *pcbOIDs = 0; } else { *cNumOIDs = validUsages.cUsageIdentifier; if (!rghOIDs) *pcbOIDs = cbOIDs; else if (*pcbOIDs < cbOIDs) { *pcbOIDs = cbOIDs; SetLastError(ERROR_MORE_DATA); ret = FALSE; } else { LPSTR nextOID = (LPSTR)((LPBYTE)rghOIDs + validUsages.cUsageIdentifier * sizeof(LPSTR)); *pcbOIDs = cbOIDs; for (i = 0; i < validUsages.cUsageIdentifier; i++) { rghOIDs[i] = nextOID; lstrcpyA(nextOID, validUsages.rgpszUsageIdentifier[i]); nextOID += lstrlenA(nextOID) + 1; } } } CryptMemFree(validUsages.rgpszUsageIdentifier); TRACE("cNumOIDs: %d\n", *cNumOIDs); TRACE("returning %d\n", ret); return ret; } /* Sets the CERT_KEY_PROV_INFO_PROP_ID property of context from pInfo, or, if * pInfo is NULL, from the attributes of hProv. */ static void CertContext_SetKeyProvInfo(PCCERT_CONTEXT context, const CRYPT_KEY_PROV_INFO *pInfo, HCRYPTPROV hProv) { CRYPT_KEY_PROV_INFO info = { 0 }; BOOL ret; if (!pInfo) { DWORD size; int len; ret = CryptGetProvParam(hProv, PP_CONTAINER, NULL, &size, 0); if (ret) { LPSTR szContainer = CryptMemAlloc(size); if (szContainer) { ret = CryptGetProvParam(hProv, PP_CONTAINER, (BYTE *)szContainer, &size, 0); if (ret) { len = MultiByteToWideChar(CP_ACP, 0, szContainer, -1, NULL, 0); if (len) { info.pwszContainerName = CryptMemAlloc(len * sizeof(WCHAR)); MultiByteToWideChar(CP_ACP, 0, szContainer, -1, info.pwszContainerName, len); } } CryptMemFree(szContainer); } } ret = CryptGetProvParam(hProv, PP_NAME, NULL, &size, 0); if (ret) { LPSTR szProvider = CryptMemAlloc(size); if (szProvider) { ret = CryptGetProvParam(hProv, PP_NAME, (BYTE *)szProvider, &size, 0); if (ret) { len = MultiByteToWideChar(CP_ACP, 0, szProvider, -1, NULL, 0); if (len) { info.pwszProvName = CryptMemAlloc(len * sizeof(WCHAR)); MultiByteToWideChar(CP_ACP, 0, szProvider, -1, info.pwszProvName, len); } } CryptMemFree(szProvider); } } /* in case no CRYPT_KEY_PROV_INFO given, * we always use AT_SIGNATURE key spec */ info.dwKeySpec = AT_SIGNATURE; size = sizeof(info.dwProvType); ret = CryptGetProvParam(hProv, PP_PROVTYPE, (LPBYTE)&info.dwProvType, &size, 0); if (!ret) info.dwProvType = PROV_RSA_FULL; pInfo = &info; } CertSetCertificateContextProperty(context, CERT_KEY_PROV_INFO_PROP_ID, 0, pInfo); if (pInfo == &info) { CryptMemFree(info.pwszContainerName); CryptMemFree(info.pwszProvName); } } /* Creates a signed certificate context from the unsigned, encoded certificate * in blob, using the crypto provider hProv and the signature algorithm sigAlgo. */ static PCCERT_CONTEXT CRYPT_CreateSignedCert(const CRYPT_DER_BLOB *blob, HCRYPTPROV hProv, DWORD dwKeySpec, PCRYPT_ALGORITHM_IDENTIFIER sigAlgo) { PCCERT_CONTEXT context = NULL; BOOL ret; DWORD sigSize = 0; ret = CryptSignCertificate(hProv, dwKeySpec, X509_ASN_ENCODING, blob->pbData, blob->cbData, sigAlgo, NULL, NULL, &sigSize); if (ret) { LPBYTE sig = CryptMemAlloc(sigSize); ret = CryptSignCertificate(hProv, dwKeySpec, X509_ASN_ENCODING, blob->pbData, blob->cbData, sigAlgo, NULL, sig, &sigSize); if (ret) { CERT_SIGNED_CONTENT_INFO signedInfo; BYTE *encodedSignedCert = NULL; DWORD encodedSignedCertSize = 0; signedInfo.ToBeSigned.cbData = blob->cbData; signedInfo.ToBeSigned.pbData = blob->pbData; signedInfo.SignatureAlgorithm = *sigAlgo; signedInfo.Signature.cbData = sigSize; signedInfo.Signature.pbData = sig; signedInfo.Signature.cUnusedBits = 0; ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_CERT, &signedInfo, CRYPT_ENCODE_ALLOC_FLAG, NULL, &encodedSignedCert, &encodedSignedCertSize); if (ret) { context = CertCreateCertificateContext(X509_ASN_ENCODING, encodedSignedCert, encodedSignedCertSize); LocalFree(encodedSignedCert); } } CryptMemFree(sig); } return context; } /* Copies data from the parameters into info, where: * pSerialNumber: The serial number. Must not be NULL. * pSubjectIssuerBlob: Specifies both the subject and issuer for info. * Must not be NULL * pSignatureAlgorithm: Optional. * pStartTime: The starting time of the certificate. If NULL, the current * system time is used. * pEndTime: The ending time of the certificate. If NULL, one year past the * starting time is used. * pubKey: The public key of the certificate. Must not be NULL. * pExtensions: Extensions to be included with the certificate. Optional. */ static void CRYPT_MakeCertInfo(PCERT_INFO info, const CRYPT_DATA_BLOB *pSerialNumber, const CERT_NAME_BLOB *pSubjectIssuerBlob, const CRYPT_ALGORITHM_IDENTIFIER *pSignatureAlgorithm, const SYSTEMTIME *pStartTime, const SYSTEMTIME *pEndTime, const CERT_PUBLIC_KEY_INFO *pubKey, const CERT_EXTENSIONS *pExtensions) { static CHAR oid[] = szOID_RSA_SHA1RSA; assert(info); assert(pSerialNumber); assert(pSubjectIssuerBlob); assert(pubKey); if (pExtensions && pExtensions->cExtension) info->dwVersion = CERT_V3; else info->dwVersion = CERT_V1; info->SerialNumber.cbData = pSerialNumber->cbData; info->SerialNumber.pbData = pSerialNumber->pbData; if (pSignatureAlgorithm) info->SignatureAlgorithm = *pSignatureAlgorithm; else { info->SignatureAlgorithm.pszObjId = oid; info->SignatureAlgorithm.Parameters.cbData = 0; info->SignatureAlgorithm.Parameters.pbData = NULL; } info->Issuer.cbData = pSubjectIssuerBlob->cbData; info->Issuer.pbData = pSubjectIssuerBlob->pbData; if (pStartTime) SystemTimeToFileTime(pStartTime, &info->NotBefore); else GetSystemTimeAsFileTime(&info->NotBefore); if (pEndTime) SystemTimeToFileTime(pEndTime, &info->NotAfter); else { SYSTEMTIME endTime; if (FileTimeToSystemTime(&info->NotBefore, &endTime)) { endTime.wYear++; SystemTimeToFileTime(&endTime, &info->NotAfter); } } info->Subject.cbData = pSubjectIssuerBlob->cbData; info->Subject.pbData = pSubjectIssuerBlob->pbData; info->SubjectPublicKeyInfo = *pubKey; if (pExtensions) { info->cExtension = pExtensions->cExtension; info->rgExtension = pExtensions->rgExtension; } else { info->cExtension = 0; info->rgExtension = NULL; } } typedef RPC_STATUS (RPC_ENTRY *UuidCreateFunc)(UUID *); typedef RPC_STATUS (RPC_ENTRY *UuidToStringFunc)(UUID *, unsigned char **); typedef RPC_STATUS (RPC_ENTRY *RpcStringFreeFunc)(unsigned char **); static HCRYPTPROV CRYPT_CreateKeyProv(void) { HCRYPTPROV hProv = 0; HMODULE rpcrt = LoadLibraryA("rpcrt4"); if (rpcrt) { UuidCreateFunc uuidCreate = (UuidCreateFunc)GetProcAddress(rpcrt, "UuidCreate"); UuidToStringFunc uuidToString = (UuidToStringFunc)GetProcAddress(rpcrt, "UuidToStringA"); RpcStringFreeFunc rpcStringFree = (RpcStringFreeFunc)GetProcAddress( rpcrt, "RpcStringFreeA"); if (uuidCreate && uuidToString && rpcStringFree) { UUID uuid; RPC_STATUS status = uuidCreate(&uuid); if (status == RPC_S_OK || status == RPC_S_UUID_LOCAL_ONLY) { unsigned char *uuidStr; status = uuidToString(&uuid, &uuidStr); if (status == RPC_S_OK) { BOOL ret = CryptAcquireContextA(&hProv, (LPCSTR)uuidStr, MS_DEF_PROV_A, PROV_RSA_FULL, CRYPT_NEWKEYSET); if (ret) { HCRYPTKEY key; ret = CryptGenKey(hProv, AT_SIGNATURE, 0, &key); if (ret) CryptDestroyKey(key); } rpcStringFree(&uuidStr); } } } FreeLibrary(rpcrt); } return hProv; } PCCERT_CONTEXT WINAPI CertCreateSelfSignCertificate(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hProv, PCERT_NAME_BLOB pSubjectIssuerBlob, DWORD dwFlags, PCRYPT_KEY_PROV_INFO pKeyProvInfo, PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, PSYSTEMTIME pStartTime, PSYSTEMTIME pEndTime, PCERT_EXTENSIONS pExtensions) { PCCERT_CONTEXT context = NULL; BOOL ret, releaseContext = FALSE; PCERT_PUBLIC_KEY_INFO pubKey = NULL; DWORD pubKeySize = 0, dwKeySpec; TRACE("(%08lx, %p, %08x, %p, %p, %p, %p, %p)\n", hProv, pSubjectIssuerBlob, dwFlags, pKeyProvInfo, pSignatureAlgorithm, pStartTime, pExtensions, pExtensions); if(!pSubjectIssuerBlob) { SetLastError(ERROR_INVALID_PARAMETER); return NULL; } dwKeySpec = pKeyProvInfo ? pKeyProvInfo->dwKeySpec : AT_SIGNATURE; if (!hProv) { if (!pKeyProvInfo) { hProv = CRYPT_CreateKeyProv(); releaseContext = TRUE; } else if (pKeyProvInfo->dwFlags & CERT_SET_KEY_PROV_HANDLE_PROP_ID) { SetLastError(NTE_BAD_FLAGS); return NULL; } else { HCRYPTKEY hKey = 0; /* acquire the context using the given information*/ ret = CryptAcquireContextW(&hProv,pKeyProvInfo->pwszContainerName, pKeyProvInfo->pwszProvName,pKeyProvInfo->dwProvType, pKeyProvInfo->dwFlags); if (!ret) { if(GetLastError() != NTE_BAD_KEYSET) return NULL; /* create the key set */ ret = CryptAcquireContextW(&hProv,pKeyProvInfo->pwszContainerName, pKeyProvInfo->pwszProvName,pKeyProvInfo->dwProvType, pKeyProvInfo->dwFlags|CRYPT_NEWKEYSET); if (!ret) return NULL; } /* check if the key is here */ ret = CryptGetUserKey(hProv,dwKeySpec,&hKey); if(!ret) { if (NTE_NO_KEY == GetLastError()) { /* generate the key */ ret = CryptGenKey(hProv,dwKeySpec,0,&hKey); } if (!ret) { CryptReleaseContext(hProv,0); SetLastError(NTE_BAD_KEYSET); return NULL; } } CryptDestroyKey(hKey); releaseContext = TRUE; } } ret = CryptExportPublicKeyInfo(hProv, dwKeySpec, X509_ASN_ENCODING, NULL, &pubKeySize); if (!ret) goto end; pubKey = CryptMemAlloc(pubKeySize); if (pubKey) { ret = CryptExportPublicKeyInfo(hProv, dwKeySpec, X509_ASN_ENCODING, pubKey, &pubKeySize); if (ret) { CERT_INFO info = { 0 }; CRYPT_DER_BLOB blob = { 0, NULL }; BYTE serial[16]; CRYPT_DATA_BLOB serialBlob = { sizeof(serial), serial }; CryptGenRandom(hProv, sizeof(serial), serial); CRYPT_MakeCertInfo(&info, &serialBlob, pSubjectIssuerBlob, pSignatureAlgorithm, pStartTime, pEndTime, pubKey, pExtensions); ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, &info, CRYPT_ENCODE_ALLOC_FLAG, NULL, &blob.pbData, &blob.cbData); if (ret) { if (!(dwFlags & CERT_CREATE_SELFSIGN_NO_SIGN)) context = CRYPT_CreateSignedCert(&blob, hProv,dwKeySpec, &info.SignatureAlgorithm); else context = CertCreateCertificateContext(X509_ASN_ENCODING, blob.pbData, blob.cbData); if (context && !(dwFlags & CERT_CREATE_SELFSIGN_NO_KEY_INFO)) CertContext_SetKeyProvInfo(context, pKeyProvInfo, hProv); LocalFree(blob.pbData); } } CryptMemFree(pubKey); } end: if (releaseContext) CryptReleaseContext(hProv, 0); return context; } BOOL WINAPI CertVerifyCTLUsage(DWORD dwEncodingType, DWORD dwSubjectType, void *pvSubject, PCTL_USAGE pSubjectUsage, DWORD dwFlags, PCTL_VERIFY_USAGE_PARA pVerifyUsagePara, PCTL_VERIFY_USAGE_STATUS pVerifyUsageStatus) { FIXME("(0x%x, %d, %p, %p, 0x%x, %p, %p): stub\n", dwEncodingType, dwSubjectType, pvSubject, pSubjectUsage, dwFlags, pVerifyUsagePara, pVerifyUsageStatus); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } const void * WINAPI CertCreateContext(DWORD dwContextType, DWORD dwEncodingType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, PCERT_CREATE_CONTEXT_PARA pCreatePara) { TRACE("(0x%x, 0x%x, %p, %d, 0x%08x, %p)\n", dwContextType, dwEncodingType, pbEncoded, cbEncoded, dwFlags, pCreatePara); if (dwFlags) { FIXME("dwFlags 0x%08x not handled\n", dwFlags); return NULL; } if (pCreatePara) { FIXME("pCreatePara not handled\n"); return NULL; } switch (dwContextType) { case CERT_STORE_CERTIFICATE_CONTEXT: return CertCreateCertificateContext(dwEncodingType, pbEncoded, cbEncoded); case CERT_STORE_CRL_CONTEXT: return CertCreateCRLContext(dwEncodingType, pbEncoded, cbEncoded); case CERT_STORE_CTL_CONTEXT: return CertCreateCTLContext(dwEncodingType, pbEncoded, cbEncoded); default: WARN("unknown context type: 0x%x\n", dwContextType); return NULL; } }