/**
 * 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_app
 * @brief Application support functions for MSCng.
 * @details Common functions for the xmlsec1 command-line utility for the Microsoft Cryptography API: Next Generation (CNG).
 */
#include "globals.h"

#include <string.h>

#include <xmlsec/xmlsec.h>
#include <xmlsec/keys.h>
#include <xmlsec/errors.h>
#include <xmlsec/keysmngr.h>
#include <xmlsec/transforms.h>
#include <xmlsec/xmltree.h>
#include <xmlsec/private.h>

#include <xmlsec/mscng/app.h>
#include <xmlsec/mscng/crypto.h>
#include <xmlsec/mscng/certkeys.h>
#include <xmlsec/mscng/keysstore.h>
#include <xmlsec/mscng/symbols.h>
#include <xmlsec/mscng/x509.h>

#include "../cast_helpers.h"
#include "private.h"

/* config info for the mscng keysstore */
static LPTSTR gXmlSecMSCngAppCertStoreName = NULL;

/**
 * @brief Initializes the MSCng crypto engine.
 * @details General crypto engine initialization. This function is used
 * by the XMLSec command-line utility and is called before the
 * #xmlSecInit function.
 *
 * @param config the path to MSCng configuration (unused).
 * @return 0 on success or a negative value otherwise.
 */
int
xmlSecMSCngAppInit(const char* config) {
    /* initialize MSCng crypto engine */

    /* config parameter is an ms cert store name */
    if(config != NULL && strlen(config) > 0) {
        if(gXmlSecMSCngAppCertStoreName != NULL) {
            /* deny double initialization */
            xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_CONFIG, NULL,
                "config=%s, config already set",
                xmlSecErrorsSafeString(config));
            return(-1);
        }

        gXmlSecMSCngAppCertStoreName = xmlSecWin32ConvertUtf8ToTstr((const xmlChar*)config);
        if(gXmlSecMSCngAppCertStoreName == NULL) {
            xmlSecInternalError2("xmlSecWin32ConvertUtf8ToTstr", NULL,
                "config=%s", xmlSecErrorsSafeString(config));
            return(-1);
        }
    }

    return(0);
}

/**
 * @brief Shuts down the MSCng crypto engine.
 * @details General crypto engine shutdown. This function is used
 * by the XMLSec command-line utility and is called after the
 * #xmlSecShutdown function.
 *
 * @return 0 on success or a negative value otherwise.
 */
int
xmlSecMSCngAppShutdown(void) {
    /* shutdown MSCng crypto engine */
    if(gXmlSecMSCngAppCertStoreName != NULL) {
        xmlFree(gXmlSecMSCngAppCertStoreName);
        gXmlSecMSCngAppCertStoreName = NULL;
    }
    return(0);
}

/**
 * @brief Gets the MSCng certs store name.
 * @details Gets the MS Cng certs store name set by #xmlSecMSCngAppInit function.
 *
 * @return the MS Cng certs name used by xmlsec-mscng.
 */
LPCTSTR
xmlSecMSCngAppGetCertStoreName(void) {
    return(gXmlSecMSCngAppCertStoreName);
}

/**
 * @brief Reads a key from a file.
 * @param filename the key filename.
 * @param type the expected key type.
 * @param format the key file format.
 * @param pwd the key file password.
 * @param pwdCallback the key password callback.
 * @param pwdCallbackCtx the user context for password callback.
 * @return pointer to the key or NULL if an error occurs.
 */


