/**
 * XMLSec library
 *
 * X509 support
 *
 *
 * This is free software; see Copyright file in the source
 * distribution for preciese wording.
 *
 * Copyright (C) 2003 Cordys R&D BV, All rights reserved.
 * Copyright (C) 2003 Aleksey Sanin <aleksey@aleksey.com>
 */
#include "globals.h"

#ifndef XMLSEC_NO_X509

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

#include <libxml/tree.h>

#include <xmlsec/xmlsec.h>
#include <xmlsec/xmltree.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/mscrypto/crypto.h>
#include <xmlsec/mscrypto/x509.h>
#include "private.h"

/**************************************************************************
 *
 * Internal MSCRYPTO X509 store CTX
 *
 *************************************************************************/
typedef struct _xmlSecMSCryptoX509StoreCtx    xmlSecMSCryptoX509StoreCtx,
                        *xmlSecMSCryptoX509StoreCtxPtr;
struct _xmlSecMSCryptoX509StoreCtx {
    HCERTSTORE trusted;
    HCERTSTORE untrusted;
    int        dont_use_system_trusted_certs;
};

/****************************************************************************
 *
 * xmlSecMSCryptoKeyDataStoreX509Id:
 *
 * xmlSecMSCryptoX509StoreCtx is located after xmlSecTransform
 *
 ***************************************************************************/
#define xmlSecMSCryptoX509StoreGetCtx(store) \
    ((xmlSecMSCryptoX509StoreCtxPtr)(((xmlSecByte*)(store)) + \
                    sizeof(xmlSecKeyDataStoreKlass)))
#define xmlSecMSCryptoX509StoreSize    \
    (sizeof(xmlSecKeyDataStoreKlass) + sizeof(xmlSecMSCryptoX509StoreCtx))

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 xmlChar *ski);


/**
 * xmlSecMSCryptoX509StoreGetKlass:
 *
 * The MSCrypto X509 certificates key data store klass.
 *
 * Returns: pointer to MSCrypto X509 certificates key data store klass.
 */
xmlSecKeyDataStoreId
xmlSecMSCryptoX509StoreGetKlass(void) {
    return(&xmlSecMSCryptoX509StoreKlass);
}

/**
 * xmlSecMSCryptoX509StoreFindCert:
 * @store:          the pointer to X509 key data store klass.
 * @subjectName:    the desired certificate name.
 * @issuerName:     the desired certificate issuer name.
 * @issuerSerial:   the desired certificate issuer serial number.
 * @ski:            the desired certificate SKI.
 * @keyInfoCtx:     the pointer to <dsig:KeyInfo/> element processing context.
 *
 * Searches @store for a certificate that matches given criteria.
 *
 * Returns: 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) {
    xmlSecMSCryptoX509StoreCtxPtr ctx;
    PCCERT_CONTEXT pCert = NULL;

    xmlSecAssert2(xmlSecKeyDataStoreCheckId(store, xmlSecMSCryptoX509StoreId), NULL);
    xmlSecAssert2(keyInfoCtx != NULL, NULL);

    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);
    }

    /* search untrusted certs store */
    if((ctx->trusted != NULL) && (pCert == NULL)) {
        pCert = xmlSecMSCryptoX509FindCert(ctx->trusted, subjectName, issuerName, issuerSerial, ski);
    }

    return pCert;
}


static void
xmlSecMSCryptoUnixTimeToFileTime(time_t t, LPFILETIME pft) {
    /* Note that LONGLONG is a 64-bit value */
    LONGLONG ll;

    xmlSecAssert(pft != NULL);

#if defined( __MINGW32__)
    ll = Int32x32To64(t, 10000000) + 116444736000000000ULL;
#else
    ll = Int32x32To64(t, 10000000) + 116444736000000000;
#endif
    pft->dwLowDateTime = (DWORD)ll;
    pft->dwHighDateTime = ll >> 32;
}

static BOOL
xmlSecMSCrypoVerifyCertTime(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)) {
            xmlSecError(XMLSEC_ERRORS_HERE,
                NULL,
                "CertFindCertificateInCRL",
                XMLSEC_ERRORS_R_CERT_VERIFY_FAILED,
                "cert found in crl list");
            return(FALSE);
        }
    }

    return(TRUE);
}

