/
githubmirror
/
xmlsec
Обзор
Документация
Войти
/
githubmirror
/
xmlsec
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/mscrypto/x509vfy.c
1 376 строк
46 KB
lsh123
Fix doc build warnings (#1159)
07 май 2026, 01:12
Не верифицирован
07 май 2026, 01:12
52ab355
Код
Авторство
О чём код?
/** * XML Security Library (http://www.aleksey.com/xmlsec). * * This is free software; see the Copyright file in the source distribution for precise wording. * * Copyright (C) 2003 Cordys R&D BV, All rights reserved. * Copyright (C) 2002-2026 Aleksey Sanin <aleksey@aleksey.com>. All Rights Reserved. */ /** * @addtogroup xmlsec_mscrypto_x509 * @brief X509 certificates verification support functions for MSCrypto. */ #include "globals.h" #ifndef XMLSEC_NO_X509 #include <stdlib.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <errno.h> #include <xmlsec/xmlsec.h> #include <xmlsec/keys.h> #include <xmlsec/keyinfo.h> #include <xmlsec/keysmngr.h> #include <xmlsec/base64.h> #include <xmlsec/bn.h> #include <xmlsec/errors.h> #include <xmlsec/xmltree.h> #include <xmlsec/private.h> #include <xmlsec/mscrypto/crypto.h> #include <xmlsec/mscrypto/x509.h> #include "private.h" #include "../cast_helpers.h" #include "../x509_helpers.h" /****************************************************************************** * * Internal MSCRYPTO X509 store CTX * *****************************************************************************/ typedef struct _xmlSecMSCryptoX509StoreCtx xmlSecMSCryptoX509StoreCtx, *xmlSecMSCryptoX509StoreCtxPtr; struct _xmlSecMSCryptoX509StoreCtx { HCERTSTORE trusted; HCERTSTORE untrusted; int dont_use_system_trusted_certs; }; /****************************************************************************** * * xmlSecMSCryptoKeyDataStoreX509Id: * *****************************************************************************/ XMLSEC_KEY_DATA_STORE_DECLARE(MSCryptoX509Store, xmlSecMSCryptoX509StoreCtx) #define xmlSecMSCryptoX509StoreSize XMLSEC_KEY_DATA_STORE_SIZE(MSCryptoX509Store) static int xmlSecMSCryptoX509StoreInitialize (xmlSecKeyDataStorePtr store); static void xmlSecMSCryptoX509StoreFinalize (xmlSecKeyDataStorePtr store); static xmlSecKeyDataStoreKlass xmlSecMSCryptoX509StoreKlass = { sizeof(xmlSecKeyDataStoreKlass), xmlSecMSCryptoX509StoreSize, /* data */ xmlSecNameX509Store, /* const xmlChar* name; */ /* constructors/destructor */ xmlSecMSCryptoX509StoreInitialize, /* xmlSecKeyDataStoreInitializeMethod initialize; */ xmlSecMSCryptoX509StoreFinalize, /* xmlSecKeyDataStoreFinalizeMethod finalize; */ /* reserved for the future */ NULL, /* void* reserved0; */ NULL, /* void* reserved1; */ }; static PCCERT_CONTEXT xmlSecMSCryptoX509FindCert(HCERTSTORE store, const xmlChar *subjectName, const xmlChar *issuerName, const xmlChar *issuerSerial, const xmlSecByte* ski, xmlSecSize skiSize); /** * @brief The MSCrypto X509 certificates store klass. * @details The MSCrypto X509 certificates key data store klass. * @return pointer to MSCrypto X509 certificates key data store klass. */ xmlSecKeyDataStoreId xmlSecMSCryptoX509StoreGetKlass(void) { return(&xmlSecMSCryptoX509StoreKlass); } /** * @brief Searches @p store for a certificate that matches given criteria. * @param store the pointer to X509 key data store klass. * @param subjectName the desired certificate name. * @param issuerName the desired certificate issuer name. * @param issuerSerial the desired certificate issuer serial number. * @param ski the desired certificate SKI. * @param keyInfoCtx the pointer to <dsig:KeyInfo/> element processing context. * * * @return pointer to found certificate or NULL if certificate is not found * or an error occurs. */ PCCERT_CONTEXT xmlSecMSCryptoX509StoreFindCert(xmlSecKeyDataStorePtr store, xmlChar *subjectName, xmlChar *issuerName, xmlChar *issuerSerial, xmlChar *ski, xmlSecKeyInfoCtx* keyInfoCtx) { if (ski != NULL) { xmlSecSize skiDecodedSize = 0; int ret; /* our usual trick with base64 decode */ ret = xmlSecBase64DecodeInPlace(ski, &skiDecodedSize); if (ret < 0) { xmlSecInternalError2("xmlSecBase64DecodeInPlace", NULL, "ski=%s", xmlSecErrorsSafeString(ski)); return(NULL); } return(xmlSecMSCryptoX509StoreFindCert_ex(store, subjectName, issuerName, issuerSerial, (xmlSecByte*)ski, skiDecodedSize, keyInfoCtx)); } else { return(xmlSecMSCryptoX509StoreFindCert_ex(store, subjectName, issuerName, issuerSerial, NULL, 0, keyInfoCtx)); } } /** * @brief Searches @p store for a certificate that matches given criteria. * @param store the pointer to X509 key data store klass. * @param subjectName the desired certificate name. * @param issuerName the desired certificate issuer name. * @param issuerSerial the desired certificate issuer serial number. * @param ski the desired certificate SKI. * @param skiSize the desired certificate SKI size. * @param keyInfoCtx the pointer to <dsig:KeyInfo/> element processing context. * * * @return pointer to found certificate or NULL if certificate is not found * or an error occurs. */ PCCERT_CONTEXT xmlSecMSCryptoX509StoreFindCert_ex(xmlSecKeyDataStorePtr store, xmlChar* subjectName, xmlChar* issuerName, xmlChar* issuerSerial, xmlSecByte* ski, xmlSecSize skiSize, xmlSecKeyInfoCtx* keyInfoCtx XMLSEC_ATTRIBUTE_UNUSED) { xmlSecMSCryptoX509StoreCtxPtr ctx; PCCERT_CONTEXT pCert = NULL; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCryptoX509StoreId), NULL); UNREFERENCED_PARAMETER(keyInfoCtx); ctx = xmlSecMSCryptoX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, NULL); /* search untrusted certs store */ if ((ctx->untrusted != NULL) && (pCert == NULL)) { pCert = xmlSecMSCryptoX509FindCert(ctx->untrusted, subjectName, issuerName, issuerSerial, ski, skiSize); } /* search untrusted certs store */ if ((ctx->trusted != NULL) && (pCert == NULL)) { pCert = xmlSecMSCryptoX509FindCert(ctx->trusted, subjectName, issuerName, issuerSerial, ski, skiSize); } return pCert; } static void xmlSecMSCryptoUnixTimeToFileTime(time_t t, LPFILETIME pft) { /* Note that LONGLONG is a 64-bit value */ LONGLONG ll; xmlSecAssert(pft != NULL); ll = t * 10000000LL + 116444736000000000LL; pft->dwLowDateTime = (DWORD)ll; pft->dwHighDateTime = (DWORD)(ll >> 32); } static BOOL xmlSecMSCryptoVerifyCertTime(PCCERT_CONTEXT pCert, LPFILETIME pft) { xmlSecAssert2(pCert != NULL, FALSE); xmlSecAssert2(pCert->pCertInfo != NULL, FALSE); xmlSecAssert2(pft != NULL, FALSE); if(1 == CompareFileTime(&(pCert->pCertInfo->NotBefore), pft)) { return (FALSE); } if(-1 == CompareFileTime(&(pCert->pCertInfo->NotAfter), pft)) { return (FALSE); } return (TRUE); } static BOOL xmlSecMSCryptoCheckRevocation(HCERTSTORE hStore, PCCERT_CONTEXT pCert) { PCCRL_CONTEXT pCrl = NULL; PCRL_ENTRY pCrlEntry = NULL; xmlSecAssert2(pCert != NULL, FALSE); xmlSecAssert2(hStore != NULL, FALSE); while((pCrl = CertEnumCRLsInStore(hStore, pCrl)) != NULL) { if (CertFindCertificateInCRL(pCert, pCrl, 0, NULL, &pCrlEntry) && (pCrlEntry != NULL)) { xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, NULL, "CertFindCertificateInCRL: cert found in crl list"); CertFreeCRLContext(pCrl); return(FALSE); } } return(TRUE); } /** * @brief Builds certificates chain using Windows API. * @param cert the certificate we check * @param pfTime pointer to FILETIME that we are interested in * @param store_untrusted untrusted certificates added via API * @param store_doc untrusted certificates/CRLs extracted from a document * @return TRUE on success or FALSE otherwise. */ static BOOL xmlSecBuildChainUsingWinapi (PCCERT_CONTEXT cert, LPFILETIME pfTime, HCERTSTORE store_untrusted, HCERTSTORE store_doc) { PCCERT_CHAIN_CONTEXT pChainContext = NULL; CERT_CHAIN_PARA chainPara; BOOL rc = FALSE; HCERTSTORE store_add = NULL; /* Initialize data structures. */ memset(&chainPara, 0, sizeof(CERT_CHAIN_PARA)); chainPara.cbSize = sizeof(CERT_CHAIN_PARA); /* Create additional store for CertGetCertificateChain() */ store_add = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0, 0, NULL); if (!store_add) { xmlSecMSCryptoError("CertOpenStore", NULL); goto end; } if (!CertAddStoreToCollection(store_add, store_doc, 0, 0)) { xmlSecMSCryptoError("CertAddStoreToCollection", NULL); goto end; } if (!CertAddStoreToCollection(store_add, store_untrusted, 0, 0)) { xmlSecMSCryptoError("CertAddStoreToCollection", NULL); goto end; } /* Build a chain using CertGetCertificateChain and the certificate retrieved. */ if(!CertGetCertificateChain(NULL, /* use the default chain engine */ cert, pfTime, store_add, &chainPara, CERT_CHAIN_REVOCATION_CHECK_CHAIN, NULL, &pChainContext)) { xmlSecMSCryptoError("CertGetCertificateChain", NULL); goto end; } if (pChainContext->TrustStatus.dwErrorStatus == CERT_TRUST_REVOCATION_STATUS_UNKNOWN) { CertFreeCertificateChain(pChainContext); pChainContext = NULL; if(!CertGetCertificateChain(NULL, /* use the default chain engine */ cert, pfTime, store_add, &chainPara, CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, NULL, &pChainContext)) { xmlSecMSCryptoError("CertGetCertificateChain", NULL); goto end; } } if (pChainContext->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR) { rc = TRUE; } end: if (pChainContext) CertFreeCertificateChain(pChainContext); if (store_add) CertCloseStore(store_add, 0); return (rc); } /* this function does NOT check for time validity (see xmlSecMSCngVerifyCertTime) * returns <0 if there is an error; 0 if verification failed and >0 if verification succeeded */ static int xmlSecMSCryptoX509StoreVerifySubject(PCCERT_CONTEXT cert, PCCERT_CONTEXT issuerCert) { DWORD flags; BOOL ret; xmlSecAssert2(cert != NULL, -1); xmlSecAssert2(issuerCert != NULL, -1); flags = CERT_STORE_REVOCATION_FLAG | CERT_STORE_SIGNATURE_FLAG; ret = CertVerifySubjectCertificateContext(cert, issuerCert, &flags); if (!ret) { xmlSecMSCryptoError("CertVerifySubjectCertificateContext", NULL); return(-1); } /* parse returned flags: https://learn.microsoft.com/en-us/previous-versions/windows/embedded/ms883939(v=msdn.10) */ if ((flags & CERT_STORE_SIGNATURE_FLAG) != 0) { xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, NULL, "CertVerifySubjectCertificateContext: CERT_STORE_SIGNATURE_FLAG"); return(0); } else if (((flags & CERT_STORE_REVOCATION_FLAG) != 0) && ((flags & CERT_STORE_NO_CRL_FLAG) == 0)) { /* If CERT_STORE_REVOCATION_FLAG is enabled and the issuer does not have a CRL in the store, then CERT_STORE_NO_CRL_FLAG is set in addition to CERT_STORE_REVOCATION_FLAG. */ xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, NULL, "CertVerifySubjectCertificateContext: CERT_STORE_REVOCATION_FLAG"); return(0); } /* success */ return(1); } static int xmlSecMSCryptoX509StoreContainsCert(HCERTSTORE store, CERT_NAME_BLOB* name, PCCERT_CONTEXT cert) { PCCERT_CONTEXT storeCert = NULL; int ret; xmlSecAssert2(store != NULL, -1); xmlSecAssert2(name != NULL, -1); xmlSecAssert2(cert != NULL, -1); while (TRUE) { storeCert = CertFindCertificateInStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_NAME, name, storeCert); if (storeCert == NULL) { return (0); } ret = xmlSecMSCryptoX509StoreVerifySubject(cert, storeCert); if (ret < 0) { xmlSecInternalError("xmlSecMSCryptoX509StoreVerifySubject", NULL); continue; /* storeCert will be released in the next CertFindCertificateInStore() call */ } else if (ret == 0) { xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, NULL, "xmlSecMSCryptoX509StoreVerifySubject"); continue; /* storeCert will be released in the next CertFindCertificateInStore() call */ } /* success */ CertFreeCertificateContext(storeCert); return(1); } } static PCCERT_CONTEXT xmlSecMSCryptoX509StoreFindIssuer(HCERTSTORE store, PCCERT_CONTEXT cert) { PCCERT_CONTEXT issuerCert = NULL; int ret; xmlSecAssert2(store != NULL, NULL); xmlSecAssert2(cert != NULL, NULL); issuerCert = CertFindCertificateInStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_NAME, &(cert->pCertInfo->Issuer), NULL); if(issuerCert == NULL) { return(NULL); } ret = xmlSecMSCryptoX509StoreVerifySubject(cert, issuerCert); if (ret < 0) { xmlSecInternalError("xmlSecMSCryptoX509StoreVerifySubject", NULL); CertFreeCertificateContext(issuerCert); return(NULL); } else if (ret == 0) { xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, NULL, "xmlSecMSCryptoX509StoreVerifySubject"); CertFreeCertificateContext(issuerCert); return(NULL); } return(issuerCert); } struct xmlSecMSCryptoBuildCertChainStep { PCCERT_CONTEXT cert; BOOL freeCert; }; #define XMLSEC_MSCRYPTO_BUILD_CERT_CHAIN_STEP_SIZE 32 /** * @brief Builds certificates chain manually. * @param theCert the certificate we check * @param pfTime pointer to FILETIME that we are interested in * @param store_trusted trusted certificates added via API * @param store_untrusted untrusted certificates added via API * @param certs untrusted certificates/CRLs extracted from a document * @param store pointer to store klass passed to error functions * @return TRUE on success or FALSE otherwise. */ static BOOL xmlSecMSCryptoBuildCertChainManually (PCCERT_CONTEXT theCert, LPFILETIME pfTime, HCERTSTORE store_trusted, HCERTSTORE store_untrusted, HCERTSTORE certs, xmlSecKeyDataStorePtr store) { struct xmlSecMSCryptoBuildCertChainStep * queue = NULL; xmlSecSize queueSize = 0, queueMaxSize = 0; PCCERT_CONTEXT currentCert = NULL; BOOL freeCurrentCert = FALSE; BOOL res = FALSE; int ret; xmlSecAssert2(theCert != NULL, FALSE); xmlSecAssert2(pfTime != NULL, FALSE); xmlSecAssert2(store_trusted != NULL, FALSE); xmlSecAssert2(store_untrusted != NULL, FALSE); xmlSecAssert2(certs != NULL, FALSE); xmlSecAssert2(store != NULL, FALSE); /* setup queue */ queue = (struct xmlSecMSCryptoBuildCertChainStep*)xmlMalloc( sizeof(struct xmlSecMSCryptoBuildCertChainStep) * XMLSEC_MSCRYPTO_BUILD_CERT_CHAIN_STEP_SIZE); if(queue == NULL) { xmlSecMallocError( sizeof(struct xmlSecMSCryptoBuildCertChainStep) * XMLSEC_MSCRYPTO_BUILD_CERT_CHAIN_STEP_SIZE, NULL); return(FALSE); } queueMaxSize = XMLSEC_MSCRYPTO_BUILD_CERT_CHAIN_STEP_SIZE; queue[0].cert = theCert; queue[0].freeCert = FALSE; queueSize = 1; while(queueSize > 0) { PCCERT_CONTEXT issuerCert = NULL; currentCert = queue[queueSize - 1].cert; freeCurrentCert = queue[queueSize - 1].freeCert; --queueSize; /* check certificate validity and revokation */ if (!xmlSecMSCryptoVerifyCertTime(currentCert, pfTime)) { xmlSecOtherError(XMLSEC_ERRORS_R_CERT_HAS_EXPIRED, xmlSecKeyDataStoreGetName(store), "certificate expired"); goto done; } if (!xmlSecMSCryptoCheckRevocation(certs, currentCert)) { xmlSecOtherError(XMLSEC_ERRORS_R_CRL_VERIFY_FAILED, xmlSecKeyDataStoreGetName(store), "certificate revoked"); goto done; } /* does trustedStore contain cert directly? */ ret = xmlSecMSCryptoX509StoreContainsCert(store_trusted, &(currentCert->pCertInfo->Subject), currentCert); if (ret < 0) { xmlSecInternalError("xmlSecMSCryptoX509StoreContainsCert", NULL); goto done; } else if (ret == 1) { /* success */ res = TRUE; goto done; } /* does trustedStore contain the issuer cert? */ ret = xmlSecMSCryptoX509StoreContainsCert(store_trusted, &(currentCert->pCertInfo->Issuer), currentCert); if (ret < 0) { xmlSecInternalError("xmlSecMSCryptoX509StoreContainsCert", NULL); goto done; } else if (ret == 1) { /* success */ res = TRUE; goto done; } /* is cert self-signed? no further chain building in that case */ if (CertCompareCertificateName(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &(currentCert->pCertInfo->Subject), &(currentCert->pCertInfo->Issuer)) == FALSE ) { /* we need space for at most 2 certificates */ if(queueSize + 2 > queueMaxSize) { struct xmlSecMSCryptoBuildCertChainStep * newQueue; xmlSecSize newQueueMaxSize = queueMaxSize + XMLSEC_MSCRYPTO_BUILD_CERT_CHAIN_STEP_SIZE; newQueue = (struct xmlSecMSCryptoBuildCertChainStep*)xmlRealloc(queue, sizeof(struct xmlSecMSCryptoBuildCertChainStep) * newQueueMaxSize); if(newQueue == NULL) { xmlSecMallocError( sizeof(struct xmlSecMSCryptoBuildCertChainStep) * newQueueMaxSize, NULL); goto done; } queue = newQueue; queueMaxSize = newQueueMaxSize; } /* try the untrusted certs in the chain */ issuerCert = xmlSecMSCryptoX509StoreFindIssuer(certs, currentCert); if(issuerCert != NULL) { xmlSecAssert2(queueSize < queueMaxSize, FALSE); queue[queueSize].cert = issuerCert; queue[queueSize].freeCert = TRUE; ++queueSize; } /* try the untrusted certs in the store */ issuerCert = xmlSecMSCryptoX509StoreFindIssuer(store_untrusted, currentCert); if(issuerCert != NULL) { xmlSecAssert2(queueSize < queueMaxSize, FALSE); queue[queueSize].cert = issuerCert; queue[queueSize].freeCert = TRUE; ++queueSize; } } if(freeCurrentCert == TRUE) { CertFreeCertificateContext(currentCert); } currentCert = NULL; freeCurrentCert = FALSE; } /* not verified */ done: if((currentCert != NULL) && (freeCurrentCert == TRUE)) { CertFreeCertificateContext(currentCert); } if(queue != NULL) { xmlSecSize ii; for(ii = 0; ii < queueSize; ++ii) { if((queue[ii].cert != NULL) && (queue[ii].freeCert == TRUE)) { CertFreeCertificateContext(queue[ii].cert); } } xmlFree(queue); } return(res); } static BOOL xmlSecMSCryptoX509StoreConstructCertsChain(xmlSecKeyDataStorePtr store, PCCERT_CONTEXT cert, HCERTSTORE certs, xmlSecKeyInfoCtx* keyInfoCtx) { xmlSecMSCryptoX509StoreCtxPtr ctx; PCCERT_CONTEXT tempCert = NULL; FILETIME fTime; BOOL res = FALSE; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCryptoX509StoreId), FALSE); xmlSecAssert2(cert != NULL, FALSE); xmlSecAssert2(cert->pCertInfo != NULL, FALSE); xmlSecAssert2(certs != NULL, FALSE); xmlSecAssert2(keyInfoCtx != NULL, FALSE); ctx = xmlSecMSCryptoX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, FALSE); xmlSecAssert2(ctx->trusted != NULL, FALSE); xmlSecAssert2(ctx->untrusted != NULL, FALSE); if(keyInfoCtx->certsVerificationTime > 0) { /* convert the time to FILETIME */ xmlSecMSCryptoUnixTimeToFileTime(keyInfoCtx->certsVerificationTime, &fTime); } else { /* Defaults to current time */ GetSystemTimeAsFileTime(&fTime); } /* try the certificates in the keys manager */ if(!res) { tempCert = CertEnumCertificatesInStore(ctx->trusted, NULL); if(tempCert) { CertFreeCertificateContext(tempCert); res = xmlSecMSCryptoBuildCertChainManually(cert, &fTime, ctx->trusted, ctx->untrusted, certs, store); } } /* try the certificates in the system */ if(!res && !ctx->dont_use_system_trusted_certs) { res = xmlSecBuildChainUsingWinapi(cert, &fTime, ctx->untrusted, certs); } /* done */ return res; } /** * @brief Verifies @p certs list. * @param store the pointer to X509 certificate context store klass. * @param certs the untrusted certificates stack. * @param keyInfoCtx the pointer to <dsig:KeyInfo/> element processing context. * @return pointer to the first verified certificate from @p certs. */ PCCERT_CONTEXT xmlSecMSCryptoX509StoreVerify(xmlSecKeyDataStorePtr store, HCERTSTORE certs, xmlSecKeyInfoCtx* keyInfoCtx) { PCCERT_CONTEXT cert = NULL; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCryptoX509StoreId), NULL); xmlSecAssert2(certs != NULL, NULL); xmlSecAssert2(keyInfoCtx != NULL, NULL); while((cert = CertEnumCertificatesInStore(certs, cert)) != NULL){ PCCERT_CONTEXT nextCert = NULL; unsigned char selected = 1; xmlSecAssert2(cert->pCertInfo != NULL, NULL); /* if cert is the issuer of any other cert in the list, then it is * to be skipped except a case of a celf-signed cert*/ do { nextCert = CertFindCertificateInStore(certs, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ISSUER_NAME, &(cert->pCertInfo->Subject), nextCert); if((nextCert != NULL) && !CertCompareCertificateName(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &(nextCert->pCertInfo->Subject), &(nextCert->pCertInfo->Issuer))) { selected = 0; } } while((selected == 1) && (nextCert != NULL)); if(nextCert != NULL) { CertFreeCertificateContext(nextCert); } if(selected == 1) { if((keyInfoCtx->flags & XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS) != 0 || xmlSecMSCryptoX509StoreConstructCertsChain(store, cert, certs, keyInfoCtx)) { return(cert); } } } return (NULL); } /** * @brief Adds trusted or untrusted certificate to the store. * @details Adds trusted (root) or untrusted certificate to the store. * @param store the pointer to X509 key data store klass. * @param pCert the pointer to PCCERT_CONTEXT X509 certificate. * @param type the certificate type (trusted/untrusted). * @return 0 on success or a negative value if an error occurs. */ int xmlSecMSCryptoX509StoreAdoptCert(xmlSecKeyDataStorePtr store, PCCERT_CONTEXT pCert, xmlSecKeyDataType type) { xmlSecMSCryptoX509StoreCtxPtr ctx; HCERTSTORE certStore; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCryptoX509StoreId), -1); xmlSecAssert2(pCert != NULL, -1); ctx = xmlSecMSCryptoX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->trusted != NULL, -1); xmlSecAssert2(ctx->untrusted != NULL, -1); if(type == xmlSecKeyDataTypeTrusted) { certStore = ctx->trusted; } else if(type == xmlSecKeyDataTypeNone) { certStore = ctx->untrusted; } else { xmlSecUnsupportedEnumValueError("key data type", type, xmlSecKeyDataStoreGetName(store)); return(-1); } /* TODO: The context to be added here is not duplicated first, * hopefully this will not lead to errors when closing the store * and freeing the mem for all the context in the store. */ xmlSecAssert2(certStore != NULL, -1); if (!CertAddCertificateContextToStore(certStore, pCert, CERT_STORE_ADD_ALWAYS, NULL)) { xmlSecMSCryptoError("CertAddCertificateContextToStore", xmlSecKeyDataStoreGetName(store)); return(-1); } CertFreeCertificateContext(pCert); return(0); } /** * @brief Adds @p keyStore to the list of key stores. * @param store the pointer to X509 key data store klass. * @param keyStore the pointer to keys store. * @return 0 on success or a negative value if an error occurs. */ int xmlSecMSCryptoX509StoreAdoptKeyStore (xmlSecKeyDataStorePtr store, HCERTSTORE keyStore) { xmlSecMSCryptoX509StoreCtxPtr ctx; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCryptoX509StoreId), -1); xmlSecAssert2( keyStore != NULL, -1); ctx = xmlSecMSCryptoX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->trusted != NULL, -1); if(!CertAddStoreToCollection ( ctx->trusted , keyStore , CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG , 2)) { xmlSecMSCryptoError("CertAddStoreToCollection", xmlSecKeyDataStoreGetName(store)); return(-1); } return(0); } /** * @brief Adds @p trustedStore to the trusted certs list. * @details Adds @p trustedStore to the list of trusted certs stores. * @param store the pointer to X509 key data store klass. * @param trustedStore the pointer to certs store. * @return 0 on success or a negative value if an error occurs. */ int xmlSecMSCryptoX509StoreAdoptTrustedStore (xmlSecKeyDataStorePtr store, HCERTSTORE trustedStore) { xmlSecMSCryptoX509StoreCtxPtr ctx; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCryptoX509StoreId), -1); xmlSecAssert2( trustedStore != NULL, -1); ctx = xmlSecMSCryptoX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->trusted != NULL, -1); if( !CertAddStoreToCollection ( ctx->trusted , trustedStore , CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG , 3 ) ) { xmlSecMSCryptoError("CertAddStoreToCollection", xmlSecKeyDataStoreGetName(store)); return(-1); } return(0); } /** * @brief Adds @p trustedStore to the untrusted certs list. * @details Adds @p trustedStore to the list of un-trusted certs stores. * @param store the pointer to X509 key data store klass. * @param untrustedStore the pointer to certs store. * @return 0 on success or a negative value if an error occurs. */ int xmlSecMSCryptoX509StoreAdoptUntrustedStore (xmlSecKeyDataStorePtr store, HCERTSTORE untrustedStore) { xmlSecMSCryptoX509StoreCtxPtr ctx; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCryptoX509StoreId), -1); xmlSecAssert2( untrustedStore != NULL, -1); ctx = xmlSecMSCryptoX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->untrusted != NULL, -1); if( !CertAddStoreToCollection ( ctx->untrusted , untrustedStore , CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG , 2 ) ) { xmlSecMSCryptoError("CertAddStoreToCollection", xmlSecKeyDataStoreGetName(store)); return(-1); } return(0); } /** * @brief Enables/disables the system trusted certs. * @param store the pointer to X509 key data store klass. * @param val the enable/disable flag * */ void xmlSecMSCryptoX509StoreEnableSystemTrustedCerts (xmlSecKeyDataStorePtr store, int val) { xmlSecMSCryptoX509StoreCtxPtr ctx; xmlSecAssert(xmlSecKeyDataStoreCheckId(store, xmlSecMSCryptoX509StoreId)); ctx = xmlSecMSCryptoX509StoreGetCtx(store); xmlSecAssert(ctx != NULL); xmlSecAssert(ctx->untrusted != NULL); /* it is other way around to make default value 0 mimic old behaviour */ ctx->dont_use_system_trusted_certs = !val; } static int xmlSecMSCryptoX509StoreInitialize(xmlSecKeyDataStorePtr store) { xmlSecMSCryptoX509StoreCtxPtr ctx; HCERTSTORE hTrustedMemStore ; HCERTSTORE hUntrustedMemStore ; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCryptoX509StoreId), -1); ctx = xmlSecMSCryptoX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, -1); memset(ctx, 0, sizeof(xmlSecMSCryptoX509StoreCtx)); /* create trusted certs store collection */ ctx->trusted = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0, 0, NULL); if(ctx->trusted == NULL) { xmlSecMSCryptoError("CertOpenStore", xmlSecKeyDataStoreGetName(store)); return(-1); } /* create trusted certs store */ hTrustedMemStore = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL); if(hTrustedMemStore == NULL) { xmlSecMSCryptoError("CertOpenStore", xmlSecKeyDataStoreGetName(store)); CertCloseStore(ctx->trusted, 0); ctx->trusted = NULL ; return(-1); } /* add the memory trusted certs store to trusted certs store collection */ if( !CertAddStoreToCollection( ctx->trusted, hTrustedMemStore, CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 1 ) ) { xmlSecMSCryptoError("CertAddStoreToCollection", xmlSecKeyDataStoreGetName(store)); CertCloseStore(ctx->trusted, 0); CertCloseStore(hTrustedMemStore, 0); ctx->trusted = NULL ; return(-1); } CertCloseStore(hTrustedMemStore, 0); /* create untrusted certs store collection */ ctx->untrusted = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0, 0, NULL); if(ctx->untrusted == NULL) { xmlSecMSCryptoError("CertOpenStore", xmlSecKeyDataStoreGetName(store)); CertCloseStore(ctx->trusted, 0); ctx->trusted = NULL ; return(-1); } /* create untrusted certs store */ hUntrustedMemStore = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL); if(hUntrustedMemStore == NULL) { xmlSecMSCryptoError("CertOpenStore", xmlSecKeyDataStoreGetName(store)); CertCloseStore(ctx->trusted, 0); CertCloseStore(ctx->untrusted, 0); ctx->trusted = NULL ; ctx->untrusted = NULL ; return(-1); } /* add the memory trusted certs store to untrusted certs store collection */ if( !CertAddStoreToCollection( ctx->untrusted, hUntrustedMemStore, CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 1 ) ) { xmlSecMSCryptoError("CertAddStoreToCollection", xmlSecKeyDataStoreGetName(store)); CertCloseStore(ctx->untrusted, 0); CertCloseStore(ctx->trusted, 0); CertCloseStore(hUntrustedMemStore, 0); ctx->trusted = NULL ; ctx->untrusted = NULL ; return(-1); } CertCloseStore(hUntrustedMemStore, 0); return(0); } static void xmlSecMSCryptoX509StoreFinalize(xmlSecKeyDataStorePtr store) { xmlSecMSCryptoX509StoreCtxPtr ctx; xmlSecAssert(xmlSecKeyDataStoreCheckId(store, xmlSecMSCryptoX509StoreId)); ctx = xmlSecMSCryptoX509StoreGetCtx(store); xmlSecAssert(ctx != NULL); if (ctx->trusted) { CertCloseStore(ctx->trusted, 0); } if (ctx->untrusted) { CertCloseStore(ctx->untrusted, 0); } memset(ctx, 0, sizeof(xmlSecMSCryptoX509StoreCtx)); } /****************************************************************************** * * Low-level x509 functions * *****************************************************************************/ /** * @brief Converts an input string to a cert name. * @details Converts input string to name by calling CertStrToName function. * @param dwCertEncodingType the encoding used. * @param pszX500 the string to convert. * @param dwStrType the string type. * @param len the result len. * @return a pointer to newly allocated string or NULL if an error occurs. */ static BYTE* xmlSecMSCryptoCertStrToName(DWORD dwCertEncodingType, LPTSTR pszX500, DWORD dwStrType, DWORD* len) { BYTE* str = NULL; LPCTSTR ppszError = NULL; xmlSecAssert2(pszX500 != NULL, NULL); xmlSecAssert2(len != NULL, NULL); if (!CertStrToName(dwCertEncodingType, pszX500, dwStrType, NULL, NULL, len, &ppszError)) { /* this might not be an error, string might just not exist */ return(NULL); } str = (BYTE *)xmlMalloc(sizeof(TCHAR) * ((*len) + 1)); if(str == NULL) { xmlSecMallocError(sizeof(TCHAR) * ((*len) + 1), NULL); return(NULL); } memset(str, 0, (*len) + 1); if (!CertStrToName(dwCertEncodingType, pszX500, dwStrType, NULL, str, len, NULL)) { xmlSecMSCryptoError("CertStrToName", NULL); xmlFree(str); return(NULL); } return(str); } /** * @brief Searches for a cert by @p subject in the @p store. * @details Searches for a cert with given @p subject in the @p store * @param store the pointer to certs store * @param wcSubject the cert subject (Unicode) * @param dwCertEncodingType the cert encoding type * @return cert handle on success or NULL otherwise */ PCCERT_CONTEXT xmlSecMSCryptoX509FindCertBySubject(HCERTSTORE store, const LPTSTR wcSubject, DWORD dwCertEncodingType) { PCCERT_CONTEXT res = NULL; CERT_NAME_BLOB cnb; BYTE* bdata; DWORD len; xmlSecAssert2(store != NULL, NULL); xmlSecAssert2(wcSubject != NULL, NULL); /* CASE 1: UTF8, DN */ if (NULL == res) { bdata = xmlSecMSCryptoCertStrToName(dwCertEncodingType, wcSubject, CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG | CERT_OID_NAME_STR, &len); if(bdata != NULL) { cnb.cbData = len; cnb.pbData = bdata; res = CertFindCertificateInStore(store, dwCertEncodingType, 0, CERT_FIND_SUBJECT_NAME, &cnb, NULL); xmlFree(bdata); } } /* CASE 2: UTF8, REVERSE DN */ if (NULL == res) { bdata = xmlSecMSCryptoCertStrToName(dwCertEncodingType, wcSubject, CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG | CERT_OID_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, &len); if(bdata != NULL) { cnb.cbData = len; cnb.pbData = bdata; res = CertFindCertificateInStore(store, dwCertEncodingType, 0, CERT_FIND_SUBJECT_NAME, &cnb, NULL); xmlFree(bdata); } } /* CASE 3: UNICODE, DN */ if (NULL == res) { bdata = xmlSecMSCryptoCertStrToName(dwCertEncodingType, wcSubject, CERT_OID_NAME_STR, &len); if(bdata != NULL) { cnb.cbData = len; cnb.pbData = bdata; res = CertFindCertificateInStore(store, dwCertEncodingType, 0, CERT_FIND_SUBJECT_NAME, &cnb, NULL); xmlFree(bdata); } } /* CASE 4: UNICODE, REVERSE DN */ if (NULL == res) { bdata = xmlSecMSCryptoCertStrToName(dwCertEncodingType, wcSubject, CERT_OID_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, &len); if(bdata != NULL) { cnb.cbData = len; cnb.pbData = bdata; res = CertFindCertificateInStore(store, dwCertEncodingType, 0, CERT_FIND_SUBJECT_NAME, &cnb, NULL); xmlFree(bdata); } } /* done */ return (res); } /** * @brief Searches for a cert by @p issuer in the @p store. * @details Searches for a cert with given @p subject in the @p store * @param store the pointer to certs store * @param wcIssuer the cert issuer (Unicode) * @param issuerSerialBn the cert issuer serial * @param dwCertEncodingType the cert encoding type * @return cert handle on success or NULL otherwise */ static PCCERT_CONTEXT xmlSecMSCryptoX509FindCertByIssuer(HCERTSTORE store, const LPTSTR wcIssuer, xmlSecBnPtr issuerSerialBn, DWORD dwCertEncodingType) { PCCERT_CONTEXT res = NULL; xmlSecSize size; CERT_INFO certInfo; BYTE* bdata; DWORD len; xmlSecAssert2(store != NULL, NULL); xmlSecAssert2(wcIssuer != NULL, NULL); xmlSecAssert2(issuerSerialBn != NULL, NULL); certInfo.SerialNumber.pbData = xmlSecBnGetData(issuerSerialBn); size = xmlSecBnGetSize(issuerSerialBn); XMLSEC_SAFE_CAST_SIZE_TO_ULONG(size, certInfo.SerialNumber.cbData, return(NULL), NULL); /* CASE 1: UTF8, DN */ if (NULL == res) { bdata = xmlSecMSCryptoCertStrToName(dwCertEncodingType, wcIssuer, CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG | CERT_OID_NAME_STR, &len); if(bdata != NULL) { certInfo.Issuer.cbData = len; certInfo.Issuer.pbData = bdata; res = CertFindCertificateInStore(store, dwCertEncodingType, 0, CERT_FIND_SUBJECT_CERT, &certInfo, NULL); xmlFree(bdata); } } /* CASE 2: UTF8, REVERSE DN */ if (NULL == res) { bdata = xmlSecMSCryptoCertStrToName(dwCertEncodingType, wcIssuer, CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG | CERT_OID_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, &len); if(bdata != NULL) { certInfo.Issuer.cbData = len; certInfo.Issuer.pbData = bdata; res = CertFindCertificateInStore(store, dwCertEncodingType, 0, CERT_FIND_SUBJECT_CERT, &certInfo, NULL); xmlFree(bdata); } } /* CASE 3: UNICODE, DN */ if (NULL == res) { bdata = xmlSecMSCryptoCertStrToName(dwCertEncodingType, wcIssuer, CERT_OID_NAME_STR, &len); if(bdata != NULL) { certInfo.Issuer.cbData = len; certInfo.Issuer.pbData = bdata; res = CertFindCertificateInStore(store, dwCertEncodingType, 0, CERT_FIND_SUBJECT_CERT, &certInfo, NULL); xmlFree(bdata); } } /* CASE 4: UNICODE, REVERSE DN */ if (NULL == res) { bdata = xmlSecMSCryptoCertStrToName(dwCertEncodingType, wcIssuer, CERT_OID_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, &len); if(bdata != NULL) { certInfo.Issuer.cbData = len; certInfo.Issuer.pbData = bdata; res = CertFindCertificateInStore(store, dwCertEncodingType, 0, CERT_FIND_SUBJECT_CERT, &certInfo, NULL); xmlFree(bdata); } } /* done */ return (res); } static LPTSTR xmlSecMSCryptoX509GetCertName(const xmlChar * name) { xmlChar *name2 = NULL; xmlChar *p = NULL; LPTSTR res = NULL; xmlSecAssert2(name != 0, NULL); /* MSCrypto doesn't support "emailAddress" attribute (see NSS as well). * This code is not bullet proof and may produce incorrect results if someone has * "emailAddress=" string in one of the fields, but it is best I can suggest to fix * this problem. */ name2 = xmlStrdup(name); if(name2 == NULL) { xmlSecStrdupError(name, NULL); return(NULL); } while( (p = (xmlChar*)xmlStrstr(name2, BAD_CAST "emailAddress=")) != NULL) { memcpy(p, " E=", 13); } /* get name */ res = xmlSecWin32ConvertUtf8ToTstr(name2); if(res == NULL) { xmlSecInternalError("xmlSecWin32ConvertUtf8ToTstr", NULL); xmlFree(name2); return(NULL); } /* done */ xmlFree(name2); return(res); } static PCCERT_CONTEXT xmlSecMSCryptoX509FindCertBySki(HCERTSTORE store, const xmlSecByte* ski, xmlSecSize skiSize) { CRYPT_HASH_BLOB blob; xmlSecAssert2(store != 0, NULL); xmlSecAssert2(ski != NULL, NULL); xmlSecAssert2(skiSize > 0, NULL); blob.pbData = (xmlSecByte*)ski; XMLSEC_SAFE_CAST_SIZE_TO_ULONG(skiSize, blob.cbData, return(NULL), NULL); return(CertFindCertificateInStore(store, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_KEY_IDENTIFIER, &blob, NULL)); } static PCCERT_CONTEXT xmlSecMSCryptoX509FindCert(HCERTSTORE store, const xmlChar *subjectName, const xmlChar *issuerName, const xmlChar *issuerSerial, const xmlSecByte* ski, xmlSecSize skiSize) { PCCERT_CONTEXT pCert = NULL; int ret; xmlSecAssert2(store != 0, NULL); if((pCert == NULL) && (NULL != subjectName)) { LPTSTR wcSubjectName = NULL; /* get unicode subject name */ wcSubjectName = xmlSecMSCryptoX509GetCertName(subjectName); if(wcSubjectName == NULL) { xmlSecInternalError("xmlSecMSCryptoX509GetCertName(subjectName)", NULL); return(NULL); } /* search */ pCert = xmlSecMSCryptoX509FindCertBySubject(store, wcSubjectName, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING); /* cleanup */ xmlFree(wcSubjectName); } if((pCert == NULL) && (NULL != issuerName) && (NULL != issuerSerial)) { xmlSecBn issuerSerialBn; LPTSTR wcIssuerName = NULL; /* get serial number */ ret = xmlSecBnInitialize(&issuerSerialBn, 0); if(ret < 0) { xmlSecInternalError("xmlSecBnInitialize", NULL); return(NULL); } ret = xmlSecBnFromDecString(&issuerSerialBn, issuerSerial); if(ret < 0) { xmlSecInternalError("xmlSecBnInitialize", NULL); xmlSecBnFinalize(&issuerSerialBn); return(NULL); } /* I have no clue why at a sudden a swap is needed to * convert from lsb... This code is purely based upon * trial and error :( WK */ ret = xmlSecBnReverse(&issuerSerialBn); if(ret < 0) { xmlSecInternalError("xmlSecBnReverse", NULL); xmlSecBnFinalize(&issuerSerialBn); return(NULL); } /* get issuer name */ wcIssuerName = xmlSecMSCryptoX509GetCertName(issuerName); if(wcIssuerName == NULL) { xmlSecInternalError("xmlSecMSCryptoX509GetCertName(issuerName)", NULL); xmlSecBnFinalize(&issuerSerialBn); return(NULL); } /* search */ pCert = xmlSecMSCryptoX509FindCertByIssuer(store, wcIssuerName, &issuerSerialBn, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING); xmlFree(wcIssuerName); /* cleanup */ xmlSecBnFinalize(&issuerSerialBn); } if((pCert == NULL) && (ski != NULL) && (skiSize > 0)) { pCert = xmlSecMSCryptoX509FindCertBySki(store, ski, skiSize); } return(pCert); } /** * @brief Gets the certificate name string. * @details Gets the name string for certificate (see CertGetNameString description in MSDN). * @param pCertContext the pointer to cert * @param dwType the type (see CertGetNameString description in MSDN) * @param dwFlags the flags (see CertGetNameString description in MSDN) * @param pvTypePara the type parameter (see CertGetNameString description in MSDN) * @return name string (should be freed with xmlFree) or NULL if failed. */ xmlChar * xmlSecMSCryptoX509GetNameString(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, void *pvTypePara) { LPTSTR name = NULL; xmlChar * res = NULL; DWORD dwSize; xmlSecAssert2(pCertContext != NULL, NULL); /* get size first */ dwSize = CertGetNameString(pCertContext, dwType, dwFlags, pvTypePara, NULL, 0); if(dwSize <= 0) { xmlSecMSCryptoError("CertGetNameString", NULL); return (NULL); } /* allocate buffer */ name = (LPTSTR)xmlMalloc(sizeof(TCHAR) * (dwSize + 1)); if(name == NULL) { xmlSecMallocError(sizeof(TCHAR) * (dwSize + 1), NULL); return (NULL); } /* actually get the name */ dwSize = CertGetNameString(pCertContext, dwType, dwFlags, pvTypePara, name, dwSize); if(dwSize <= 0) { xmlSecMSCryptoError("CertGetNameString", NULL); xmlFree(name); return (NULL); } res = xmlSecWin32ConvertTstrToUtf8(name); if(res == NULL) { xmlSecInternalError("xmlSecWin32ConvertTstrToUtf8", NULL); xmlFree(name); return (NULL); } /* done */ xmlFree(name); return (res); } #endif /* XMLSEC_NO_X509 */