xmlSecKeyPtr
xmlSecMSCngAppKeyLoadEx(const char *filename, xmlSecKeyDataType type XMLSEC_ATTRIBUTE_UNUSED, xmlSecKeyDataFormat format,
    const char *pwd, void* pwdCallback, void* pwdCallbackCtx
) {
    xmlSecBuffer buffer;
    xmlSecKeyPtr key = NULL;
    int ret;

    xmlSecAssert2(filename != NULL, NULL);
    xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, NULL);
    UNREFERENCED_PARAMETER(type);

    switch(format) {
    case xmlSecKeyDataFormatPkcs12:
        key = xmlSecMSCngAppPkcs12Load(filename, pwd, pwdCallback,
            pwdCallbackCtx);
        if(key == NULL) {
            xmlSecInternalError("xmlSecMSCngAppPkcs12Load", NULL);
            return(NULL);
        }
        break;
    case xmlSecKeyDataFormatCertDer:
        ret = xmlSecBufferInitialize(&buffer, 0);
        if(ret < 0) {
            xmlSecInternalError("xmlSecBufferInitialize", NULL);
            return(NULL);
        }

        ret = xmlSecBufferReadFile(&buffer, filename);
        if(ret < 0) {
            xmlSecInternalError2("xmlSecBufferReadFile", NULL,
                "filename=%s", xmlSecErrorsSafeString(filename));
            xmlSecBufferFinalize(&buffer);
            return (NULL);
        }

        key = xmlSecMSCngAppKeyLoadMemory(xmlSecBufferGetData(&buffer),
                                        xmlSecBufferGetSize(&buffer), format,
                                        pwd, pwdCallback, pwdCallbackCtx);
        if(key == NULL) {
            xmlSecInternalError("xmlSecMSCngAppKeyLoadMemory", NULL);
            xmlSecBufferFinalize(&buffer);
            return(NULL);
        }
        xmlSecBufferFinalize(&buffer);
        break;
    case xmlSecKeyDataFormatDer: {
        xmlSecKeyDataPtr keyData = NULL;
        xmlSecSize bufSize;
        DWORD dwDataSize = 0;

        ret = xmlSecBufferInitialize(&buffer, 0);
        if(ret < 0) {
            xmlSecInternalError("xmlSecBufferInitialize(DER)", NULL);
            return(NULL);
        }
        ret = xmlSecBufferReadFile(&buffer, filename);
        if(ret < 0) {
            xmlSecInternalError2("xmlSecBufferReadFile(DER)", NULL,
                "filename=%s", xmlSecErrorsSafeString(filename));
            xmlSecBufferFinalize(&buffer);
            return(NULL);
        }
        bufSize = xmlSecBufferGetSize(&buffer);
        XMLSEC_SAFE_CAST_SIZE_TO_ULONG(bufSize, dwDataSize, {xmlSecBufferFinalize(&buffer); return(NULL);}, NULL);

        /* try to read private key first and if no luck, try public key */
        keyData = xmlSecMSCngAppKeyReadPrivKeyFromDer(xmlSecBufferGetData(&buffer), dwDataSize);
        if(keyData == NULL) {
            keyData = xmlSecMSCngAppKeyReadPubKeyFromDer(xmlSecBufferGetData(&buffer), dwDataSize);
        }
        if(keyData == NULL) {
            xmlSecInternalError("xmlSecMSCngAppKeyReadPrivKeyFromDer and xmlSecMSCngAppKeyReadPubKeyFromDer", NULL);
            xmlSecBufferFinalize(&buffer);
            return(NULL);
        }
        xmlSecBufferFinalize(&buffer);
        key = xmlSecKeyCreate();
        if(key == NULL) {
            xmlSecInternalError("xmlSecKeyCreate", NULL);
            xmlSecKeyDataDestroy(keyData);
            return(NULL);
        }
        ret = xmlSecKeySetValue(key, keyData);
        if(ret < 0) {
            xmlSecInternalError("xmlSecKeySetValue", NULL);
            xmlSecKeyDataDestroy(keyData);
            xmlSecKeyDestroy(key);
            return(NULL);
        }
        break;
    }
    default:
        xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_FORMAT, NULL,
            "format=" XMLSEC_ENUM_FMT, XMLSEC_ENUM_CAST(format));
        return(NULL);
        break;
    }

    return(key);
}

/**
 * @brief Reads a key from the binary @p data.
 * @param data the key binary data.
 * @param dataSize the key binary data size.
 * @param format the key data format.
 * @param pwd the key data password.
 * @param pwdCallback the key password callback.
 * @param pwdCallbackCtx the user context for password callback.
 * @return pointer to the key or NULL if an error occurs.
 */
xmlSecKeyPtr
xmlSecMSCngAppKeyLoadMemory(const xmlSecByte* data, xmlSecSize dataSize, xmlSecKeyDataFormat format,
                            const char *pwd, void* pwdCallback, void* pwdCallbackCtx) {
    PCCERT_CONTEXT pCert = NULL;
    PCCERT_CONTEXT pKeyCert = NULL;
    xmlSecKeyDataPtr x509Data = NULL;
    xmlSecKeyDataPtr keyData = NULL;
    xmlSecKeyPtr key = NULL;
    xmlSecKeyPtr res = NULL;
    DWORD dwDataSize;
    int ret;

    xmlSecAssert2(data != NULL, NULL);
    xmlSecAssert2(dataSize > 0, NULL);
    xmlSecAssert2(format == xmlSecKeyDataFormatCertDer, NULL);
    UNREFERENCED_PARAMETER(pwd);
    UNREFERENCED_PARAMETER(pwdCallback);
    UNREFERENCED_PARAMETER(pwdCallbackCtx);

    /* read cert and make a copy for keyCert */
    XMLSEC_SAFE_CAST_SIZE_TO_ULONG(dataSize, dwDataSize, goto done, NULL);
    pCert = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, data, dwDataSize);
    if(pCert == NULL) {
        xmlSecMSCngLastError("CertCreateCertificateContext", NULL);
        goto done;
    }
    pKeyCert = CertDuplicateCertificateContext(pCert);
    if(pKeyCert == NULL) {
        xmlSecMSCngLastError("CertDuplicateCertificateContext", NULL);
        goto done;
    }

    /* create key */
    key = xmlSecKeyCreate();
    if(key == NULL) {
        xmlSecInternalError("xmlSecKeyCreate",
            xmlSecKeyDataGetName(x509Data));
        goto done;
    }

    keyData = xmlSecMSCngCertAdopt(pCert, xmlSecKeyDataTypePublic);
    if(keyData == NULL) {
        xmlSecInternalError("xmlSecMSCngCertAdopt", NULL);
        goto done;
    }
    pCert = NULL;  /* owned by keyData now */

    ret = xmlSecKeySetValue(key, keyData);
    if(ret < 0) {
        xmlSecInternalError("xmlSecKeySetValue", NULL);
        goto done;
    }
    keyData = NULL;

    /* add keyCert to x509 data and add it to the key */
    x509Data = xmlSecKeyDataCreate(xmlSecMSCngKeyDataX509Id);
    if(x509Data == NULL) {
        xmlSecInternalError("xmlSecKeyDataCreate", NULL);
        goto done;
    }

    ret = xmlSecMSCngKeyDataX509AdoptKeyCert(x509Data, pKeyCert);
    if(ret < 0) {
        xmlSecInternalError("xmlSecMSCngKeyDataX509AdoptKeyCert", NULL);
        goto done;
    }
    pKeyCert = NULL; /* owned by x509Data data now */

    ret = xmlSecKeyAdoptData(key, x509Data);
    if(ret < 0) {
        xmlSecInternalError("xmlSecKeyAdoptData", NULL);
        goto done;
    }
    x509Data = NULL;

    /* success */
    res = key;
    key = NULL;