static void
xmlSecMSCryptoX509StoreCertError(xmlSecKeyDataStorePtr store, PCCERT_CONTEXT cert, DWORD flags) {
    xmlChar * subject = NULL;
    DWORD dwSize;

    xmlSecAssert(xmlSecKeyDataStoreCheckId(store, xmlSecMSCryptoX509StoreId));
    xmlSecAssert(cert != NULL);
    xmlSecAssert(flags != 0);

    /* get certs subject */
    subject = xmlSecMSCryptoX509GetNameString(cert, CERT_NAME_RDN_TYPE, 0, NULL);
    if(subject == NULL) {
        xmlSecError(XMLSEC_ERRORS_HERE,
            "xmlSecMSCryptoX509GetNameString",
            NULL,
            XMLSEC_ERRORS_R_XMLSEC_FAILED,
            XMLSEC_ERRORS_NO_MESSAGE);
        return;
    }

    /* print error */
    if (flags & CERT_STORE_SIGNATURE_FLAG) {
        xmlSecError(XMLSEC_ERRORS_HERE,
                xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)),
                xmlSecErrorsSafeString(subject),
                XMLSEC_ERRORS_R_CERT_VERIFY_FAILED,
                "signature");
    } else if (flags & CERT_STORE_TIME_VALIDITY_FLAG) {
        xmlSecError(XMLSEC_ERRORS_HERE,
                xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)),
                xmlSecErrorsSafeString(subject),
                XMLSEC_ERRORS_R_CERT_HAS_EXPIRED,
                XMLSEC_ERRORS_NO_MESSAGE);
    } else if (flags & CERT_STORE_REVOCATION_FLAG) {
        if (flags & CERT_STORE_NO_CRL_FLAG) {
            xmlSecError(XMLSEC_ERRORS_HERE,
                xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)),
                xmlSecErrorsSafeString(subject),
                XMLSEC_ERRORS_R_CERT_REVOKED,
                "no crl");
        } else {
            xmlSecError(XMLSEC_ERRORS_HERE,
                xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)),
                xmlSecErrorsSafeString(subject),
                XMLSEC_ERRORS_R_CERT_REVOKED,
                XMLSEC_ERRORS_NO_MESSAGE);
        }
    } else {
        xmlSecError(XMLSEC_ERRORS_HERE,
                xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)),
                xmlSecErrorsSafeString(subject),
                XMLSEC_ERRORS_R_CERT_VERIFY_FAILED,
                XMLSEC_ERRORS_NO_MESSAGE);
    }

    xmlFree(subject);
}

/**
 * xmlSecBuildChainUsingWinapi:
 * @cert: the certificate we check
 * @pfTime: pointer to FILETIME that we are interested in
 * @store_untrusted: untrusted certificates added via API
 * @store_doc: untrusted certificates/CRLs extracted from a document
 *
 * Builds certificates chain using Windows API.
 *
 * Returns: 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) {
                xmlSecError(XMLSEC_ERRORS_HERE,
                                        "chain additional collection store",
                                        "CertOpenStore",
                                        XMLSEC_ERRORS_R_CRYPTO_FAILED,
                                        XMLSEC_ERRORS_NO_MESSAGE);
                goto end;
        }
        if (!CertAddStoreToCollection(store_add, store_doc, 0, 0)) {
                xmlSecError(XMLSEC_ERRORS_HERE,
                                        "adding document store",
                                        "CertAddStoreToCollection",
                                        XMLSEC_ERRORS_R_CRYPTO_FAILED,
                                        XMLSEC_ERRORS_NO_MESSAGE);
                goto end;
        }
        if (!CertAddStoreToCollection(store_add, store_untrusted, 0, 0)) {
                xmlSecError(XMLSEC_ERRORS_HERE,
                                        "adding untrusted store",
                                        "CertAddStoreToCollection",
                                        XMLSEC_ERRORS_R_CRYPTO_FAILED,
                                        XMLSEC_ERRORS_NO_MESSAGE);
                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))
    {
        xmlSecError(XMLSEC_ERRORS_HERE,
                    "building certificate chain, checking root",
                    "CertGetCertificateChain",
                    XMLSEC_ERRORS_R_CRYPTO_FAILED,
                    XMLSEC_ERRORS_NO_MESSAGE);
                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))
                {
                        xmlSecError(XMLSEC_ERRORS_HERE,
                                                "building certificate chain, excluding root",
                                                "CertGetCertificateChain",
                                                XMLSEC_ERRORS_R_CRYPTO_FAILED,
            XMLSEC_ERRORS_NO_MESSAGE);
                        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);
}

/**
 * xmlSecMSCryptoBuildCertChainManually:
 * @cert: the certificate we check
 * @pfTime: pointer to FILETIME that we are interested in
 * @store_trusted: trusted certificates added via API
 * @store_untrusted: untrusted certificates added via API
 * @certs: untrusted certificates/CRLs extracted from a document
 * @store: pointer to store klass passed to error functions
 *
 * Builds certificates chain manually.
 *
 * Returns: TRUE on success or FALSE otherwise.
 */
