/
githubmirror
/
xmlsec
Обзор
Документация
Войти
/
githubmirror
/
xmlsec
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/mscng/x509vfy.c
1 894 строки
61 KB
lsh123
(xmlsec-mscng) Added support for SHA256 in the X509Digest element (#1214)
17 июл 2026, 00:48
Не верифицирован
17 июл 2026, 00:48
e532ab0
Код
Авторство
О чём код?
/** * 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) 2018-2026 Aleksey Sanin <aleksey@aleksey.com>. All Rights Reserved. * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. */ /** * @addtogroup xmlsec_mscng_x509 * @brief X509 certificates verification support functions for MSCng. */ #include "globals.h" #ifndef XMLSEC_NO_X509 #include <string.h> #include <xmlsec/xmlsec.h> #include <xmlsec/base64.h> #include <xmlsec/bn.h> #include <xmlsec/errors.h> #include <xmlsec/keys.h> #include <xmlsec/keyinfo.h> #include <xmlsec/keysmngr.h> #include <xmlsec/xmltree.h> #include <xmlsec/private.h> #include <xmlsec/mscng/crypto.h> #include <xmlsec/mscng/x509.h> #include "private.h" #include "../cast_helpers.h" #include "../x509_helpers.h" typedef struct _xmlSecMSCngX509StoreCtx xmlSecMSCngX509StoreCtx, *xmlSecMSCngX509StoreCtxPtr; struct _xmlSecMSCngX509StoreCtx { HCERTSTORE trusted; HCERTSTORE trustedMemStore; HCERTSTORE untrusted; HCERTSTORE untrustedMemStore; HCERTSTORE crlMemStore; }; XMLSEC_KEY_DATA_STORE_DECLARE(MSCngX509Store, xmlSecMSCngX509StoreCtx) #define xmlSecMSCngX509StoreSize XMLSEC_KEY_DATA_STORE_SIZE(MSCngX509Store) // https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certclosestore // // CERT_CLOSE_STORE_CHECK_FLAG should only be used as a diagnostic tool in the development // of applications. #ifdef _DEBUG #define XMLSEC_CLOSE_STORE_FLAG (CERT_CLOSE_STORE_CHECK_FLAG) #else // _DEBUG #define XMLSEC_CLOSE_STORE_FLAG (0) #endif // _DEBUG static int xmlSecMSCngUnixTimeToFileTime (time_t in, LPFILETIME out); static FILETIME* xmlSecMSCngX509StoreGetVerificationTime(xmlSecKeyInfoCtxPtr keyInfoCtx, FILETIME* timeContainer) { xmlSecAssert2(keyInfoCtx != NULL, NULL); xmlSecAssert2(timeContainer != NULL, NULL); if(keyInfoCtx->certsVerificationTime > 0) { if(xmlSecMSCngUnixTimeToFileTime(keyInfoCtx->certsVerificationTime, timeContainer) < 0) { xmlSecInternalError("xmlSecMSCngUnixTimeToFileTime", NULL); return(NULL); } return(timeContainer); } else if((keyInfoCtx->flags & XMLSEC_KEYINFO_FLAGS_X509DATA_SKIP_TIME_CHECKS) != 0) { return(NULL); } else { GetSystemTimeAsFileTime(timeContainer); return(timeContainer); } } static int xmlSecMSCngX509StoreIsCrlTimeValid(PCCRL_CONTEXT crlCtx, LPFILETIME time) { xmlSecAssert2(crlCtx != NULL, -1); xmlSecAssert2(crlCtx->pCrlInfo != NULL, -1); if(time == NULL) { return(1); } if(CompareFileTime(time, &(crlCtx->pCrlInfo->ThisUpdate)) < 0) { return(0); } if((crlCtx->pCrlInfo->NextUpdate.dwLowDateTime != 0) || (crlCtx->pCrlInfo->NextUpdate.dwHighDateTime != 0)) { if(CompareFileTime(time, &(crlCtx->pCrlInfo->NextUpdate)) > 0) { return(0); } } return(1); } static void xmlSecMSCngX509StoreFinalize(xmlSecKeyDataStorePtr store) { xmlSecMSCngX509StoreCtxPtr ctx; int ret; xmlSecAssert(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId)); ctx = xmlSecMSCngX509StoreGetCtx(store); xmlSecAssert(ctx != NULL); if(ctx->trusted != NULL) { ret = CertCloseStore(ctx->trusted, XMLSEC_CLOSE_STORE_FLAG); if(ret == FALSE) { xmlSecMSCngLastError("CertCloseStore", xmlSecKeyDataStoreGetName(store)); /* ignore error */ } } if(ctx->trustedMemStore != NULL) { ret = CertCloseStore(ctx->trustedMemStore, XMLSEC_CLOSE_STORE_FLAG); if(ret == FALSE) { xmlSecMSCngLastError("CertCloseStore", xmlSecKeyDataStoreGetName(store)); /* ignore error */ } } if(ctx->untrusted != NULL) { ret = CertCloseStore(ctx->untrusted, XMLSEC_CLOSE_STORE_FLAG); if(ret == FALSE) { xmlSecMSCngLastError("CertCloseStore", xmlSecKeyDataStoreGetName(store)); /* ignore error */ } } if(ctx->untrustedMemStore != NULL) { ret = CertCloseStore(ctx->untrustedMemStore, XMLSEC_CLOSE_STORE_FLAG); if(ret == FALSE) { xmlSecMSCngLastError("CertCloseStore", xmlSecKeyDataStoreGetName(store)); /* ignore error */ } } if(ctx->crlMemStore != NULL) { ret = CertCloseStore(ctx->crlMemStore, XMLSEC_CLOSE_STORE_FLAG); if(ret == FALSE) { xmlSecMSCngLastError("CertCloseStore", xmlSecKeyDataStoreGetName(store)); /* ignore error */ } } memset(ctx, 0, sizeof(xmlSecMSCngX509StoreCtx)); } /** * @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 xmlSecMSCngX509StoreAdoptKeyStore(xmlSecKeyDataStorePtr store, HCERTSTORE keyStore) { xmlSecMSCngX509StoreCtxPtr ctx; int ret; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), -1); xmlSecAssert2(keyStore != NULL, -1); ctx = xmlSecMSCngX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->trusted != NULL, -1); ret = CertAddStoreToCollection(ctx->trusted, keyStore, CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 2); if(ret != TRUE) { xmlSecMSCngLastError("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 xmlSecMSCngX509StoreAdoptTrustedStore(xmlSecKeyDataStorePtr store, HCERTSTORE trustedStore) { xmlSecMSCngX509StoreCtxPtr ctx; int ret; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), -1); xmlSecAssert2( trustedStore != NULL, -1); ctx = xmlSecMSCngX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->trusted != NULL, -1); ret = CertAddStoreToCollection(ctx->trusted , trustedStore , CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG , 3); if(ret == FALSE) { xmlSecMSCngLastError("CertAddStoreToCollection", xmlSecKeyDataStoreGetName(store)); return(-1); } return(0); } /** * @brief Adds @p trustedStore to the untrusted certs list. * @details Adds @p trustedStore to the list of untrusted 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 xmlSecMSCngX509StoreAdoptUntrustedStore(xmlSecKeyDataStorePtr store, HCERTSTORE untrustedStore) { xmlSecMSCngX509StoreCtxPtr ctx; int ret; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), -1); xmlSecAssert2(untrustedStore != NULL, -1); ctx = xmlSecMSCngX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->untrusted != NULL, -1); ret = CertAddStoreToCollection(ctx->untrusted, untrustedStore, CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG , 2); if(ret == FALSE) { xmlSecMSCngLastError("CertAddStoreToCollection", xmlSecKeyDataStoreGetName(store)); return(-1); } return(0); } static int xmlSecMSCngX509StoreInitialize(xmlSecKeyDataStorePtr store) { int ret; xmlSecMSCngX509StoreCtxPtr ctx; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), -1); ctx = xmlSecMSCngX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, -1); memset(ctx, 0, sizeof(xmlSecMSCngX509StoreCtx)); /* create a trusted store that will be a collection of other stores */ ctx->trusted = CertOpenStore( CERT_STORE_PROV_COLLECTION, 0, 0, 0, NULL); if(ctx->trusted == NULL) { xmlSecMSCngLastError("CertOpenStore", xmlSecKeyDataStoreGetName(store)); return(-1); } /* create an actual trusted store */ ctx->trustedMemStore = CertOpenStore( CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL); if(ctx->trustedMemStore == NULL) { xmlSecMSCngLastError("CertOpenStore", xmlSecKeyDataStoreGetName(store)); xmlSecMSCngX509StoreFinalize(store); return(-1); } /* add the store to the trusted collection */ ret = CertAddStoreToCollection( ctx->trusted, ctx->trustedMemStore, CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 1); if(ret == 0) { xmlSecMSCngLastError("CertAddStoreToCollection", xmlSecKeyDataStoreGetName(store)); xmlSecMSCngX509StoreFinalize(store); return(-1); } /* create an untrusted store that will be a collection of other stores */ ctx->untrusted = CertOpenStore( CERT_STORE_PROV_COLLECTION, 0, 0, 0, NULL); if(ctx->untrusted == NULL) { xmlSecMSCngLastError("CertOpenStore", xmlSecKeyDataStoreGetName(store)); xmlSecMSCngX509StoreFinalize(store); return(-1); } /* create an actual untrusted store */ ctx->untrustedMemStore = CertOpenStore( CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL); if(ctx->untrustedMemStore == NULL) { xmlSecMSCngLastError("CertOpenStore", xmlSecKeyDataStoreGetName(store)); xmlSecMSCngX509StoreFinalize(store); return(-1); } /* add the store to the untrusted collection */ ret = CertAddStoreToCollection( ctx->untrusted, ctx->untrustedMemStore, CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 1); if(ret == 0) { xmlSecMSCngLastError("CertAddStoreToCollection", xmlSecKeyDataStoreGetName(store)); xmlSecMSCngX509StoreFinalize(store); return(-1); } /* create a memory store for externally loaded CRLs */ ctx->crlMemStore = CertOpenStore( CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL); if(ctx->crlMemStore == NULL) { xmlSecMSCngLastError("CertOpenStore", xmlSecKeyDataStoreGetName(store)); xmlSecMSCngX509StoreFinalize(store); return(-1); } return(0); } static xmlSecKeyDataStoreKlass xmlSecMSCngX509StoreKlass = { sizeof(xmlSecKeyDataStoreKlass), xmlSecMSCngX509StoreSize, /* data */ xmlSecNameX509Store, /* const xmlChar* name; */ /* constructors/destructor */ xmlSecMSCngX509StoreInitialize, /* xmlSecKeyDataStoreInitializeMethod initialize; */ xmlSecMSCngX509StoreFinalize, /* xmlSecKeyDataStoreFinalizeMethod finalize; */ /* reserved for the future */ NULL, /* void* reserved0; */ NULL, /* void* reserved1; */ }; /** * @brief The MSCng X509 certificates key data store klass. * @return pointer to MSCng X509 certificates key data store klass. */ xmlSecKeyDataStoreId xmlSecMSCngX509StoreGetKlass(void) { return(&xmlSecMSCngX509StoreKlass); } /** * @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 xmlSecMSCngX509StoreAdoptCert(xmlSecKeyDataStorePtr store, PCCERT_CONTEXT pCert, xmlSecKeyDataType type) { xmlSecMSCngX509StoreCtxPtr ctx; HCERTSTORE hCertStore; int ret; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), -1); xmlSecAssert2(pCert != NULL, -1); ctx = xmlSecMSCngX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->trusted != NULL, -1); if(type == xmlSecKeyDataTypeTrusted) { hCertStore = ctx->trusted; } else if(type == xmlSecKeyDataTypeNone) { hCertStore = ctx->untrusted; } else { xmlSecNotImplementedError2("MSCNG doesn't support key data type: %d", (int)type); return(-1); } xmlSecAssert2(hCertStore != NULL, -1); ret = CertAddCertificateContextToStore( hCertStore, pCert, CERT_STORE_ADD_ALWAYS, NULL); if(ret == FALSE) { xmlSecMSCngLastError("CertAddCertificateContextToStore", xmlSecKeyDataStoreGetName(store)); return(-1); } CertFreeCertificateContext(pCert); return(0); } /** * @brief Adds CRL to the store for revocation checking. * @param store the pointer to X509 key data store klass. * @param crl the pointer to PCCRL_CONTEXT X509 CRL. * @return 0 on success or a negative value if an error occurs. */ int xmlSecMSCngX509StoreAdoptCrl(xmlSecKeyDataStorePtr store, PCCRL_CONTEXT crl) { xmlSecMSCngX509StoreCtxPtr ctx; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), -1); xmlSecAssert2(crl != NULL, -1); ctx = xmlSecMSCngX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->crlMemStore != NULL, -1); if(!CertAddCRLContextToStore(ctx->crlMemStore, crl, CERT_STORE_ADD_ALWAYS, NULL)) { xmlSecMSCngLastError("CertAddCRLContextToStore", xmlSecKeyDataStoreGetName(store)); return(-1); } CertFreeCRLContext(crl); return(0); } /** * @brief Checks if @p cert is in the CRL of @p store. * @param store may contain a CRL * @param cert the certificate that is revoked (or not) * @param time the time for CRL validity check (can be NULL) * @return 0 on success or a negative value if an errors occurs. */ static int xmlSecMSCngCheckRevocation(HCERTSTORE store, PCCERT_CONTEXT cert, LPFILETIME time) { PCCRL_CONTEXT crlCtx = NULL; PCRL_ENTRY crlEntry = NULL; int isCrlTimeValid; int ret; xmlSecAssert2(store != NULL, -1); xmlSecAssert2(cert != NULL, -1); while((crlCtx = CertEnumCRLsInStore(store, crlCtx)) != NULL) { isCrlTimeValid = xmlSecMSCngX509StoreIsCrlTimeValid(crlCtx, time); if(isCrlTimeValid < 0) { xmlSecInternalError("xmlSecMSCngX509StoreIsCrlTimeValid", NULL); CertFreeCRLContext(crlCtx); return(-1); } else if(isCrlTimeValid == 0) { continue; } ret = CertFindCertificateInCRL(cert, crlCtx, 0, NULL, &crlEntry); if(ret == 0) { continue; } if(crlEntry == NULL) { continue; } xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, NULL, "cert found in CRL"); CertFreeCRLContext(crlCtx); return(-1); } return(0); } /* 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 xmlSecMSCngX509StoreVerifySubject(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) { xmlSecMSCngLastError("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); } /** * @brief Determines if cert is found in store. * @param store the certificate store * @param name the name of the subject or issuer to find * @param cert the certificate * * * @return 1 and 0 if find does or does not succeed, or a negative value if an * error occurs. */ static int xmlSecMSCngX509StoreContainsCert(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 = xmlSecMSCngX509StoreVerifySubject(cert, storeCert); if (ret < 0) { xmlSecInternalError("xmlSecMSCngX509StoreVerifySubject", NULL); continue; /* storeCert will be released in the next CertFindCertificateInStore() call */ } else if (ret == 0) { xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, NULL, "xmlSecMSCngX509StoreVerifySubject"); continue; /* storeCert will be released in the next CertFindCertificateInStore() call */ } /* success */ CertFreeCertificateContext(storeCert); return(1); } /* no luck */ return (0); } static int xmlSecMSCngVerifyCertTime(PCCERT_CONTEXT cert, LPFILETIME time) { xmlSecAssert2(cert != NULL, -1); xmlSecAssert2(cert->pCertInfo != NULL, -1); xmlSecAssert2(time != NULL, -1); if(CompareFileTime(&(cert->pCertInfo->NotBefore), time) == 1) { xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, NULL, "CompareFileTime"); return(-1); } if(CompareFileTime(&(cert->pCertInfo->NotAfter), time) == -1) { xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, NULL, "CompareFileTime"); return(-1); } return(0); } /* returns 1 if verified, 0 if not, or a negative value if an error occurs */ static int xmlSecMSCngX509StoreVerifyCertificateItself(PCCERT_CONTEXT cert, FILETIME* time, HCERTSTORE trustedStore, HCERTSTORE certStore) { int ret; xmlSecAssert2(cert != NULL, -1); xmlSecAssert2(trustedStore != NULL, -1); xmlSecAssert2(certStore != NULL, -1); /* if time is specified, check certificate notBefore/notAfter */ if (time != NULL) { ret = xmlSecMSCngVerifyCertTime(cert, time); if (ret < 0) { xmlSecInternalError("xmlSecMSCngVerifyCertTime", NULL); return(-1); } } /* check certificate revokation */ ret = xmlSecMSCngCheckRevocation(certStore, cert, time); if(ret < 0) { xmlSecInternalError("xmlSecMSCngCheckRevocation", NULL); return(-1); } /* does trustedStore contain cert directly? */ ret = xmlSecMSCngX509StoreContainsCert(trustedStore, &(cert->pCertInfo->Subject), cert); if(ret < 0) { xmlSecInternalError("xmlSecMSCngX509StoreContainsCert", NULL); return(-1); } else if(ret == 1) { /* success */ return(1); } /* does trustedStore contain the issuer cert? */ ret = xmlSecMSCngX509StoreContainsCert(trustedStore, &(cert->pCertInfo->Issuer), cert); if(ret < 0) { xmlSecInternalError("xmlSecMSCngX509StoreContainsCert", NULL); return(-1); } else if(ret == 1) { /* success */ return(1); } /* no luck */ return(0); } static PCCERT_CONTEXT xmlSecMSCngX509StoreFindIssuer(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 = xmlSecMSCngX509StoreVerifySubject(cert, issuerCert); if (ret < 0) { xmlSecInternalError("xmlSecMSCngX509StoreVerifySubject", NULL); CertFreeCertificateContext(issuerCert); return(NULL); } else if (ret == 0) { xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, NULL, "xmlSecMSCngX509StoreVerifySubject"); CertFreeCertificateContext(issuerCert); return(NULL); } return(issuerCert); } struct xmlSecMSCngX509StoreVerifyCertificateChainStep { PCCERT_CONTEXT cert; BOOL freeCert; }; #define XMLSEC_MSCNG_X509_STORE_VERIFY_CERTIFICATE_CHAIN_STEP_SIZE 32 /** * @brief Verifies @p cert against the trusted store. * @details Verifies @p cert based on trustedStore (ignoring system trusted certificates). * @param cert the certificate to verify. * @param time pointer to FILETIME that we are interested in (if NULL, don't check certificate notBefore/notAfter) * @param trustedStore trusted certificates added via xmlSecMSCngX509StoreAdoptCert(). * @param untrustedStore untrusted certificates stack. * @param certStore the certificates stack from the document. * @return 1 on success (cert verified), 0 if cert can't be verified, or a negative value if an error occurs. */ static int xmlSecMSCngX509StoreVerifyCertificateChain(PCCERT_CONTEXT cert, FILETIME* time, HCERTSTORE trustedStore, HCERTSTORE untrustedStore, HCERTSTORE certStore ) { struct xmlSecMSCngX509StoreVerifyCertificateChainStep * queue = NULL; xmlSecSize queueSize = 0, queueMaxSize = 0; PCCERT_CONTEXT currentCert = NULL; BOOL freeCurrentCert = FALSE; int res = -1; int ret; xmlSecAssert2(cert != NULL, -1); xmlSecAssert2(trustedStore != NULL, -1); xmlSecAssert2(certStore != NULL, -1); /* setup queue */ queue = (struct xmlSecMSCngX509StoreVerifyCertificateChainStep*)xmlMalloc(sizeof(struct xmlSecMSCngX509StoreVerifyCertificateChainStep) * XMLSEC_MSCNG_X509_STORE_VERIFY_CERTIFICATE_CHAIN_STEP_SIZE); if(queue == NULL) { xmlSecMallocError(sizeof(struct xmlSecMSCngX509StoreVerifyCertificateChainStep) * XMLSEC_MSCNG_X509_STORE_VERIFY_CERTIFICATE_CHAIN_STEP_SIZE, NULL); return(-1); } queueMaxSize = XMLSEC_MSCNG_X509_STORE_VERIFY_CERTIFICATE_CHAIN_STEP_SIZE; queue[0].cert = cert; 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 itself */ ret = xmlSecMSCngX509StoreVerifyCertificateItself(currentCert, time, trustedStore, certStore); if(ret < 0) { xmlSecInternalError("xmlSecMSCngX509StoreVerifyCertificateItself", NULL); goto done; } else if (ret == 1) { /* success */ res = 1; goto done; } /* is cert self-signed? no recursion 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 issuer certificates */ if(queueSize + 2 > queueMaxSize) { struct xmlSecMSCngX509StoreVerifyCertificateChainStep * newQueue; xmlSecSize newQueueMaxSize = queueMaxSize + XMLSEC_MSCNG_X509_STORE_VERIFY_CERTIFICATE_CHAIN_STEP_SIZE; newQueue = (struct xmlSecMSCngX509StoreVerifyCertificateChainStep*)xmlRealloc(queue, sizeof(struct xmlSecMSCngX509StoreVerifyCertificateChainStep) * newQueueMaxSize); if(newQueue == NULL) { xmlSecMallocError(sizeof(struct xmlSecMSCngX509StoreVerifyCertificateChainStep) * newQueueMaxSize, NULL); goto done; } queue = newQueue; queueMaxSize = newQueueMaxSize; } /* try issuer in certStore */ issuerCert = xmlSecMSCngX509StoreFindIssuer(certStore, currentCert); if(issuerCert != NULL) { queue[queueSize].cert = issuerCert; queue[queueSize].freeCert = TRUE; ++queueSize; } /* try issuer in untrustedStore */ issuerCert = xmlSecMSCngX509StoreFindIssuer(untrustedStore, currentCert); if(issuerCert != NULL) { xmlSecAssert2(queueSize < queueMaxSize, -1); queue[queueSize].cert = issuerCert; queue[queueSize].freeCert = TRUE; ++queueSize; } } if(freeCurrentCert == TRUE) { CertFreeCertificateContext(currentCert); } currentCert = NULL; freeCurrentCert = FALSE; } /* not verified */ res = 0; 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); } /** * @brief Verifies @p cert against system trusted certs. * @details Verifies @p cert based on system trusted certificates. * @param cert the certificate we check * @param time pointer to FILETIME that we are interested in (can be NULL, don't check certificate notBefore/notAfter) * @param untrustedStore untrusted certificates added via API * @param docStore untrusted certificates/CRLs extracted from a document * @param crlStore CRLs store (can be NULL) * @return 1 on success (cert verified), 0 if cert can't be verified, or a negative value if an error occurs. */ static int xmlSecMSCngX509StoreVerifyCertificateSystem(PCCERT_CONTEXT cert, FILETIME* time, HCERTSTORE untrustedStore, HCERTSTORE docStore, HCERTSTORE crlStore ) { PCCERT_CHAIN_CONTEXT pChainContext = NULL; CERT_CHAIN_PARA chainPara; HCERTSTORE chainStore = NULL; int res = -1; int ret; xmlSecAssert2(cert != NULL, -1); /* initialize data structures */ memset(&chainPara, 0, sizeof(CERT_CHAIN_PARA)); chainPara.cbSize = sizeof(CERT_CHAIN_PARA); /* create additional store for CertGetCertificateChain() */ chainStore = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0, 0, NULL); if(chainStore == NULL) { xmlSecMSCngLastError("CertOpenStore", NULL); goto done; } /* add stores to collection */ if (docStore != NULL) { ret = CertAddStoreToCollection(chainStore, docStore, 0, 0); if(ret == FALSE) { xmlSecMSCngLastError("CertAddStoreToCollection(docStore)", NULL); goto done; } } if(untrustedStore != NULL) { ret = CertAddStoreToCollection(chainStore, untrustedStore, 0, 0); if(ret == FALSE) { xmlSecMSCngLastError("CertAddStoreToCollection(untrustedStore)", NULL); goto done; } } if(crlStore != NULL) { ret = CertAddStoreToCollection(chainStore, crlStore, 0, 0); if(ret == FALSE) { xmlSecMSCngLastError("CertAddStoreToCollection(crlStore)", NULL); goto done; } } /* build a chain using CertGetCertificateChain and the certificate retrieved */ ret = CertGetCertificateChain(NULL, cert, time, chainStore, &chainPara, CERT_CHAIN_REVOCATION_CHECK_CHAIN, NULL, &pChainContext); if(ret == FALSE) { xmlSecMSCngLastError("CertGetCertificateChain", NULL); goto done; } if (pChainContext->TrustStatus.dwErrorStatus == CERT_TRUST_REVOCATION_STATUS_UNKNOWN) { CertFreeCertificateChain(pChainContext); pChainContext = NULL; ret = CertGetCertificateChain(NULL, cert, time, chainStore, &chainPara, CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, NULL, &pChainContext); if(ret == FALSE) { xmlSecMSCngLastError("CertGetCertificateChain", NULL); goto done; } } if(pChainContext->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR) { /* success: verified */ res = 1; } else { /* not verified */ res = 0; } done: if(pChainContext != NULL) { CertFreeCertificateChain(pChainContext); } if(chainStore != NULL) { CertCloseStore(chainStore, 0); } return (res); } /** * @brief Converts time_t into FILETIME timestamp. See xmlSecMSCngX509CertGetTime() * for details. */ static int xmlSecMSCngUnixTimeToFileTime(time_t in, LPFILETIME out) { /* 64-bit value */ LONGLONG ll; xmlSecAssert2(out != NULL, -1); /* seconds -> 100 nanoseconds */ /* 1970-01-01 epoch -> 1601-01-01 epoch */ ll = in * 10000000LL + 116444736000000000LL; out->dwLowDateTime = (DWORD)ll; out->dwHighDateTime = (DWORD)(ll >> 32); return(0); } /** * @brief Verifies @p cert. * @param ctx the pointer to X509 certificate context store klass. * @param cert the certificate to verify. * @param certStore the untrusted certificates stack. * @param keyInfoCtx the pointer to <dsig:KeyInfo/> element processing context. * @return 1 on success (cert verified), 0 if cert can't be verified, or a negative value if an error occurs. */ static int xmlSecMSCngX509StoreVerifyCertificate(xmlSecMSCngX509StoreCtxPtr ctx, PCCERT_CONTEXT cert, HCERTSTORE certStore, xmlSecKeyInfoCtx* keyInfoCtx ) { FILETIME timeContainer; FILETIME* time; int ret; xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->trusted != NULL, -1); xmlSecAssert2(cert != NULL, -1); xmlSecAssert2(cert->pCertInfo != NULL, -1); xmlSecAssert2(certStore != NULL, -1); xmlSecAssert2(keyInfoCtx != NULL, -1); if ((keyInfoCtx->flags & XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS) != 0) { /* no need to verify anything */ return(1); } time = xmlSecMSCngX509StoreGetVerificationTime(keyInfoCtx, &timeContainer); /* check certificate revocation against externally loaded CRLs */ if(ctx->crlMemStore != NULL) { ret = xmlSecMSCngCheckRevocation(ctx->crlMemStore, cert, time); if(ret < 0) { xmlSecInternalError("xmlSecMSCngCheckRevocation", NULL); return(-1); } } /* verify based on the own trusted certificates */ ret = xmlSecMSCngX509StoreVerifyCertificateChain(cert, time, ctx->trusted, ctx->untrusted, certStore); if(ret < 0){ xmlSecInternalError("xmlSecMSCngX509StoreVerifyCertificateOwn", NULL); return(-1); } else if(ret == 1) { /* success */ return(1); } /* verify based on the system certificates */ ret = xmlSecMSCngX509StoreVerifyCertificateSystem(cert, time, ctx->untrusted, certStore, ctx->crlMemStore); if (ret < 0) { xmlSecInternalError("xmlSecMSCngX509StoreVerifyCertificateSystem", NULL); return(-1); } else if (ret == 1) { /* success */ return(1); } /* not verified */ return(0); } /** * @brief Verifies @p key with the keys manager @p mngr created with #xmlSecCryptoAppDefaultKeysMngrInit * @param store the pointer to X509 key data store klass. * @param key the pointer to key. * @param keyInfoCtx the key info context for verification. * * function: * - Checks that key certificate is present * - Checks that key certificate is valid * * Adds @p key to the keys manager @p mngr created with #xmlSecCryptoAppDefaultKeysMngrInit * function. * * @return 1 if key is verified, 0 otherwise, or a negative value if an error occurs. */ int xmlSecMSCngX509StoreVerifyKey(xmlSecKeyDataStorePtr store, xmlSecKeyPtr key, xmlSecKeyInfoCtxPtr keyInfoCtx) { xmlSecMSCngX509StoreCtxPtr ctx; xmlSecKeyDataPtr x509Data; PCCERT_CONTEXT keyCert; HCERTSTORE certStore; int ret; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), -1); xmlSecAssert2(key != NULL, -1); xmlSecAssert2(keyInfoCtx != NULL, -1); ctx = xmlSecMSCngX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, -1); /* retrieve X509 data and get key cert */ x509Data = xmlSecKeyGetData(key, xmlSecMSCngKeyDataX509Id); if (x509Data == NULL) { xmlSecInternalError("xmlSecKeyGetData(xmlSecMSCngKeyDataX509Id)", xmlSecKeyDataStoreGetName(store)); return(0); /* key cannot be verified w/o key cert */ } keyCert = xmlSecMSCngKeyDataX509GetKeyCert(x509Data); if (keyCert == NULL) { xmlSecInternalError("xmlSecMSCngKeyDataX509GetKeyCert", xmlSecKeyDataStoreGetName(store)); return(0); /* key cannot be verified w/o key cert */ } certStore = xmlSecMSCngKeyDataX509GetCertStore(x509Data); if (certStore == 0) { xmlSecInternalError("xmlSecMSCngKeyDataX509GetKeyCert", xmlSecKeyDataStoreGetName(store)); return(-1); } /* need to actually verify the certificate */ ret = xmlSecMSCngX509StoreVerifyCertificate(ctx, keyCert, certStore, keyInfoCtx); if (ret < 0) { xmlSecInternalError("xmlSecMSCngX509StoreVerifyCertificate", xmlSecKeyDataStoreGetName(store)); return(-1); } else if (ret != 1) { return(0); /* key cannot be verified */ } /* success */ return(1); } /** * @brief Verifies @p crl by checking: * @param store the pointer to X509 key data store klass. * @param crl the CRL to verify. * @param keyInfoCtx the key info context for verification parameters. * * - The CRL signature is valid (signed by a trusted issuer certificate) * - thisUpdate <= verification_time <= nextUpdate * * @return 1 if verified, 0 if not verified, or a negative value if an error occurs. */ int xmlSecMSCngX509StoreVerifyCrl(xmlSecKeyDataStorePtr store, PCCRL_CONTEXT crl, xmlSecKeyInfoCtxPtr keyInfoCtx ) { xmlSecMSCngX509StoreCtxPtr ctx; PCCERT_CONTEXT issuerCert = NULL; FILETIME timeContainer; FILETIME* time; BOOL verified = FALSE; int ret; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), -1); xmlSecAssert2(crl != NULL, -1); xmlSecAssert2(crl->pCrlInfo != NULL, -1); xmlSecAssert2(keyInfoCtx != NULL, -1); if ((keyInfoCtx->flags & XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS) != 0) { return(1); } ctx = xmlSecMSCngX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, -1); time = xmlSecMSCngX509StoreGetVerificationTime(keyInfoCtx, &timeContainer); /* find the issuer certificate in the trusted store and verify the CRL signature */ issuerCert = CertFindCertificateInStore(ctx->trusted, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_NAME, &(crl->pCrlInfo->Issuer), NULL); while (issuerCert != NULL) { verified = CryptVerifyCertificateSignatureEx( (HCRYPTPROV_LEGACY)NULL, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, CRYPT_VERIFY_CERT_SIGN_SUBJECT_CRL, (void*)crl, CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void*)issuerCert, 0, NULL); if (verified == TRUE) { CertFreeCertificateContext(issuerCert); issuerCert = NULL; break; } /* try next matching cert; CertFindCertificateInStore frees issuerCert */ issuerCert = CertFindCertificateInStore(ctx->trusted, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_NAME, &(crl->pCrlInfo->Issuer), issuerCert); } /* if not verified via trusted store, also search untrusted store (e.g. intermediate CAs) */ if (verified == FALSE) { issuerCert = CertFindCertificateInStore(ctx->untrusted, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_NAME, &(crl->pCrlInfo->Issuer), NULL); while (issuerCert != NULL) { if (CryptVerifyCertificateSignatureEx( (HCRYPTPROV_LEGACY)NULL, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, CRYPT_VERIFY_CERT_SIGN_SUBJECT_CRL, (void*)crl, CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void*)issuerCert, 0, NULL) == TRUE) { /* verify that the issuer cert itself chains to a trusted root */ ret = xmlSecMSCngX509StoreVerifyCertificateChain(issuerCert, time, ctx->trusted, ctx->untrusted, ctx->untrusted); if (ret < 0) { xmlSecInternalError("xmlSecMSCngX509StoreVerifyCertificateOwn", NULL); CertFreeCertificateContext(issuerCert); return(-1); } if (ret == 1) { verified = TRUE; CertFreeCertificateContext(issuerCert); issuerCert = NULL; break; } } /* try next matching cert; CertFindCertificateInStore frees issuerCert */ issuerCert = CertFindCertificateInStore(ctx->untrusted, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_NAME, &(crl->pCrlInfo->Issuer), issuerCert); } } if (verified == FALSE) { xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, xmlSecKeyDataStoreGetName(store), "CRL signature verification failed"); return(0); } /* check time validity */ if (time != NULL) { if (CompareFileTime(time, &(crl->pCrlInfo->ThisUpdate)) < 0) { xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, xmlSecKeyDataStoreGetName(store), "CRL is not yet valid (thisUpdate is in the future)"); return(0); } if ((crl->pCrlInfo->NextUpdate.dwLowDateTime != 0) || (crl->pCrlInfo->NextUpdate.dwHighDateTime != 0)) { if (CompareFileTime(time, &(crl->pCrlInfo->NextUpdate)) > 0) { xmlSecOtherError(XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, xmlSecKeyDataStoreGetName(store), "CRL has expired (nextUpdate is in the past)"); return(0); } } } return(1); } /** * @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 xmlSecMSCngX509StoreVerify(xmlSecKeyDataStorePtr store, HCERTSTORE certs, xmlSecKeyInfoCtx* keyInfoCtx) { xmlSecMSCngX509StoreCtxPtr ctx; PCCERT_CONTEXT cert = NULL; int ret; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), NULL); xmlSecAssert2(certs != NULL, NULL); xmlSecAssert2(keyInfoCtx != NULL, NULL); ctx = xmlSecMSCngX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, NULL); while((cert = CertEnumCertificatesInStore(certs, cert)) != NULL) { PCCERT_CONTEXT foundCert = NULL; int skip = 0; xmlSecAssert2(cert->pCertInfo != NULL, NULL); /* is cert the issuer of a certificate in certs? if so, skip it */ do { foundCert = CertFindCertificateInStore(certs, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ISSUER_NAME, &(cert->pCertInfo->Subject), foundCert); /* don't skip self-signed certificates */ if((foundCert != NULL) && !CertCompareCertificateName(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &(foundCert->pCertInfo->Subject), &(foundCert->pCertInfo->Issuer))) { skip = 1; } } while(skip == 0 && foundCert != NULL); if(foundCert != NULL) { CertFreeCertificateContext(foundCert); } if(skip == 0) { /* verify the certificate */ ret = xmlSecMSCngX509StoreVerifyCertificate(ctx, cert, certs, keyInfoCtx); if (ret < 0) { xmlSecInternalError("xmlSecMSCngX509StoreVerifyCertificate", xmlSecKeyDataStoreGetName(store)); continue; /* ignore errors and continue to the next cert */ } else if (ret != 1) { continue; /* ignore verification failures and continue to the next cert */ } /* success! */ return(cert); } } return(NULL); } static LPTSTR xmlSecMSCngX509GetCertName(const xmlChar* name) { xmlChar* copy; xmlChar* p; LPTSTR res; xmlSecAssert2(name != 0, NULL); /* emailAddress= results in an error, E= does not, so replace the former */ copy = xmlStrdup(name); if(copy == NULL) { xmlSecStrdupError(name, NULL); return(NULL); } while((p = (xmlChar*)xmlStrstr(copy, BAD_CAST "emailAddress=")) != NULL) { memcpy(p, " E=", 13); } res = xmlSecWin32ConvertUtf8ToTstr(copy); if(res == NULL) { xmlSecInternalError("xmlSecWin32ConvertUtf8ToTstr", NULL); xmlFree(copy); return(NULL); } xmlFree(copy); return(res); } static BYTE* xmlSecMSCngCertStrToName(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)) { xmlSecMSCngLastError("CertStrToName", NULL); xmlFree(str); return(NULL); } return(str); } static PCCERT_CONTEXT xmlSecMSCngX509FindCertByIssuerNameAndSerial(HCERTSTORE store, LPTSTR wcIssuerName, xmlSecBnPtr issuerSerialBn, DWORD dwCertEncodingType) { PCCERT_CONTEXT res = NULL; CERT_INFO certInfo; BYTE* bdata = NULL; xmlSecSize issuerSerialSize; DWORD len; xmlSecAssert2(store != 0, NULL); xmlSecAssert2(wcIssuerName != NULL, NULL); xmlSecAssert2(issuerSerialBn != NULL, NULL); certInfo.SerialNumber.pbData = xmlSecBnGetData(issuerSerialBn); issuerSerialSize = xmlSecBnGetSize(issuerSerialBn); XMLSEC_SAFE_CAST_SIZE_TO_ULONG(issuerSerialSize, certInfo.SerialNumber.cbData, return(NULL), NULL); /* CASE 1: UTF8, DN */ if (NULL == res) { bdata = xmlSecMSCngCertStrToName(dwCertEncodingType, wcIssuerName, 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); bdata = NULL; } } /* CASE 2: UTF8, REVERSE DN */ if (NULL == res) { bdata = xmlSecMSCngCertStrToName(dwCertEncodingType, wcIssuerName, 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); bdata = NULL; } } /* CASE 3: UNICODE, DN */ if (NULL == res) { bdata = xmlSecMSCngCertStrToName(dwCertEncodingType, wcIssuerName, 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); bdata = NULL; } } /* CASE 4: UNICODE, REVERSE DN */ if (NULL == res) { bdata = xmlSecMSCngCertStrToName(dwCertEncodingType, wcIssuerName, 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); bdata = NULL; } } if (bdata != NULL) { xmlFree(bdata); } return(res); } static PCCERT_CONTEXT xmlSecMSCngX509FindCertBySki(HCERTSTORE store, const xmlSecByte* ski, DWORD skiLen, DWORD dwCertEncodingType) { CRYPT_HASH_BLOB blob; xmlSecAssert2(store != 0, NULL); xmlSecAssert2(ski != NULL, NULL); xmlSecAssert2(skiLen > 0, NULL); blob.pbData = (PBYTE)ski; /* remove const */ blob.cbData = skiLen; return(CertFindCertificateInStore(store, dwCertEncodingType, 0, CERT_FIND_KEY_IDENTIFIER, &blob, NULL)); } /* SHA1 and SHA256 digests are currently supported */ static PCCERT_CONTEXT xmlSecMSCngX509FindCertByDigest(HCERTSTORE store, const xmlSecByte* digest, DWORD digestLen, DWORD dwCertEncodingType, DWORD findType) { CRYPT_HASH_BLOB blob; xmlSecAssert2(store != 0, NULL); xmlSecAssert2(digest != NULL, NULL); xmlSecAssert2(digestLen > 0, NULL); xmlSecAssert2(findType != 0, NULL); blob.pbData = (PBYTE)digest; /* remove const */ blob.cbData = digestLen; return(CertFindCertificateInStore(store, dwCertEncodingType, 0, findType, &blob, NULL)); } PCCERT_CONTEXT xmlSecMSCngX509FindCert(HCERTSTORE store, xmlSecMSCngX509FindCertCtxPtr findCertCtx) { DWORD dwCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; PCCERT_CONTEXT cert = NULL; xmlSecAssert2(store != 0, NULL); xmlSecAssert2(findCertCtx != 0, NULL); if((cert == NULL) && (findCertCtx->wcSubjectName != NULL)) { cert = xmlSecMSCngX509FindCertBySubject(store, findCertCtx->wcSubjectName, dwCertEncodingType); } if((cert == NULL) && (findCertCtx->wcIssuerName != NULL) && (findCertCtx->issuerSerialBn != NULL)) { cert = xmlSecMSCngX509FindCertByIssuerNameAndSerial(store, findCertCtx->wcIssuerName, findCertCtx->issuerSerialBn, dwCertEncodingType); } if((cert == NULL) && (findCertCtx->ski != NULL) && (findCertCtx->skiLen > 0)) { cert = xmlSecMSCngX509FindCertBySki(store, findCertCtx->ski, findCertCtx->skiLen, dwCertEncodingType); } if ((cert == NULL) && (findCertCtx->digestValue != NULL) && (findCertCtx->digestLen > 0) && (findCertCtx->digestFindType != 0)) { cert = xmlSecMSCngX509FindCertByDigest(store, findCertCtx->digestValue, findCertCtx->digestLen, dwCertEncodingType, findCertCtx->digestFindType); } return(cert); } /* caller must free returned string with xmlFree() */ LPCWSTR xmlSecMSCngX509GetFriendlyNameUnicode(PCCERT_CONTEXT cert) { DWORD dwPropSize; PBYTE pbFriendlyName; BOOL ret; xmlSecAssert2(cert != 0, NULL); /* CERT_FRIENDLY_NAME_PROP_ID: Returns a null-terminated Unicode character * string that contains the display name for the certificate. */ ret = CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, NULL, &dwPropSize); if (ret != TRUE) { /* name might not exists */ return(NULL); } pbFriendlyName = xmlMalloc(dwPropSize); if (pbFriendlyName == NULL) { xmlSecMallocError(dwPropSize, NULL); return(NULL); } ret = CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, pbFriendlyName, &dwPropSize); if ((ret != TRUE) || (dwPropSize <= 0)) { xmlSecMSCngLastError("CertGetCertificateContextProperty", NULL); xmlFree(pbFriendlyName); return(NULL); } /* success: always unicode string! */ return((LPCWSTR)pbFriendlyName); } /* caller must free returned string with xmlFree() */ xmlChar* xmlSecMSCngX509GetFriendlyNameUtf8(PCCERT_CONTEXT cert) { LPCWSTR str; xmlChar* res; xmlSecAssert2(cert != 0, NULL); str = xmlSecMSCngX509GetFriendlyNameUnicode(cert); if (str == NULL) { /* name might not exists */ return(NULL); } /* convert name to utf8 */ res = xmlSecWin32ConvertUnicodeToUtf8(str); if (res == NULL) { xmlSecInternalError("xmlSecWin32ConvertUnicodeToUtf8", NULL); xmlFree((void*)str); return(NULL); } /* success */ xmlFree((void*)str); return(res); } /** * @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 xmlSecMSCngX509StoreFindCert(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(xmlSecMSCngX509StoreFindCert_ex(store, subjectName, issuerName, issuerSerial, (xmlSecByte*)ski, skiDecodedSize, keyInfoCtx)); } else { return(xmlSecMSCngX509StoreFindCert_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 xmlSecMSCngX509StoreFindCert_ex(xmlSecKeyDataStorePtr store, xmlChar* subjectName, xmlChar* issuerName, xmlChar* issuerSerial, xmlSecByte* ski, xmlSecSize skiSize, xmlSecKeyInfoCtx* keyInfoCtx XMLSEC_ATTRIBUTE_UNUSED) { xmlSecMSCngX509FindCertCtx findCertCtx; xmlSecMSCngX509StoreCtxPtr ctx; PCCERT_CONTEXT cert = NULL; int ret; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), NULL); UNREFERENCED_PARAMETER(keyInfoCtx); ctx = xmlSecMSCngX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, NULL); ret = xmlSecMSCngX509FindCertCtxInitialize(&findCertCtx, subjectName, issuerName, issuerSerial, ski, skiSize); if (ret < 0) { xmlSecInternalError("xmlSecMSCngX509FindCertCtxInitialize", NULL); xmlSecMSCngX509FindCertCtxFinalize(&findCertCtx); return(NULL); } /* search untrusted certs store */ if ((cert == NULL) && (ctx->untrusted != NULL)) { cert = xmlSecMSCngX509FindCert(ctx->untrusted, &findCertCtx); } /* search trusted certs store */ if ((cert == NULL) && (ctx->trusted != NULL)) { cert = xmlSecMSCngX509FindCert(ctx->trusted, &findCertCtx); } /* done */ xmlSecMSCngX509FindCertCtxFinalize(&findCertCtx); return(cert); } PCCERT_CONTEXT xmlSecMSCngX509StoreFindCertByValue(xmlSecKeyDataStorePtr store, xmlSecKeyX509DataValuePtr x509Value) { xmlSecMSCngX509FindCertCtx findCertCtx; xmlSecMSCngX509StoreCtxPtr ctx; PCCERT_CONTEXT cert = NULL; int ret; xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCngX509StoreId), NULL); xmlSecAssert2(x509Value != NULL, NULL); ctx = xmlSecMSCngX509StoreGetCtx(store); xmlSecAssert2(ctx != NULL, NULL); ret = xmlSecMSCngX509FindCertCtxInitializeFromValue(&findCertCtx, x509Value); if (ret < 0) { xmlSecInternalError("xmlSecMSCngX509FindCertCtxInitializeFromValue", NULL); xmlSecMSCngX509FindCertCtxFinalize(&findCertCtx); return(NULL); } /* search untrusted certs store */ if ((cert == NULL) && (ctx->untrusted != NULL)) { cert = xmlSecMSCngX509FindCert(ctx->untrusted, &findCertCtx); } /* search trusted certs store */ if ((cert == NULL) && (ctx->trusted != NULL)) { cert = xmlSecMSCngX509FindCert(ctx->trusted, &findCertCtx); } /* done */ xmlSecMSCngX509FindCertCtxFinalize(&findCertCtx); return(cert); } /** * @brief Searches for a cert by @p subject in the 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 xmlSecMSCngX509FindCertBySubject(HCERTSTORE store, 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(res == NULL) { bdata = xmlSecMSCngCertStrToName(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(res == NULL) { bdata = xmlSecMSCngCertStrToName(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(res == NULL) { bdata = xmlSecMSCngCertStrToName(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(res == NULL) { bdata = xmlSecMSCngCertStrToName(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); } } return(res); } /****************************************************************************** * * xmlSecMSCngX509FindCert functions * *****************************************************************************/ int xmlSecMSCngX509FindCertCtxInitialize(xmlSecMSCngX509FindCertCtxPtr ctx, const xmlChar* subjectName, const xmlChar* issuerName, const xmlChar* issuerSerial, const xmlSecByte* ski, xmlSecSize skiSize ) { int ret; xmlSecAssert2(ctx != NULL, -1); memset(ctx, 0, sizeof(*ctx)); /* simplest one first */ if ((ski != NULL) && (skiSize > 0)) { ctx->ski = ski; XMLSEC_SAFE_CAST_SIZE_TO_UINT(skiSize, ctx->skiLen, return(-1), NULL); } if (subjectName != NULL) { ctx->wcSubjectName = xmlSecMSCngX509GetCertName(subjectName); if (ctx->wcSubjectName == NULL) { xmlSecInternalError("xmlSecMSCngX509GetCertName(subject)", NULL); xmlSecMSCngX509FindCertCtxFinalize(ctx); return(-1); } } if ((issuerName != NULL) && (issuerSerial != NULL)) { ctx->wcIssuerName = xmlSecMSCngX509GetCertName(issuerName); if (ctx->wcIssuerName == NULL) { xmlSecInternalError("xmlSecMSCngX509GetCertName(issuer)", NULL); xmlSecMSCngX509FindCertCtxFinalize(ctx); return(-1); } ctx->issuerSerialBn = xmlSecBnCreate(0); if (ctx->issuerSerialBn == NULL) { xmlSecInternalError("xmlSecBnCreate(issuerSerial)", NULL); xmlSecMSCngX509FindCertCtxFinalize(ctx); return(-1); } ret = xmlSecBnFromDecString(ctx->issuerSerialBn, issuerSerial); if (ret < 0) { xmlSecInternalError("xmlSecBnFromDecString(issuerSerial)", NULL); xmlSecMSCngX509FindCertCtxFinalize(ctx); return(-1); } /* MS Windows wants this in the opposite order */ ret = xmlSecBnReverse(ctx->issuerSerialBn); if (ret < 0) { xmlSecInternalError("xmlSecBnReverse", NULL); xmlSecMSCngX509FindCertCtxFinalize(ctx); return(-1); } } /* done! */ return(0); } int xmlSecMSCngX509FindCertCtxInitializeFromValue(xmlSecMSCngX509FindCertCtxPtr ctx, xmlSecKeyX509DataValuePtr x509Value) { int ret; xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(x509Value != NULL, -1); ret = xmlSecMSCngX509FindCertCtxInitialize(ctx, x509Value->subject, x509Value->issuerName, x509Value->issuerSerial, xmlSecBufferGetData(&(x509Value->ski)), xmlSecBufferGetSize(&(x509Value->ski)) ); if (ret < 0) { xmlSecInternalError("xmlSecMSCngX509FindCertCtxInitialize", NULL); xmlSecMSCngX509FindCertCtxFinalize(ctx); return(-1); } if ((!xmlSecBufferIsEmpty(&(x509Value->digest))) && (x509Value->digestAlgorithm != NULL)) { xmlSecSize digestSize; /* SHA1 and SHA256 algorithms are currently supported */ if (xmlStrcmp(x509Value->digestAlgorithm, xmlSecHrefSha1) == 0) { ctx->digestFindType = CERT_FIND_SHA1_HASH; } else if (xmlStrcmp(x509Value->digestAlgorithm, xmlSecHrefSha256) == 0) { ctx->digestFindType = CERT_FIND_SHA256_HASH; } else { xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_ALGORITHM, NULL, "href=%s", xmlSecErrorsSafeString(x509Value->digestAlgorithm)); xmlSecMSCngX509FindCertCtxFinalize(ctx); return(-1); } ctx->digestValue = xmlSecBufferGetData(&(x509Value->digest)); digestSize = xmlSecBufferGetSize(&(x509Value->digest)); XMLSEC_SAFE_CAST_SIZE_TO_UINT(digestSize, ctx->digestLen, return(-1), NULL); } /* done */ return(0); } void xmlSecMSCngX509FindCertCtxFinalize(xmlSecMSCngX509FindCertCtxPtr ctx) { xmlSecAssert(ctx != NULL); if (ctx->wcSubjectName != NULL) { xmlFree(ctx->wcSubjectName); } if (ctx->wcIssuerName != NULL) { xmlFree(ctx->wcIssuerName); } if (ctx->issuerSerialBn != NULL) { xmlSecBnDestroy(ctx->issuerSerialBn); } memset(ctx, 0, sizeof(*ctx)); } #endif /* XMLSEC_NO_X509 */