done:
    if(pCert != NULL) {
        CertFreeCertificateContext(pCert);
    }
    if(pKeyCert != NULL) {
        CertFreeCertificateContext(pKeyCert);
    }
    if(x509Data != NULL) {
        xmlSecKeyDataDestroy(x509Data);
    }
    if(keyData != NULL) {
        xmlSecKeyDataDestroy(keyData);
    }
    if(key != NULL) {
        xmlSecKeyDestroy(key);
    }

    return(res);
}


#ifndef XMLSEC_NO_X509
/**
 * @brief Reads the certificate from a file and adds to key.
 * @details Reads the certificate from @p filename and adds it to key.
 *
 * @param key the pointer to key.
 * @param filename the certificate filename.
 * @param format the certificate file format.
 * @return 0 on success or a negative value otherwise.
 */
int
xmlSecMSCngAppKeyCertLoad(xmlSecKeyPtr key, const char* filename,
                          xmlSecKeyDataFormat format) {
    xmlSecAssert2(key != NULL, -1);
    xmlSecAssert2(filename != NULL, -1);
    xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, -1);

    xmlSecNotImplementedError("MSCNG doesn't support loading X509 certificates at runtime");
    return(-1);
}

/**
 * @brief Reads the certificate from memory and adds to key.
 * @details Reads the certificate from memory buffer and adds it to key.
 *
 * @param key the pointer to key.
 * @param data the certificate binary data.
 * @param dataSize the certificate binary data size.
 * @param format the certificate file format.
 * @return 0 on success or a negative value otherwise.
 */
int
xmlSecMSCngAppKeyCertLoadMemory(xmlSecKeyPtr key, const xmlSecByte* data, xmlSecSize dataSize,
                                xmlSecKeyDataFormat format) {
    xmlSecAssert2(key != NULL, -1);
    xmlSecAssert2(data != NULL, -1);
    xmlSecAssert2(dataSize > 0, -1);
    xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, -1);

    xmlSecNotImplementedError("MSCNG doesn't support loading X509 certificates at runtime");
    return(-1);
}

/**
 * @brief Reads key and certificates from PKCS12 file.
 * @details Reads a key and all associated certificates from the PKCS12 file.
 * For uniformity, call #xmlSecMSCngAppKeyLoadEx instead of this function. Pass
 * in format=xmlSecKeyDataFormatPkcs12.
 *
 * @param filename the PKCS12 key filename.
 * @param pwd the PKCS12 file password.
 * @param pwdCallback the password callback.
 * @param pwdCallbackCtx the user context for password callback.
 * @return pointer to the key or NULL if an error occurs.
 */
xmlSecKeyPtr
xmlSecMSCngAppPkcs12Load(const char *filename,
    const char *pwd, void* pwdCallback, void* pwdCallbackCtx
) {
    xmlSecBuffer buffer;
    xmlSecByte* data;
    xmlSecKeyPtr key;
    int ret;

    xmlSecAssert2(filename != NULL, NULL);
    xmlSecAssert2(pwd != NULL, NULL);

    ret = xmlSecBufferInitialize(&buffer, 0);
    if(ret < 0) {
        xmlSecInternalError("xmlSecBufferInitialize", NULL);
        return(NULL);
    }

    ret = xmlSecBufferReadFile(&buffer, filename);
    if(ret < 0) {
        xmlSecInternalError2("xmlSecBufferReadFile", NULL, "filename=%s",
            xmlSecErrorsSafeString(filename));
        return(NULL);
    }

    data = xmlSecBufferGetData(&buffer);
    if(data == NULL) {
        xmlSecInternalError("xmlSecBufferGetData", NULL);
        xmlSecBufferFinalize(&buffer);
        return(NULL);
    }

    key = xmlSecMSCngAppPkcs12LoadMemory(data, xmlSecBufferGetSize(&buffer),
        pwd, pwdCallback, pwdCallbackCtx);
    if(key == NULL) {
        xmlSecInternalError("xmlSecMSCngAppPkcs12LoadMemory", NULL);
        xmlSecBufferFinalize(&buffer);
        return(NULL);
    }

    xmlSecBufferFinalize(&buffer);
    return(key);
}