static BOOL
xmlSecMSCryptoBuildCertChainManually (PCCERT_CONTEXT cert, LPFILETIME pfTime,
        HCERTSTORE store_trusted, HCERTSTORE store_untrusted, HCERTSTORE certs,
        xmlSecKeyDataStorePtr store) {
    PCCERT_CONTEXT issuerCert = NULL;
    DWORD flags;

    if (!xmlSecMSCrypoVerifyCertTime(cert, pfTime)) {
        xmlSecMSCryptoX509StoreCertError(store, cert, CERT_STORE_TIME_VALIDITY_FLAG);
        return(FALSE);
    }

    if (!xmlSecMSCryptoCheckRevocation(certs, cert)) {
        return(FALSE);
    }

    /*
     * Try to find the cert in the trusted cert store. We will trust
     * the certificate in the trusted store.
     */
    issuerCert = CertFindCertificateInStore(store_trusted,
                X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
                0,
                CERT_FIND_SUBJECT_NAME,
                &(cert->pCertInfo->Subject),
                NULL);
    if( issuerCert != NULL) {
        /* We have found the trusted cert, so return true */
        /* todo: do we want to verify the trusted cert's revocation? we must, I think */
        CertFreeCertificateContext( issuerCert ) ;
        return( TRUE ) ;
    }

    /* Check whether the certificate is self signed certificate */
    if(CertCompareCertificateName(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &(cert->pCertInfo->Subject), &(cert->pCertInfo->Issuer))) {
        return(FALSE);
    }

    /* try to find issuer cert in the trusted cert in the store */
    issuerCert = CertFindCertificateInStore(store_trusted,
                X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
                0,
                CERT_FIND_SUBJECT_NAME,
                &(cert->pCertInfo->Issuer),
                NULL);
    if(issuerCert != NULL) {
        flags = CERT_STORE_REVOCATION_FLAG | CERT_STORE_SIGNATURE_FLAG;
        if(!CertVerifySubjectCertificateContext(cert, issuerCert, &flags)) {
            xmlSecMSCryptoX509StoreCertError(store, issuerCert, flags);
            CertFreeCertificateContext(issuerCert);
            return(FALSE);
        }
            /* todo: do we want to verify the trusted cert? we must check
                 * revocation, I think */
        CertFreeCertificateContext(issuerCert);
        return(TRUE);
    }

    /* try the untrusted certs in the chain */
    issuerCert = CertFindCertificateInStore(certs,
                X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
                0,
                CERT_FIND_SUBJECT_NAME,
                &(cert->pCertInfo->Issuer),
                NULL);
    if(issuerCert != NULL) {
        flags = CERT_STORE_REVOCATION_FLAG | CERT_STORE_SIGNATURE_FLAG;
        if(!CertVerifySubjectCertificateContext(cert, issuerCert, &flags)) {
            xmlSecMSCryptoX509StoreCertError(store, issuerCert, flags);
            CertFreeCertificateContext(issuerCert);
            return(FALSE);
        }
        if(!xmlSecMSCryptoBuildCertChainManually(issuerCert, pfTime, store_trusted, store_untrusted, certs, store)) {
            xmlSecMSCryptoX509StoreCertError(store, issuerCert, flags);
            CertFreeCertificateContext(issuerCert);
            return(FALSE);
        }
        CertFreeCertificateContext(issuerCert);
        return(TRUE);
    }

    /* try the untrusted certs in the store */
    issuerCert = CertFindCertificateInStore(store_untrusted,
                X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
                0,
                CERT_FIND_SUBJECT_NAME,
                &(cert->pCertInfo->Issuer),
                NULL);
    if(issuerCert != NULL) {
        flags = CERT_STORE_REVOCATION_FLAG | CERT_STORE_SIGNATURE_FLAG;
        if(!CertVerifySubjectCertificateContext(cert, issuerCert, &flags)) {
            xmlSecMSCryptoX509StoreCertError(store, issuerCert, flags);
            CertFreeCertificateContext(issuerCert);
            return(FALSE);
        }
        if(!xmlSecMSCryptoBuildCertChainManually(issuerCert, pfTime, store_trusted, store_untrusted, certs, store)) {
            CertFreeCertificateContext(issuerCert);
            return(FALSE);
        }
        CertFreeCertificateContext(issuerCert);
        return(TRUE);
    }

    return(FALSE);
}

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;
}

/**
 * xmlSecMSCryptoX509StoreVerify:
 * @store:        the pointer to X509 certificate context store klass.
 * @certs:        the untrusted certificates stack.
 * @keyInfoCtx:        the pointer to <dsig:KeyInfo/> element processing context.
 *
 * Verifies @certs list.
 *
 * Returns: pointer to the first verified certificate from @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) && xmlSecMSCryptoX509StoreConstructCertsChain(store, cert, certs, keyInfoCtx)) {
            return(cert);
        }
    }

    return (NULL);
}

/**
 * xmlSecMSCryptoX509StoreAdoptCert:
 * @store:              the pointer to X509 key data store klass.
 * @cert:               the pointer to PCCERT_CONTEXT X509 certificate.
 * @type:               the certificate type (trusted/untrusted).
 *
 * Adds trusted (root) or untrusted certificate to the store.
 *
 * Returns: 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 {
        xmlSecError(XMLSEC_ERRORS_HERE,
                xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)),
                NULL,
                XMLSEC_ERRORS_R_INVALID_TYPE,
                "type=%d", type);
        return(-1);
    }

    /* TODO: The context to be added here is not duplicated first,
    * hopefully this will not lead to errors when closing teh 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)) {
        xmlSecError(XMLSEC_ERRORS_HERE,
                xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)),
                "CertAddCertificateContextToStore",
                XMLSEC_ERRORS_R_CRYPTO_FAILED,
                XMLSEC_ERRORS_NO_MESSAGE);
        return(-1);
    }

    return(0);
}


/**
 * xmlSecMSCryptoX509StoreAdoptKeyStore:
 * @store:              the pointer to X509 key data store klass.
 * @keyStore:           the pointer to keys store.
 *
 * Adds @keyStore to the list of key stores.
 *
 * Returns: 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)) {
        xmlSecError(XMLSEC_ERRORS_HERE,
            xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)),
            "CertAddStoreToCollection",
            XMLSEC_ERRORS_R_CRYPTO_FAILED,
            XMLSEC_ERRORS_NO_MESSAGE);
        return(-1);
    }

    return(0);
}

/**
 * xmlSecMSCryptoX509StoreAdoptTrustedStore:
 * @store:              the pointer to X509 key data store klass.
 * @trustedStore:       the pointer to certs store.
 *
 * Adds @trustedStore to the list of trusted certs stores.
 *
 * Returns: 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 ) ) {
        xmlSecError(XMLSEC_ERRORS_HERE,
            xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)),
            "CertAddStoreToCollection",
            XMLSEC_ERRORS_R_CRYPTO_FAILED,
            XMLSEC_ERRORS_NO_MESSAGE);
        return(-1);
    }

    return(0);
}

/**
 * xmlSecMSCryptoX509StoreAdoptUntrustedStore:
 * @store:              the pointer to X509 key data store klass.
 * @untrustedStore:     the pointer to certs store.
 *
 * Adds @trustedStore to the list of un-trusted certs stores.
 *
 * Returns: 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 ) ) {
        xmlSecError(XMLSEC_ERRORS_HERE,
            xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)),
            "CertAddStoreToCollection",
            XMLSEC_ERRORS_R_CRYPTO_FAILED,
            XMLSEC_ERRORS_NO_MESSAGE);
        return(-1);
    }

    return(0);
}

/**
 * xmlSecMSCryptoX509StoreEnableSystemTrustedCerts:
 * @store:              the pointer to X509 key data store klass.
 * @val:                the enable/disable flag
 *
 * Enables/disables the system trusted certs.
 */
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 behaiviour */
    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) {
        xmlSecError(XMLSEC_ERRORS_HERE,
                xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)),
                "CertOpenStore",
                XMLSEC_ERRORS_R_CRYPTO_FAILED,
                XMLSEC_ERRORS_NO_MESSAGE);
        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) {
        xmlSecError(XMLSEC_ERRORS_HERE,
                xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)),
                "CertOpenStore",
                XMLSEC_ERRORS_R_CRYPTO_FAILED,
                XMLSEC_ERRORS_NO_MESSAGE);
        CertCloseStore(ctx->trusted, CERT_CLOSE_STORE_FORCE_FLAG);
        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 ) ) {
        xmlSecError(XMLSEC_ERRORS_HERE,
                xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)),
                "CertAddStoreToCollection",
                XMLSEC_ERRORS_R_CRYPTO_FAILED,
                XMLSEC_ERRORS_NO_MESSAGE);
        CertCloseStore(ctx->trusted, CERT_CLOSE_STORE_FORCE_FLAG);
        CertCloseStore(hTrustedMemStore, CERT_CLOSE_STORE_CHECK_FLAG);
        ctx->trusted = NULL ;
        return(-1);
    }
    CertCloseStore(hTrustedMemStore, CERT_CLOSE_STORE_CHECK_FLAG);

    /* create untrusted certs store collection */
    ctx->untrusted = CertOpenStore(CERT_STORE_PROV_COLLECTION,
                   0,
                   0,
                   0,
                   NULL);
    if(ctx->untrusted == NULL) {
        xmlSecError(XMLSEC_ERRORS_HERE,
                xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)),
                "CertOpenStore",
                XMLSEC_ERRORS_R_CRYPTO_FAILED,
                XMLSEC_ERRORS_NO_MESSAGE);
        CertCloseStore(ctx->trusted, CERT_CLOSE_STORE_FORCE_FLAG);
        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) {
        xmlSecError(XMLSEC_ERRORS_HERE,
                xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)),
                "CertOpenStore",
                XMLSEC_ERRORS_R_CRYPTO_FAILED,
                XMLSEC_ERRORS_NO_MESSAGE);
        CertCloseStore(ctx->trusted, CERT_CLOSE_STORE_FORCE_FLAG);
        CertCloseStore(ctx->untrusted, CERT_CLOSE_STORE_FORCE_FLAG);
        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 ) ) {
        xmlSecError(XMLSEC_ERRORS_HERE,
                xmlSecErrorsSafeString(xmlSecKeyDataStoreGetName(store)),
                "CertAddStoreToCollection",
                XMLSEC_ERRORS_R_CRYPTO_FAILED,
                XMLSEC_ERRORS_NO_MESSAGE);
        CertCloseStore(ctx->untrusted, CERT_CLOSE_STORE_FORCE_FLAG);
        CertCloseStore(ctx->trusted, CERT_CLOSE_STORE_FORCE_FLAG);
        CertCloseStore(hUntrustedMemStore, CERT_CLOSE_STORE_CHECK_FLAG);
        ctx->trusted = NULL ;
        ctx->untrusted = NULL ;
        return(-1);
    }
    CertCloseStore(hUntrustedMemStore, CERT_CLOSE_STORE_CHECK_FLAG);

    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, CERT_CLOSE_STORE_FORCE_FLAG);
    }
    if (ctx->untrusted) {
        CertCloseStore(ctx->untrusted, CERT_CLOSE_STORE_FORCE_FLAG);
    }

    memset(ctx, 0, sizeof(xmlSecMSCryptoX509StoreCtx));
}