static BOOL
xmlSecMSCngIsPrivateKeyCert(PCCERT_CONTEXT cert, BOOL isPersistentKey) {
    xmlSecAssert2(cert != NULL, FALSE);

    if (isPersistentKey) {
        DWORD dwData = 0;
        DWORD dwDataLen = sizeof(dwData);
        return(CertGetCertificateContextProperty(cert, CERT_KEY_SPEC_PROP_ID, &dwData, &dwDataLen));
    } else {
        CERT_KEY_CONTEXT ckc;
        DWORD dwDataLen = sizeof(ckc);
        return CertGetCertificateContextProperty(cert, CERT_KEY_CONTEXT_PROP_ID, &ckc, &dwDataLen);
    }
}

/**
 * @brief Reads key and certs from PKCS12 binary data.
 * @details Reads a key and all associated certificates from the PKCS12 binary data.
 * For uniformity, call #xmlSecMSCngAppKeyLoadEx instead of this function. Pass
 * in format=xmlSecKeyDataFormatPkcs12.
 *
 * @param data the key binary data.
 * @param dataSize the key binary data size.
 * @param pwd the PKCS12 password.
 * @param pwdCallback the password callback.
 * @param pwdCallbackCtx the user context for password callback.
 * @return pointer to the key or NULL if an error occurs.
 */
xmlSecKeyPtr
xmlSecMSCngAppPkcs12LoadMemory(const xmlSecByte* data, xmlSecSize dataSize, const char *pwd,
                               void *pwdCallback,
                               void* pwdCallbackCtx) {
    UNREFERENCED_PARAMETER(pwdCallback);
    UNREFERENCED_PARAMETER(pwdCallbackCtx);
    CRYPT_DATA_BLOB pfx;
    xmlSecKeyPtr key = NULL;
    WCHAR* pwdWideChar = NULL;
    HCERTSTORE certStore = NULL;
    xmlSecKeyDataPtr keyData = NULL;
    xmlSecKeyDataPtr privKeyData = NULL;
    PCCERT_CONTEXT cert = NULL;
    PCCERT_CONTEXT certDuplicate = NULL;
    xmlChar* keyName = NULL;
    int ret;

    xmlSecAssert2(data != NULL, NULL);
    xmlSecAssert2(dataSize > 1, NULL);
    xmlSecAssert2(pwd != NULL, NULL);

    memset(&pfx, 0, sizeof(pfx));
    pfx.pbData = (BYTE *)data;
    XMLSEC_SAFE_CAST_SIZE_TO_ULONG(dataSize, pfx.cbData, return(NULL), NULL);

    ret = PFXIsPFXBlob(&pfx);
    if(ret == FALSE) {
        xmlSecMSCngLastError("PFXIsPFXBlob", NULL);
        return(NULL);
    }

    pwdWideChar = xmlSecWin32ConvertLocaleToUnicode(pwd);
    if(pwdWideChar == NULL) {
        xmlSecInternalError("xmlSecWin32ConvertLocaleToUnicode", NULL);
        goto cleanup;
    }

    ret = PFXVerifyPassword(&pfx, pwdWideChar, 0);
    if(ret == FALSE) {
        xmlSecMSCngLastError("PFXVerifyPassword", NULL);
        goto cleanup;
    }

    DWORD flags = CRYPT_EXPORTABLE | PKCS12_ALWAYS_CNG_KSP;
    if (!xmlSecImportGetPersistKey()) {
        flags |= PKCS12_NO_PERSIST_KEY;
    }
    certStore = PFXImportCertStore(&pfx, pwdWideChar, flags);
    if(certStore == NULL) {
        xmlSecMSCngLastError("PFXImportCertStore", NULL);
        goto cleanup;
    }

    keyData = xmlSecKeyDataCreate(xmlSecMSCngKeyDataX509Id);
    if(keyData == NULL) {
        xmlSecInternalError("xmlSecKeyDataCreate", NULL);
        goto cleanup;
    }

    /* enumerate over certifiates in the store */
    while((cert = CertEnumCertificatesInStore(certStore, cert)) != NULL) {
        /* multiple private keys, use the first one */
        if ((privKeyData == NULL) && (xmlSecMSCngIsPrivateKeyCert(cert, xmlSecImportGetPersistKey()) == TRUE)) {
            /* get key name */
            if (keyName == NULL) {
                keyName = xmlSecMSCngX509GetFriendlyNameUtf8(cert);
            }

            /* adopt private key */
            certDuplicate = CertDuplicateCertificateContext(cert);
            if (certDuplicate == NULL) {
                xmlSecMSCngLastError("CertDuplicateCertificateContext", NULL);
                goto cleanup;
            }

            privKeyData = xmlSecMSCngCertAdopt(certDuplicate, xmlSecKeyDataTypePrivate | xmlSecKeyDataTypePublic);
            if (privKeyData == NULL) {
                xmlSecInternalError("xmlSecMSCngCertAdopt", NULL);
                goto cleanup;
            }
            certDuplicate = NULL;

            /* adopt key certificate */
            certDuplicate = CertDuplicateCertificateContext(cert);
            if (certDuplicate == NULL) {
                xmlSecMSCngLastError("CertDuplicateCertificateContext", NULL);
                goto cleanup;
            }

            ret = xmlSecMSCngKeyDataX509AdoptKeyCert(keyData, certDuplicate);
            if (ret < 0) {
                xmlSecInternalError("xmlSecMSCngKeyDataX509AdoptKeyCert", NULL);
                goto cleanup;
            }
            certDuplicate = NULL;
        } else {
            /* adopt certificate */
            certDuplicate = CertDuplicateCertificateContext(cert);
            if (certDuplicate == NULL) {
                xmlSecMSCngLastError("CertDuplicateCertificateContext", NULL);
                goto cleanup;
            }

            ret = xmlSecMSCngKeyDataX509AdoptCert(keyData, certDuplicate);
            if (ret < 0) {
                xmlSecInternalError("xmlSecMSCngKeyDataX509AdoptKeyCert", NULL);
                goto cleanup;
            }
            certDuplicate = NULL;
        }
    }

    /* at this point we should have a private key */
    if(privKeyData == NULL) {
        xmlSecInternalError("privKeyData is NULL", NULL);
        goto cleanup;
    }

    key = xmlSecKeyCreate();
    if(key == NULL) {
        xmlSecInternalError("xmlSecKeyCreate", NULL);
        goto cleanup;
    }

    ret = xmlSecKeySetValue(key, privKeyData);
    if(ret < 0) {
        xmlSecInternalError("xmlSecKeySetValue", NULL);
        xmlSecKeyDestroy(key);
        key = NULL;
        goto cleanup;
    }
    privKeyData = NULL;

    ret = xmlSecKeyAdoptData(key, keyData);
    if(ret < 0) {
        xmlSecInternalError("xmlSecKeyAdoptData", NULL);
        xmlSecKeyDestroy(key);
        key = NULL;
        goto cleanup;
    }
    keyData = NULL;

    if (keyName != NULL) {
        ret = xmlSecKeySetName(key, keyName);
        if (ret < 0) {
            xmlSecInternalError("xmlSecKeySetName", NULL);
            xmlSecKeyDestroy(key);
            goto cleanup;
        }
    }

cleanup:
    if(certStore != NULL) {
        CertCloseStore(certStore, 0);
    }
    if(pwdWideChar != NULL) {
        xmlFree(pwdWideChar);
    }
    if (keyName != NULL) {
        xmlFree(keyName);
    }
    if(keyData != NULL) {
        xmlSecKeyDataDestroy(keyData);
    }
    if(privKeyData != NULL) {
        xmlSecKeyDataDestroy(privKeyData);
    }
    if(cert != NULL) {
        CertFreeCertificateContext(cert);
    }
    if(certDuplicate != NULL) {
        CertFreeCertificateContext(certDuplicate);
    }
    return(key);
}

/**
 * @brief Reads a cert from a file and adds to the key store.
 * @details Reads cert from @p filename and adds to the list of trusted or known
 * untrusted certs in @p store.
 *
 * @param mngr the keys manager.
 * @param filename the certificate file.
 * @param format the certificate file format.
 * @param type the flag that indicates is the certificate in @p filename
 *                      trusted or not.
 * @return 0 on success or a negative value otherwise.
 */
int
xmlSecMSCngAppKeysMngrCertLoad(xmlSecKeysMngrPtr mngr, const char *filename,
                               xmlSecKeyDataFormat format,
                               xmlSecKeyDataType type) {
    xmlSecBuffer buffer;
    int ret;

    xmlSecAssert2(mngr != NULL, -1);
    xmlSecAssert2(filename != NULL, -1);
    xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, -1);

    ret = xmlSecBufferInitialize(&buffer, 0);
    if(ret < 0) {
        xmlSecInternalError("xmlSecBufferInitialize", NULL);
        return(-1);
    }

    ret = xmlSecBufferReadFile(&buffer, filename);
    if(ret < 0) {
        xmlSecInternalError2("xmlSecBufferReadFile", NULL,
                             "filename=%s", xmlSecErrorsSafeString(filename));
        xmlSecBufferFinalize(&buffer);
        return(-1);
    }

    ret = xmlSecMSCngAppKeysMngrCertLoadMemory(mngr, xmlSecBufferGetData(&buffer),
        xmlSecBufferGetSize(&buffer), format, type);
    if(ret < 0) {
        xmlSecInternalError2("xmlSecMSCngAppKeysMngrCertLoadMemory", NULL,
                             "filename=%s", xmlSecErrorsSafeString(filename));
        xmlSecBufferFinalize(&buffer);
        return(-1);
    }

    xmlSecBufferFinalize(&buffer);
    return(ret);
}

/**
 * @brief Reads cert from buffer and adds to the key store.
 * @details Reads cert from @p data and adds to the list of trusted or known
 * untrusted certs in @p store.
 *
 * @param mngr the pointer to keys manager.
 * @param data the certificate data.
 * @param dataSize the certificate data size.
 * @param format the certificate format (PEM or DER).
 * @param type the certificate type (trusted/untrusted).
 * @return 0 on success or a negative value otherwise.
 */