/*****************************************************************************
 *
 * Low-level x509 functions
 *
 *****************************************************************************/
/**
 * xmlSecMSCryptoCertStrToName:
 * @dwCertEncodingType:         the encoding used.
 * @pszX500:                    the string to convert.
 * @dwStrType:                  the string type.
 * @len:                        the result len.
 *
 * Converts input string to name by calling @CertStrToName function.
 *
 * Returns: 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 */
        DWORD dw = GetLastError();
        return(NULL);
    }

    str = (BYTE *)xmlMalloc(sizeof(TCHAR) * ((*len) + 1));
    if(str == NULL) {
        xmlSecError(XMLSEC_ERRORS_HERE,
                    NULL,
                    NULL,
                    XMLSEC_ERRORS_R_MALLOC_FAILED,
                    "len=%ld", (*len));
        return(NULL);
    }
    memset(str, 0, (*len) + 1);

    if (!CertStrToName(dwCertEncodingType, pszX500, dwStrType,
                        NULL, str, len, NULL)) {
        xmlSecError(XMLSEC_ERRORS_HERE,
                        NULL,
                        "CertStrToName",
                        XMLSEC_ERRORS_R_CRYPTO_FAILED,
                        XMLSEC_ERRORS_NO_MESSAGE);
        xmlFree(str);
        return(NULL);
    }

    return(str);
}