int
xmlSecMSCngAppKeysMngrCertLoadMemory(xmlSecKeysMngrPtr mngr, const xmlSecByte* data,
    xmlSecSize dataSize, xmlSecKeyDataFormat format, xmlSecKeyDataType type
) {
    xmlSecKeyDataStorePtr x509Store;
    PCCERT_CONTEXT pCert = NULL;
    DWORD dwDataSize;
    int ret;

    xmlSecAssert2(mngr != NULL, -1);
    xmlSecAssert2(data != NULL, -1);
    xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, -1);

    x509Store = xmlSecKeysMngrGetDataStore(mngr, xmlSecMSCngX509StoreId);
    if(x509Store == NULL) {
        xmlSecInternalError("xmlSecKeysMngrGetDataStore(xmlSecMSCngX509StoreId)", NULL);
        return(-1);
    }

    XMLSEC_SAFE_CAST_SIZE_TO_ULONG(dataSize,dwDataSize, return(-1), NULL);

    switch (format) {
        case xmlSecKeyDataFormatDer:
            pCert = CertCreateCertificateContext(
                X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
                data,
                dwDataSize);
            if(pCert == NULL) {
                xmlSecMSCngLastError("CertCreateCertificateContext", NULL)
                return(-1);
            }
            break;
        default:
            xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_FORMAT, NULL,
                "format=" XMLSEC_ENUM_FMT, XMLSEC_ENUM_CAST(format));
            return(-1);
    }

    xmlSecAssert2(pCert != NULL, -1);
    ret = xmlSecMSCngX509StoreAdoptCert(x509Store, pCert, type);
    if(ret < 0) {
        xmlSecInternalError("xmlSecMSCngX509StoreAdoptCert", NULL);
        CertFreeCertificateContext(pCert);
        return(-1);
    }

    return(0);
}

/**
 * @brief Reads CRLs from a file and adds to the store.
 * @details Reads crls from @p filename and adds to the list of crls in @p store.
 *
 * @param mngr the keys manager.
 * @param filename the CRL file.
 * @param format the CRL file format.
 * @return 0 on success or a negative value otherwise.
 */
int
xmlSecMSCngAppKeysMngrCrlLoad(xmlSecKeysMngrPtr mngr, const char *filename, xmlSecKeyDataFormat format) {
    xmlSecBuffer buffer;
    int ret;

    xmlSecAssert2(mngr != NULL, -1);
    xmlSecAssert2(filename != NULL, -1);
    xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, -1);

    ret = xmlSecBufferInitialize(&buffer, 0);
    if(ret < 0) {
        xmlSecInternalError("xmlSecBufferInitialize", NULL);
        return(-1);
    }

    ret = xmlSecBufferReadFile(&buffer, filename);
    if(ret < 0) {
        xmlSecInternalError2("xmlSecBufferReadFile", NULL,
                             "filename=%s", xmlSecErrorsSafeString(filename));
        xmlSecBufferFinalize(&buffer);
        return(-1);
    }

    ret = xmlSecMSCngAppKeysMngrCrlLoadMemory(mngr, xmlSecBufferGetData(&buffer),
        xmlSecBufferGetSize(&buffer), format);
    if(ret < 0) {
        xmlSecInternalError2("xmlSecMSCngAppKeysMngrCrlLoadMemory", NULL,
                             "filename=%s", xmlSecErrorsSafeString(filename));
        xmlSecBufferFinalize(&buffer);
        return(-1);
    }

    xmlSecBufferFinalize(&buffer);
    return(ret);
}

/* Reads a CRL from raw input data for supported formats. */
static PCCRL_CONTEXT
xmlSecMSCngReadCrlFromBuffer(const xmlSecByte* data, xmlSecSize dataSize,
    xmlSecKeyDataFormat format
) {
    PCCRL_CONTEXT pCrl = NULL;

    xmlSecAssert2(data != NULL, NULL);
    xmlSecAssert2(dataSize > 0, NULL);
    xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, NULL);

    switch(format) {
        case xmlSecKeyDataFormatDer:
            pCrl = xmlSecMSCngX509CrlDerRead(data, dataSize);
            if(pCrl == NULL) {
                xmlSecInternalError("xmlSecMSCngX509CrlDerRead", NULL);
                return(NULL);
            }
            break;
        case xmlSecKeyDataFormatPem:
            xmlSecOtherError(XMLSEC_ERRORS_R_INVALID_FORMAT, NULL,
                "PEM format is not supported for CRL loading in MSCng");
            return(NULL);
        default:
            xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_FORMAT, NULL,
                "format=" XMLSEC_ENUM_FMT, XMLSEC_ENUM_CAST(format));
            return(NULL);
    }

    return(pCrl);
}

/**
 * @brief Loads and verifies a CRL from a file.
 * @details Atomically loads and verifies a CRL from @p filename.
 *
 * @param mngr the keys manager.
 * @param filename the CRL filename.
 * @param format the CRL format (PEM or DER).
 * @param keyInfoCtx the key info context for verification parameters.
 * @return 0 on success or a negative value otherwise.
 */
int
xmlSecMSCngAppKeysMngrCrlLoadAndVerify(xmlSecKeysMngrPtr mngr, const char *filename,
    xmlSecKeyDataFormat format, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    xmlSecKeyDataStorePtr x509Store;
    xmlSecBuffer buffer;
    PCCRL_CONTEXT pCrl = NULL;
    int ret;
    int res = -1;

    xmlSecAssert2(mngr != NULL, -1);
    xmlSecAssert2(filename != NULL, -1);
    xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);

    x509Store = xmlSecKeysMngrGetDataStore(mngr, xmlSecMSCngX509StoreId);
    if(x509Store == NULL) {
        xmlSecInternalError("xmlSecKeysMngrGetDataStore(xmlSecMSCngX509StoreId)", NULL);
        return(-1);
    }

    ret = xmlSecBufferInitialize(&buffer, 0);
    if(ret < 0) {
        xmlSecInternalError("xmlSecBufferInitialize", NULL);
        return(-1);
    }

    ret = xmlSecBufferReadFile(&buffer, filename);
    if(ret < 0) {
        xmlSecInternalError2("xmlSecBufferReadFile", NULL, "filename=%s", xmlSecErrorsSafeString(filename));
        goto done;
    }

    pCrl = xmlSecMSCngReadCrlFromBuffer(xmlSecBufferGetData(&buffer), xmlSecBufferGetSize(&buffer), format);
    if(pCrl == NULL) {
        xmlSecInternalError2("xmlSecMSCngReadCrlFromBuffer", NULL, "filename=%s", xmlSecErrorsSafeString(filename));
        goto done;
    }

    ret = xmlSecMSCngX509StoreVerifyCrl(x509Store, pCrl, keyInfoCtx);
    if(ret < 0) {
        xmlSecInternalError2("xmlSecMSCngX509StoreVerifyCrl", NULL, "filename=%s", xmlSecErrorsSafeString(filename));
        goto done;
    } else if(ret != 1) {
        xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_DATA, NULL, "filename=%s", xmlSecErrorsSafeString(filename));
        goto done;
    }

    ret = xmlSecMSCngX509StoreAdoptCrl(x509Store, pCrl);
    if(ret < 0) {
        xmlSecInternalError2("xmlSecMSCngX509StoreAdoptCrl", NULL, "filename=%s", xmlSecErrorsSafeString(filename));
        goto done;
    }
    pCrl = NULL; /* owned by the store now */

    res = 0;
done:
    if(pCrl != NULL) {
        CertFreeCRLContext(pCrl);
    }
    xmlSecBufferFinalize(&buffer);
    return(res);
}

/**
 * @brief Reads CRLs from memory and adds to the store.
 * @details Reads crls from @p data and adds to the list of crls in @p store.
 *
 * @param mngr the pointer to keys manager.
 * @param data the CRL data.
 * @param dataSize the CRL data size.
 * @param format the CRL format (PEM or DER).
 * @return 0 on success or a negative value otherwise.
 */
int
xmlSecMSCngAppKeysMngrCrlLoadMemory(xmlSecKeysMngrPtr mngr, const xmlSecByte* data, xmlSecSize dataSize, xmlSecKeyDataFormat format) {
    xmlSecKeyDataStorePtr x509Store;
    PCCRL_CONTEXT pCrl = NULL;
    int ret;

    xmlSecAssert2(mngr != NULL, -1);
    xmlSecAssert2(data != NULL, -1);
    xmlSecAssert2(dataSize > 0, -1);
    xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, -1);

    x509Store = xmlSecKeysMngrGetDataStore(mngr, xmlSecMSCngX509StoreId);
    if(x509Store == NULL) {
        xmlSecInternalError("xmlSecKeysMngrGetDataStore(xmlSecMSCngX509StoreId)", NULL);
        return(-1);
    }

    pCrl = xmlSecMSCngReadCrlFromBuffer(data, dataSize, format);
    if(pCrl == NULL) {
        xmlSecInternalError("xmlSecMSCngReadCrlFromBuffer", NULL);
        return(-1);
    }

    xmlSecAssert2(pCrl != NULL, -1);
    ret = xmlSecMSCngX509StoreAdoptCrl(x509Store, pCrl);
    if(ret < 0) {
        xmlSecInternalError("xmlSecMSCngX509StoreAdoptCrl", NULL);
        CertFreeCRLContext(pCrl);
        return(-1);
    }

    return(0);
}


#endif /* XMLSEC_NO_X509 */

/**
 * @brief Initializes the default key manager for MSCng.
 * @details Initializes @p mngr with simple keys store #xmlSecSimpleKeysStoreId
 * and a default MSCng crypto key data stores.
 *
 * @param mngr the pointer to keys manager.
 * @return 0 on success or a negative value otherwise.
 */
int
xmlSecMSCngAppDefaultKeysMngrInit(xmlSecKeysMngrPtr mngr) {
    int ret;

    xmlSecAssert2(mngr != NULL, -1);

    /* create MSCng keys store if needed */
    if(xmlSecKeysMngrGetKeysStore(mngr) == NULL) {
        xmlSecKeyStorePtr keysStore;

        keysStore = xmlSecKeyStoreCreate(xmlSecMSCngKeysStoreId);
        if(keysStore == NULL) {
            xmlSecInternalError("xmlSecKeyStoreCreate(xmlSecMSCngKeysStoreId)", NULL);
            return(-1);
        }

        ret = xmlSecKeysMngrAdoptKeysStore(mngr, keysStore);
        if(ret < 0) {
            xmlSecInternalError("xmlSecKeysMngrAdoptKeysStore", NULL);
            xmlSecKeyStoreDestroy(keysStore);
            return(-1);
        }
    }

    ret = xmlSecMSCngKeysMngrInit(mngr);
    if(ret < 0) {
        xmlSecInternalError("xmlSecMSCngKeysMngrInit", NULL);
        return(-1);
    }

    mngr->getKey = xmlSecKeysMngrGetKey;
    return(0);
}