/**
 * xmlSecMSCryptoX509FindCertBySubject:
 * @store:              the pointer to certs store
 * @wcSubject:          the cert subject (Unicode)
 * @dwCertEncodingType: the cert encoding type
 *
 * Searches for a cert with given @subject in the @store
 *
 * Returns: 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);
}

/**
 * xmlSecMSCryptoX509FindCertByIssuer:
 * @store:              the pointer to certs store
 * @wcIssuer:           the cert issuer (Unicode)
 * @issuerSerialBn:     the cert issuer serial
 * @dwCertEncodingType: the cert encoding type
 *
 * Searches for a cert with given @subject in the @store
 *
 * Returns: cert handle on success or NULL otherwise
 */
static PCCERT_CONTEXT
xmlSecMSCryptoX509FindCertByIssuer(HCERTSTORE store, const LPTSTR wcIssuer,
                                   xmlSecBnPtr issuerSerialBn, DWORD dwCertEncodingType) {

    PCCERT_CONTEXT res = NULL;
    CERT_INFO certInfo;
    BYTE* bdata;
    DWORD len;


    xmlSecAssert2(store != NULL, NULL);
    xmlSecAssert2(wcIssuer != NULL, NULL);
    xmlSecAssert2(issuerSerialBn != NULL, NULL);

    certInfo.SerialNumber.cbData = xmlSecBnGetSize(issuerSerialBn);
    certInfo.SerialNumber.pbData = xmlSecBnGetData(issuerSerialBn);


    /* 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) {
        xmlSecError(XMLSEC_ERRORS_HERE,
                    NULL,
                    NULL,
                    XMLSEC_ERRORS_R_MALLOC_FAILED,
                    "xmlStrlen(name)=%d",
                    xmlStrlen(name));
        return(NULL);
    }
    while( (p = (xmlChar*)xmlStrstr(name2, BAD_CAST "emailAddress=")) != NULL) {
        memcpy(p, "           E=", 13);
    }

    /* get name */
    res = xmlSecMSCryptoConvertUtf8ToTstr(name2);
    if(res == NULL) {
        xmlSecError(XMLSEC_ERRORS_HERE,
                    NULL,
                    "xmlSecMSCryptoConvertUtf8ToTstr",
                    XMLSEC_ERRORS_R_XMLSEC_FAILED,
                    XMLSEC_ERRORS_NO_MESSAGE);
        return(NULL);
    }

    /* done */
    return(res);
}