/**
 * @brief Adds @p key to the keys manager.
 * @details Adds @p key to the keys manager @p mngr created with #xmlSecMSCngAppDefaultKeysMngrInit
 * function.
 *
 * @param mngr the pointer to keys manager.
 * @param key the pointer to key.
 * @return 0 on success or a negative value otherwise.
 */
int
xmlSecMSCngAppDefaultKeysMngrAdoptKey(xmlSecKeysMngrPtr mngr, xmlSecKeyPtr key) {
    xmlSecKeyStorePtr store;
    int ret;

    xmlSecAssert2(mngr != NULL, -1);
    xmlSecAssert2(key != NULL, -1);

    store = xmlSecKeysMngrGetKeysStore(mngr);
    if(store == NULL) {
        xmlSecInternalError("xmlSecKeysMngrGetKeysStore", NULL);
        return(-1);
    }

    ret = xmlSecMSCngKeysStoreAdoptKey(store, key);
    if(ret < 0) {
        xmlSecInternalError("xmlSecMSCngKeysStoreAdoptKey", NULL);
        return(-1);
    }

    return(0);
}

/**
 * @brief Verifies @p key using the keys manager.
 * @details Verifies @p key with the keys manager @p mngr created with #xmlSecCryptoAppDefaultKeysMngrInit
 * 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.
 *
 * @param mngr the pointer to keys manager.
 * @param key the pointer to key.
 * @param keyInfoCtx the key info context for verification.
 * @return 1 if key is verified, 0 otherwise, or a negative value if an error occurs.
 */
int
xmlSecMSCngAppDefaultKeysMngrVerifyKey(xmlSecKeysMngrPtr mngr, xmlSecKeyPtr key, xmlSecKeyInfoCtxPtr keyInfoCtx) {
#ifndef XMLSEC_NO_X509
    xmlSecKeyDataStorePtr x509Store;

    xmlSecAssert2(mngr != NULL, -1);
    xmlSecAssert2(key != NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);

    x509Store = xmlSecKeysMngrGetDataStore(mngr, xmlSecMSCngX509StoreId);
    if (x509Store == NULL) {
        xmlSecInternalError("xmlSecKeysMngrGetDataStore(xmlSecMSCngX509StoreId)", NULL);
        return(-1);
    }

    return(xmlSecMSCngX509StoreVerifyKey(x509Store, key, keyInfoCtx));

#else  /* XMLSEC_NO_X509 */

    xmlSecAssert2(mngr != NULL, -1);
    xmlSecAssert2(key != NULL, -1);
    xmlSecAssert2(keyInfoCtx != NULL, -1);

    xmlSecNotImplementedError("X509 support is disabled during compilation");
    return(-1);

#endif /* XMLSEC_NO_X509 */
}

/**
 * @brief Loads the XML keys file into the keys manager.
 * @details Loads XML keys file from @p uri to the keys manager @p mngr created
 * with #xmlSecMSCngAppDefaultKeysMngrInit function.
 *
 * @param mngr the pointer to keys manager.
 * @param uri the uri.
 * @return 0 on success or a negative value otherwise.
 */
int
xmlSecMSCngAppDefaultKeysMngrLoad(xmlSecKeysMngrPtr mngr, const char* uri) {
    xmlSecKeyStorePtr store;
    int ret;

    xmlSecAssert2(mngr != NULL, -1);
    xmlSecAssert2(uri != NULL, -1);

    store = xmlSecKeysMngrGetKeysStore(mngr);
    if(store == NULL) {
        xmlSecInternalError("xmlSecKeysMngrGetKeysStore", NULL);
        return(-1);
    }

    ret = xmlSecMSCngKeysStoreLoad(store, uri, mngr);
    if(ret < 0) {
        xmlSecInternalError2("xmlSecMSCngKeysStoreLoad", NULL, "uri=%s",
            xmlSecErrorsSafeString(uri));
        return(-1);
    }

    return(0);
}

/**
 * @brief Saves keys from @p mngr to XML keys file.
 * @param mngr the pointer to keys manager.
 * @param filename the destination filename.
 * @param type the type of keys to save (public/private/symmetric).
 * @return 0 on success or a negative value otherwise.
 */
int
xmlSecMSCngAppDefaultKeysMngrSave(xmlSecKeysMngrPtr mngr, const char* filename, xmlSecKeyDataType type) {
    xmlSecKeyStorePtr store;
    int ret;

    xmlSecAssert2(mngr != NULL, -1);
    xmlSecAssert2(filename != NULL, -1);

    store = xmlSecKeysMngrGetKeysStore(mngr);
    if(store == NULL) {
        xmlSecInternalError("xmlSecKeysMngrGetKeysStore", NULL);
        return(-1);
    }

    ret = xmlSecMSCngKeysStoreSave(store, filename, type);
    if(ret < 0) {
        xmlSecInternalError2("xmlSecMSCngKeysStoreSave", NULL, "filename=%s",
            xmlSecErrorsSafeString(filename));
        return(-1);
    }

    return(0);
}

/**
 * @brief Gets default password callback.
 *
 * @return default password callback.
 */
void*
xmlSecMSCngAppGetDefaultPwdCallback(void) {
    /* TODO: MSCNG doesn't support password callback */
    return(NULL);
}