static PCCERT_CONTEXT
xmlSecMSCryptoX509FindCert(HCERTSTORE store,
                const xmlChar *subjectName,
                const xmlChar *issuerName,
                const xmlChar *issuerSerial,
                const xmlChar *ski) {
    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) {
            xmlSecError(XMLSEC_ERRORS_HERE,
                        NULL,
                        "xmlSecMSCryptoX509GetCertName",
                        XMLSEC_ERRORS_R_XMLSEC_FAILED,
                        "wcSubjectName");
            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) {
            xmlSecError(XMLSEC_ERRORS_HERE,
                NULL,
                "xmlSecBnInitialize",
                XMLSEC_ERRORS_R_XMLSEC_FAILED,
                XMLSEC_ERRORS_NO_MESSAGE);
            return(NULL);
        }

        ret = xmlSecBnFromDecString(&issuerSerialBn, issuerSerial);
        if(ret < 0) {
            xmlSecError(XMLSEC_ERRORS_HERE,
                NULL,
                "xmlSecBnInitialize",
                XMLSEC_ERRORS_R_XMLSEC_FAILED,
                XMLSEC_ERRORS_NO_MESSAGE);
            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) {
            xmlSecError(XMLSEC_ERRORS_HERE,
                NULL,
                "xmlSecBnReverse",
                XMLSEC_ERRORS_R_XMLSEC_FAILED,
                XMLSEC_ERRORS_NO_MESSAGE);
            xmlSecBnFinalize(&issuerSerialBn);
            return(NULL);
        }

        /* get issuer name */
        wcIssuerName = xmlSecMSCryptoX509GetCertName(issuerName);
        if(wcIssuerName == NULL) {
            xmlSecError(XMLSEC_ERRORS_HERE,
                        NULL,
                        "xmlSecMSCryptoX509GetCertName",
                        XMLSEC_ERRORS_R_XMLSEC_FAILED,
                        "wcIssuerName");
            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)) {
        CRYPT_HASH_BLOB blob;
        xmlChar* binSki;
        int binSkiLen;

        binSki = xmlStrdup(ski);
        if(binSki == NULL) {
            xmlSecError(XMLSEC_ERRORS_HERE,
                NULL,
                "xmlStrdup",
                XMLSEC_ERRORS_R_MALLOC_FAILED,
                XMLSEC_ERRORS_NO_MESSAGE);
            return (NULL);
        }

        /* trick: base64 decode "in place" */
        binSkiLen = xmlSecBase64Decode(binSki, (xmlSecByte*)binSki, xmlStrlen(binSki));
        if(binSkiLen < 0) {
            xmlSecError(XMLSEC_ERRORS_HERE,
                        NULL,
                        "xmlSecBase64Decode",
                        XMLSEC_ERRORS_R_XMLSEC_FAILED,
                        "ski=%s",
                        xmlSecErrorsSafeString(ski));
            xmlFree(binSki);
            return(NULL);
        }

        blob.pbData = binSki;
        blob.cbData = binSkiLen;
        pCert = CertFindCertificateInStore(store,
                        PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
                        0,
                        CERT_FIND_KEY_IDENTIFIER,
                        &blob,
                        NULL);
        xmlFree(binSki);
    }

    return(pCert);
}


/**
 * xmlSecMSCryptoX509GetNameString:
 * @pCertContext:   the pointer to cert
 * @dwType:         the type (see CertGetNameString description in MSDN)
 * @dwFlags:        the flags (see CertGetNameString description in MSDN)
 * @pvTypePara:     the type parameter (see CertGetNameString description in MSDN)
 *
 * Gets the name string for certificate (see CertGetNameString description in MSDN).
 *
 * Returns: 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) {
        xmlSecError(XMLSEC_ERRORS_HERE,
                    "CertGetNameString",
                    NULL,
                    XMLSEC_ERRORS_R_CRYPTO_FAILED,
                    XMLSEC_ERRORS_NO_MESSAGE);
        return (NULL);
    }

    /* allocate buffer */
    name = (LPTSTR)xmlMalloc(sizeof(TCHAR) * (dwSize + 1));
    if(name == NULL) {
        xmlSecError(XMLSEC_ERRORS_HERE,
                    NULL,
                    NULL,
                    XMLSEC_ERRORS_R_MALLOC_FAILED,
                    XMLSEC_ERRORS_NO_MESSAGE);
        return (NULL);
    }

    /* actually get the name */
    dwSize = CertGetNameString(pCertContext, dwType, dwFlags, pvTypePara, name, dwSize);
    if(dwSize <= 0) {
        xmlSecError(XMLSEC_ERRORS_HERE,
                    "CertGetNameString",
                    NULL,
                    XMLSEC_ERRORS_R_CRYPTO_FAILED,
                    XMLSEC_ERRORS_NO_MESSAGE);
        xmlFree(name);
        return (NULL);
    }

    res = xmlSecMSCryptoConvertTstrToUtf8(name);
    if(res == NULL) {
        xmlSecError(XMLSEC_ERRORS_HERE,
                    "xmlSecMSCryptoConvertTstrToUtf8",
                    NULL,
                    XMLSEC_ERRORS_R_XMLSEC_FAILED,
                    XMLSEC_ERRORS_NO_MESSAGE);
        xmlFree(name);
        return (NULL);
    }
    /* done */
    xmlFree(name);
    return (res);
}

#endif /* XMLSEC_NO_X509 */


