/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/*
 * XSEC
 *
 * templatesign := tool to sign a template XML signature file
 *
 * Author(s): Berin Lautenbach
 *
 * $Id: templatesign.cpp 1894293 2021-10-15 14:14:50Z scantor $
 *
 */

//XSEC includes
// XSEC

#include <xsec/utils/XSECPlatformUtils.hpp>
#include <xsec/framework/XSECProvider.hpp>
#include <xsec/canon/XSECC14n20010315.hpp>
#include <xsec/dsig/DSIGSignature.hpp>
#include <xsec/dsig/DSIGKeyInfoX509.hpp>
#include <xsec/framework/XSECException.hpp>
#include <xsec/framework/XSECURIResolver.hpp>
#include <xsec/enc/XSECCryptoException.hpp>

#if defined (XSEC_HAVE_OPENSSL)
#   include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>
#   include <xsec/enc/OpenSSL/OpenSSLCryptoKeyEC.hpp>
#   include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>
#   include <xsec/enc/OpenSSL/OpenSSLCryptoKeyHMAC.hpp>
#   include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
#   include <xsec/enc/OpenSSL/OpenSSLSupport.hpp>

#   include <openssl/bio.h>
#   include <openssl/dsa.h>
#   include <openssl/err.h>
#   include <openssl/evp.h>
#   include <openssl/pem.h>

#endif

#if defined(XSEC_HAVE_WINCAPI)
#   include <xsec/enc/WinCAPI/WinCAPICryptoProvider.hpp>
#   include <xsec/enc/WinCAPI/WinCAPICryptoKeyDSA.hpp>
#   include <xsec/enc/WinCAPI/WinCAPICryptoKeyRSA.hpp>
#   include <xsec/enc/WinCAPI/WinCAPICryptoKeyHMAC.hpp>
#endif

#include "../../utils/XSECDOMUtils.hpp"

#include <memory.h>
#include <string.h>
#include <iostream>
#include <stdlib.h>

#if defined(HAVE_UNISTD_H)
# include <unistd.h>
#else
# if defined(HAVE_DIRECT_H)
#  include <direct.h>
# endif
#endif

#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/XMLString.hpp>

#include <xercesc/dom/DOM.hpp>
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/util/XMLException.hpp>
#include <xercesc/util/XMLUniDefs.hpp>
#include <xercesc/util/XMLNetAccessor.hpp>
#include <xercesc/util/XMLUri.hpp>

#ifdef XSEC_HAVE_XALAN

// XALAN

#include <xalanc/XPath/XPathEvaluator.hpp>
#include <xalanc/XalanTransformer/XalanTransformer.hpp>

// If this isn't defined, we're on Xalan 1.12+ and require modern C++
#ifndef XALAN_USING_XALAN
# define XALAN_USING_XALAN(NAME) using xalanc :: NAME;
#endif

XALAN_USING_XALAN(XPathEvaluator)
XALAN_USING_XALAN(XalanTransformer)

#endif

using std::ostream;
using std::cout;
using std::cerr;
using std::endl;
using std::flush;

// Uplift entire program into Xerces namespace

XERCES_CPP_NAMESPACE_USE

// --------------------------------------------------------------------------------
//           Much code taken from the DOMPrint Xerces example
// --------------------------------------------------------------------------------

static XMLFormatter*            gFormatter             = 0;
static XMLCh*                   gEncodingName          = 0;
static XMLFormatter::UnRepFlags gUnRepFlags            = XMLFormatter::UnRep_CharRef;




static const XMLCh  gEndElement[] = { chOpenAngle, chForwardSlash, chNull };
static const XMLCh  gEndPI[] = { chQuestion, chCloseAngle, chNull};
static const XMLCh  gStartPI[] = { chOpenAngle, chQuestion, chNull };

static const XMLCh  gStartCDATA[] =
{
        chOpenAngle, chBang, chOpenSquare, chLatin_C, chLatin_D,
        chLatin_A, chLatin_T, chLatin_A, chOpenSquare, chNull
};

static const XMLCh  gEndCDATA[] =
{
    chCloseSquare, chCloseSquare, chCloseAngle, chNull
};
static const XMLCh  gStartComment[] =
{
    chOpenAngle, chBang, chDash, chDash, chNull
};

static const XMLCh  gEndComment[] =
{
    chDash, chDash, chCloseAngle, chNull
};

static const XMLCh  gStartDoctype[] =
{
    chOpenAngle, chBang, chLatin_D, chLatin_O, chLatin_C, chLatin_T,
    chLatin_Y, chLatin_P, chLatin_E, chSpace, chNull
};
static const XMLCh  gPublic[] =
{
    chLatin_P, chLatin_U, chLatin_B, chLatin_L, chLatin_I,
    chLatin_C, chSpace, chDoubleQuote, chNull
};
static const XMLCh  gSystem[] =
{
    chLatin_S, chLatin_Y, chLatin_S, chLatin_T, chLatin_E,
    chLatin_M, chSpace, chDoubleQuote, chNull
};
static const XMLCh  gStartEntity[] =
{
    chOpenAngle, chBang, chLatin_E, chLatin_N, chLatin_T, chLatin_I,
    chLatin_T, chLatin_Y, chSpace, chNull
};
static const XMLCh  gNotation[] =
{
    chLatin_N, chLatin_D, chLatin_A, chLatin_T, chLatin_A,
    chSpace, chDoubleQuote, chNull
};



// ---------------------------------------------------------------------------
//  Local classes
// ---------------------------------------------------------------------------

class DOMPrintFormatTarget : public XMLFormatTarget
{
public:
    DOMPrintFormatTarget()  {};
    ~DOMPrintFormatTarget() {};

    // -----------------------------------------------------------------------
    //  Implementations of the format target interface
    // -----------------------------------------------------------------------

    void writeChars(const   XMLByte* const  toWrite,
                    const   XMLSize_t    count,
                    XMLFormatter * const formatter)
    {
        // Surprisingly, Solaris was the only platform on which
        // required the char* cast to print out the string correctly.
        // Without the cast, it was printing the pointer value in hex.
        // Quite annoying, considering every other platform printed
        // the string with the explicit cast to char* below.
        cout.write((char *) toWrite, (int) count);
    };

private:
    // -----------------------------------------------------------------------
    //  Unimplemented methods.
    // -----------------------------------------------------------------------
    DOMPrintFormatTarget(const DOMPrintFormatTarget& other);
    void operator=(const DOMPrintFormatTarget& rhs);
};


// ---------------------------------------------------------------------------
//  ostream << DOMNode
//
//  Stream out a DOM node, and, recursively, all of its children. This
//  function is the heart of writing a DOM tree out as XML source. Give it
//  a document node and it will do the whole thing.
// ---------------------------------------------------------------------------
ostream& operator<<(ostream& target, DOMNode* toWrite)
{
    // Get the name and value out for convenience
    const XMLCh*   nodeName = toWrite->getNodeName();
    const XMLCh*   nodeValue = toWrite->getNodeValue();
    XMLSize_t lent = XMLString::stringLen(nodeValue);

    switch (toWrite->getNodeType())
    {
        case DOMNode::TEXT_NODE:
        {
            gFormatter->formatBuf(nodeValue,
                                  lent, XMLFormatter::CharEscapes);
            break;
        }


        case DOMNode::PROCESSING_INSTRUCTION_NODE :
        {
            *gFormatter << XMLFormatter::NoEscapes << gStartPI  << nodeName;
            if (lent > 0)
            {
                *gFormatter << chSpace << nodeValue;
            }
            *gFormatter << XMLFormatter::NoEscapes << gEndPI;
            break;
        }


        case DOMNode::DOCUMENT_NODE :
        {

            DOMNode *child = toWrite->getFirstChild();
            while( child != 0)
            {
                target << child;
                // add linefeed in requested output encoding
                *gFormatter << chLF;
                target << flush;
                child = child->getNextSibling();
            }
            break;
        }


        case DOMNode::ELEMENT_NODE :
        {
            // The name has to be representable without any escapes
            *gFormatter  << XMLFormatter::NoEscapes
                         << chOpenAngle << nodeName;

            // Output the element start tag.

            // Output any attributes on this element
            DOMNamedNodeMap *attributes = toWrite->getAttributes();
            XMLSize_t attrCount = attributes->getLength();
            for (XMLSize_t i = 0; i < attrCount; i++)
            {
                DOMNode  *attribute = attributes->item(i);

                //
                //  Again the name has to be completely representable. But the
                //  attribute can have refs and requires the attribute style
                //  escaping.
                //
                *gFormatter  << XMLFormatter::NoEscapes
                             << chSpace << attribute->getNodeName()
                             << chEqual << chDoubleQuote
                             << XMLFormatter::AttrEscapes
                             << attribute->getNodeValue()
                             << XMLFormatter::NoEscapes
                             << chDoubleQuote;
            }

            //
            //  Test for the presence of children, which includes both
            //  text content and nested elements.
            //
            DOMNode *child = toWrite->getFirstChild();
            if (child != 0)
            {
                // There are children. Close start-tag, and output children.
                // No escapes are legal here
                *gFormatter << XMLFormatter::NoEscapes << chCloseAngle;

                while( child != 0)
                {
                    target << child;
                    child = child->getNextSibling();
                }

                //
                // Done with children.  Output the end tag.
                //
                *gFormatter << XMLFormatter::NoEscapes << gEndElement
                            << nodeName << chCloseAngle;
            }
            else
            {
                //
                //  There were no children. Output the short form close of
                //  the element start tag, making it an empty-element tag.
                //
                *gFormatter << XMLFormatter::NoEscapes << chForwardSlash << chCloseAngle;
            }
            break;
        }


        case DOMNode::ENTITY_REFERENCE_NODE:
            {
                //DOMNode *child;
#if 0
                for (child = toWrite.getFirstChild();
                child != 0;
                child = child.getNextSibling())
                {
                    target << child;
                }
#else
                //
                // Instead of printing the refernece tree
                // we'd output the actual text as it appeared in the xml file.
                // This would be the case when -e option was chosen
                //
                    *gFormatter << XMLFormatter::NoEscapes << chAmpersand
                        << nodeName << chSemiColon;
#endif
                break;
            }


        case DOMNode::CDATA_SECTION_NODE:
            {
            *gFormatter << XMLFormatter::NoEscapes << gStartCDATA
                        << nodeValue << gEndCDATA;
            break;
        }


        case DOMNode::COMMENT_NODE:
        {
            *gFormatter << XMLFormatter::NoEscapes << gStartComment
                        << nodeValue << gEndComment;
            break;
        }


        case DOMNode::DOCUMENT_TYPE_NODE:
        {
            DOMDocumentType *doctype = (DOMDocumentType *)toWrite;;

            *gFormatter << XMLFormatter::NoEscapes  << gStartDoctype
                        << nodeName;

            const XMLCh* id = doctype->getPublicId();
            if (id != 0)
            {
                *gFormatter << XMLFormatter::NoEscapes << chSpace << gPublic
                    << id << chDoubleQuote;
                id = doctype->getSystemId();
                if (id != 0)
                {
                    *gFormatter << XMLFormatter::NoEscapes << chSpace
                       << chDoubleQuote << id << chDoubleQuote;
                }
            }
            else
            {
                id = doctype->getSystemId();
                if (id != 0)
                {
                    *gFormatter << XMLFormatter::NoEscapes << chSpace << gSystem
                        << id << chDoubleQuote;
                }
            }

            id = doctype->getInternalSubset();
            if (id !=0)
                *gFormatter << XMLFormatter::NoEscapes << chOpenSquare
                            << id << chCloseSquare;

            *gFormatter << XMLFormatter::NoEscapes << chCloseAngle;
            break;
        }


        case DOMNode::ENTITY_NODE:
        {
            *gFormatter << XMLFormatter::NoEscapes << gStartEntity
                        << nodeName;

            const XMLCh * id = ((DOMEntity *)toWrite)->getPublicId();
            if (id != 0)
                *gFormatter << XMLFormatter::NoEscapes << gPublic
                            << id << chDoubleQuote;

            id = ((DOMEntity *)toWrite)->getSystemId();
            if (id != 0)
                *gFormatter << XMLFormatter::NoEscapes << gSystem
                            << id << chDoubleQuote;

            id = ((DOMEntity *)toWrite)->getNotationName();
            if (id != 0)
                *gFormatter << XMLFormatter::NoEscapes << gNotation
                            << id << chDoubleQuote;

            *gFormatter << XMLFormatter::NoEscapes << chCloseAngle << chLF;

            break;
        }

        default:
            cerr << "Unrecognized node type = "
                 << (long)toWrite->getNodeType() << endl;
    }
    return target;
}


// --------------------------------------------------------------------------------
//           End of outputter
// --------------------------------------------------------------------------------

class DOMMemFormatTarget : public XMLFormatTarget
{
public:
    
    unsigned char * buffer;     // Buffer to write to

    DOMMemFormatTarget() : buffer(NULL)  {};
    ~DOMMemFormatTarget() {};

    void setBuffer (unsigned char * toSet) {buffer = toSet;};


    // -----------------------------------------------------------------------
    //  Implementations of the format target interface
    // -----------------------------------------------------------------------

    void writeChars(const   XMLByte* const  toWrite,
                    const   XMLSize_t    count,
                    XMLFormatter * const formatter)
    {
        // Surprisingly, Solaris was the only platform on which
        // required the char* cast to print out the string correctly.
        // Without the cast, it was printing the pointer value in hex.
        // Quite annoying, considering every other platform printed
        // the string with the explicit cast to char* below.
        memcpy(buffer, (char *) toWrite, (int) count);
        buffer[count] = '\0';
    };

private:
    // -----------------------------------------------------------------------
    //  Unimplemented methods.
    // -----------------------------------------------------------------------
    DOMMemFormatTarget(const DOMMemFormatTarget& other);
    void operator=(const DOMMemFormatTarget& rhs);

    
};

// ---------------------------------------------------------------------------
//  ostream << DOMString
//
//  Stream out a DOM string. Doing this requires that we first transcode
//  to char * form in the default code page for the system
// ---------------------------------------------------------------------------


DOMPrintFormatTarget *DOMtarget;
DOMMemFormatTarget *MEMtarget;
XMLFormatter *formatter, *MEMformatter;
unsigned char *charBuffer;

void printUsage(void) {

    cerr << "\nUsage: templatesign <key options> <file to sign>\n\n";
#if defined (XSEC_HAVE_OPENSSL)
    cerr << "    Where <key options> are one of :\n\n";
    cerr << "        --x509subjectname/-s <distinguished name>\n";
    cerr << "                     <distinguished name> will be set as SubjectName in x509\n";
    cerr << "        --dsakey/-d  <dsa private key file> <password>\n";
    cerr << "                     <dsa private key file> contains a PEM encoded private key\n";
    cerr << "                     <password> is the password used to decrypt the key file\n";
#   if defined (XSEC_HAVE_WINCAPI)
    cerr << "                     NOTE: Not usable if --wincapi previously set\n";
#   endif
#   if defined(XSEC_OPENSSL_HAVE_EC)
    cerr << "        --eckey/-e  <ec private key file> <password>\n";
    cerr << "                     <ec private key file> contains a PEM encoded private key\n";
    cerr << "                     <password> is the password used to decrypt the key file\n";
#   endif
#   if defined (XSEC_HAVE_WINCAPI)
    cerr << "                     NOTE: Not usable if --wincapi previously set\n";
#   endif
    cerr << "        --rsakey/-r <rsa private key file> <password>\n";
    cerr << "                     <rsa privatekey file> contains a PEM encoded private key\n";
    cerr << "                     <password> is the password used to decrypt the key file\n";
    cerr << "        --x509cert/-x <filename>\n";
    cerr << "                      <filename> contains a PEM certificate to be added as a KeyInfo\n";
#endif
    cerr << "        --hmackey/-h <string>\n";
    cerr << "                     <string> is the hmac key to set\n";
    cerr << "        --clearkeys/-c\n";
    cerr << "                      Clears out any current KeyInfo elements in the file\n";
#if defined(XSEC_HAVE_WINCAPI)
    cerr << "        --windss/-wd\n";
    cerr << "                      Use the default user AT_SIGNATURE key from default\n";
    cerr << "                      Windows DSS CSP\n";
    cerr << "        --winrsa/-wr\n";
    cerr << "                      Use the default user AT_SIGNATURE key from default\n";
    cerr << "                      Windows RSA CSP\n";
    cerr << "        --winhmac/-wh <string>\n";
    cerr << "                      Create a windows HMAC key using <string> as the password.\n";
    cerr << "                      Uses a SHA-1 hash of the password to derive a key\n";
#if defined (CRYPT_ACQUIRE_CACHE_FLAG)
    cerr << "        --wincer/-wc <Subject Name>\n";
    cerr << "                      Use the private key associated with the named certificate in the Windows certificate store\n";
#endif /* CRYPT_ACQUIRE_CACHE_FLAG */
    cerr << "        --windsskeyinfo/-wdi\n";
    cerr << "                      Clear KeyInfo elements and insert DSS parameters from windows key\n";
    cerr << "        --winrsakeyinfo/-wri\n";
    cerr << "                      Clear KeyInfo elements and insert RSA parameters from windows key\n";
#endif


}

int main(int argc, char **argv) {

    XSECCryptoKey               * key = NULL;
    DSIGKeyInfoX509             * keyInfoX509 = NULL;
    const char                  * x509SubjectName = NULL;
#if defined (XSEC_HAVE_OPENSSL)
    OpenSSLCryptoX509           * certs[128];
#endif
    int                         certCount = 0;
    int                         paramCount;
    bool                        clearKeyInfo = false;
#if defined(XSEC_HAVE_WINCAPI)
    HCRYPTPROV                  win32DSSCSP = 0;        // Crypto Provider
    HCRYPTPROV                  win32RSACSP = 0;        // Crypto Provider
    bool                        winDssKeyInfo = false;
    bool                        winRsaKeyInfo = false;
    WinCAPICryptoKeyDSA         * winKeyDSA = NULL;
    WinCAPICryptoKeyRSA         * winKeyRSA = NULL;
#endif

    // Initialise the XML system

    try {

        XMLPlatformUtils::Initialize();
#ifdef XSEC_HAVE_XALAN
        XPathEvaluator::initialize();
        XalanTransformer::initialize();
#endif
        XSECPlatformUtils::Initialise();

    }

    catch (const XMLException &e) {

        cerr << "Error during initialisation of Xerces" << endl;
        cerr << "Error Message = : "
             << e.getMessage() << endl;

    }

#if defined (XSEC_HAVE_OPENSSL)
    
    // Initialize OpenSSL
    ERR_load_crypto_strings();
    BIO * bio_err;
    
    if ((bio_err=BIO_new(BIO_s_file())) != NULL)
        BIO_set_fp(bio_err,stderr,BIO_NOCLOSE|BIO_FP_TEXT);

#endif

    if (argc < 2) {

        printUsage();
        exit (1);
    }
    
    paramCount = 1;

    while (paramCount < argc - 1) {

        // Run through all parameters

        if (_stricmp(argv[paramCount], "--x509subjectname") == 0 || _stricmp(argv[paramCount], "-s") == 0) {

            if (paramCount +2 >= argc) {

                printUsage();
                exit(1);
            }

            // Get the subject name

            x509SubjectName = argv[paramCount + 1];
            paramCount += 2;
        }

#if defined (XSEC_HAVE_OPENSSL)

        else if (_stricmp(argv[paramCount], "--dsakey") == 0 || _stricmp(argv[paramCount], "-d") == 0
            || _stricmp(argv[paramCount], "--rsakey") == 0 || _stricmp(argv[paramCount], "-r") == 0
#   if defined(XSEC_OPENSSL_HAVE_EC)
            || _stricmp(argv[paramCount], "--eckey") == 0 || _stricmp(argv[paramCount], "-e") == 0
#   endif
            ) {

            // OpenSSL Key

            if (paramCount + 3 >= argc) {

                printUsage();
                exit (1);

            }

            if (key != 0) {

                cerr << "\nError loading private key - another key already loaded\n\n";
                printUsage();
                exit(1);

            }

            // Load the signing key
            // For now just read a particular file

            BIO * bioKey;
            if ((bioKey = BIO_new(BIO_s_file())) == NULL) {

                cerr << "Error opening private key file\n\n";
                exit (1);

            }

            if (BIO_read_filename(bioKey, argv[paramCount + 1]) <= 0) {

                cerr << "Error opening private key file\n\n";
                exit (1);

            }

            EVP_PKEY * pkey;
            pkey = PEM_read_bio_PrivateKey(bioKey,NULL,NULL,argv[paramCount + 2]);

            if (pkey == NULL) {

                cerr << "Error loading private key\n\n";
                ERR_print_errors(bio_err);
                exit (1);

            }

            if (_stricmp(argv[paramCount], "--dsakey") == 0 || _stricmp(argv[paramCount], "-d") == 0) {

                // Check type is correct

                if (EVP_PKEY_id(pkey) != EVP_PKEY_DSA) {
                    cerr << "DSA Key requested, but OpenSSL loaded something else\n";
                    exit (1);
                }

                // Create the XSEC OpenSSL interface
                key = new OpenSSLCryptoKeyDSA(pkey);
            }
#if defined(XSEC_OPENSSL_HAVE_EC)
            else if (_stricmp(argv[paramCount], "--eckey") == 0 || _stricmp(argv[paramCount], "-e") == 0) {

                // Check type is correct

                if (EVP_PKEY_id(pkey) != EVP_PKEY_EC) {
                    cerr << "EC Key requested, but OpenSSL loaded something else\n";
                    exit (1);
                }

                // Create the XSEC OpenSSL interface
                key = new OpenSSLCryptoKeyEC(pkey);
            }
#endif
            else {
                if (EVP_PKEY_id(pkey) != EVP_PKEY_RSA) {
                    cerr << "RSA Key requested, but OpenSSL loaded something else\n";
                    exit (1);
                }
                key = new OpenSSLCryptoKeyRSA(pkey);
            }

            EVP_PKEY_free(pkey);
            BIO_free(bioKey);

            paramCount += 3;
            
        } /* argv[1] = "dsa/rsa/ec" */


        else if (_stricmp(argv[paramCount], "--x509cert") == 0 || _stricmp(argv[paramCount], "-x") == 0) {

            // X509Data keyInfo

            if (paramCount + 2 >= argc) {

                printUsage();
                exit (1);

            }

            // Load the cert.
            // For now just read a particular file

            BIO * bioX509;

            if ((bioX509 = BIO_new(BIO_s_file())) == NULL) {

                cerr << "Error opening file\n\n";
                exit (1);

            }

            if (BIO_read_filename(bioX509, argv[paramCount + 1]) <= 0) {

                cerr << "Error opening X509 Certificate " << argv[paramCount + 1] << "\n\n";
                exit (1);

            }

            X509 * x
                ;
            x = PEM_read_bio_X509_AUX(bioX509,NULL,NULL,NULL);

            if (x == NULL) {

                cerr << "Error loading certificate\n\n";
                ERR_print_errors(bio_err);
                exit (1);

            }

            // Create the XSEC OpenSSL interface - used only to translate to Base64

            certs[certCount++] = new OpenSSLCryptoX509(x);
            X509_free(x);
            BIO_free(bioX509);

            paramCount += 2;
            
        } /* argv[1] = "--x509cert" */
        
        else 
#endif
        if (_stricmp(argv[paramCount], "--hmackey") == 0 || _stricmp(argv[paramCount], "-h") == 0) {
            XSECCryptoKeyHMAC* hmacKey = XSECPlatformUtils::g_cryptoProvider->keyHMAC();
            hmacKey->setKey((unsigned char *) argv[paramCount + 1], (unsigned int) strlen(argv[paramCount + 1]));
            key = hmacKey;
            paramCount += 2;

        }

        else if (_stricmp(argv[paramCount], "--clearkeys") == 0 || _stricmp(argv[paramCount], "-c") == 0) {

            clearKeyInfo = true;
            paramCount += 1;

        }

#if defined (XSEC_HAVE_WINCAPI)
        else if (_stricmp(argv[paramCount], "--windss") == 0 || _stricmp(argv[paramCount], "-wd") == 0) {

            WinCAPICryptoProvider * cp;
            // First set windows as the crypto provider
            cp = new WinCAPICryptoProvider();
            XSECPlatformUtils::SetCryptoProvider(cp);
            
            // Now set the key
            if (!CryptAcquireContext(&win32DSSCSP,
                NULL,
                NULL,
                PROV_DSS,
                0)) {

                cerr << "Error acquiring Crypto context - Attempting to generate new key pair" << endl;
                
                // Attempt to gen a new keyset
                if (!CryptAcquireContext(&win32DSSCSP,
                    NULL,
                    NULL,
                    PROV_DSS,
                    CRYPT_NEWKEYSET)) {
                        cerr << "Error acquiring DSS Crypto Service Provider with new keyset" << endl;
                        return 2;
                }
                else {
                    HCRYPTKEY k;
                    if (!CryptGenKey(win32DSSCSP, AT_SIGNATURE, CRYPT_EXPORTABLE, &k)) {
                        cerr << "Error generating DSS keyset" << endl;
                        return 2;
                    }
                    CryptDestroyKey(k);
                }
            }
            
            winKeyDSA = new WinCAPICryptoKeyDSA(win32DSSCSP, AT_SIGNATURE, true);
            key = winKeyDSA;
            paramCount++;
        }

        else if (_stricmp(argv[paramCount], "--winrsa") == 0 || _stricmp(argv[paramCount], "-wr") == 0) {
            WinCAPICryptoProvider * cp;
            cp = new WinCAPICryptoProvider();
            XSECPlatformUtils::SetCryptoProvider(cp);

            if (!CryptAcquireContext(&win32RSACSP,
                NULL,
                NULL,
                PROV_RSA_FULL,
                0)) {

                cerr << "Error acquiring Crypto context - Attempting to generate new RSA key pair" << endl;
                
                // Attempt to gen a new keyset
                if (!CryptAcquireContext(&win32RSACSP,
                    NULL,
                    NULL,
                    PROV_RSA_FULL,
                    CRYPT_NEWKEYSET)) {
                        cerr << "Error acquiring RSA Crypto Service Provider with new keyset" << endl;
                        return 2;
                }
                else {
                    HCRYPTKEY k;
                    if (!CryptGenKey(win32RSACSP, AT_SIGNATURE, CRYPT_EXPORTABLE, &k)) {
                        cerr << "Error generating RSA keyset" << endl;
                        return 2;
                    }
                    CryptDestroyKey(k);
                }
            }

            winKeyRSA = new WinCAPICryptoKeyRSA(win32RSACSP, AT_SIGNATURE, true);
            key = winKeyRSA;
            paramCount++;
        }

        else if (_stricmp(argv[paramCount], "--winhmac") == 0 || _stricmp(argv[paramCount], "-wh") == 0) {

            WinCAPICryptoProvider * cp;
            // Obtain default PROV_RSA, with default user key container
            // Note we open in VERIFYCONTEXT as we do not require a assymetric key pair
            if (!CryptAcquireContext(&win32RSACSP,
                NULL,
                NULL,
                PROV_RSA_FULL,
                CRYPT_VERIFYCONTEXT)) {
                    cerr << "Error acquiring RSA Crypto Service Provider" << endl;
                    return 2;
            }
            cp = new WinCAPICryptoProvider();
            XSECPlatformUtils::SetCryptoProvider(cp);

            paramCount++;
            HCRYPTKEY k;
            HCRYPTHASH h;
            BOOL fResult = CryptCreateHash(
                win32RSACSP,
                CALG_SHA,
                0,
                0,
                &h);

            if (fResult == 0) {
                cerr << "Error creating hash to create windows hmac key from password" << endl;
                return 2;
            }
            fResult = CryptHashData(
                h,
                (unsigned char *) argv[paramCount],
                (DWORD) strlen(argv[paramCount]),
                0);
            
            if (fResult == 0) {
                cerr << "Error hashing password to create windows hmac key" << endl;
                return 2;
            }

            // Now create a key
            fResult = CryptDeriveKey(
                win32RSACSP,
                CALG_RC2,
                h,
                CRYPT_EXPORTABLE,
                &k);

            if (fResult == 0) {
                cerr << "Error deriving key from hash value" << endl;
                return 2;
            }

            // Wrap in a WinCAPI object
            WinCAPICryptoKeyHMAC * hk;
            hk = new WinCAPICryptoKeyHMAC(win32RSACSP);
            hk->setWinKey(k); 

            key = hk;

            CryptDestroyHash(h);
            paramCount++;

        }

        else if (_stricmp(argv[paramCount], "--windsskeyinfo") == 0 || _stricmp(argv[paramCount], "-wdi") == 0) {
            winDssKeyInfo = true;
            paramCount++;
        }

        else if (_stricmp(argv[paramCount], "--winrsakeyinfo") == 0 || _stricmp(argv[paramCount], "-wri") == 0) {
            winRsaKeyInfo = true;
            paramCount++;
        }

        // Need to find a better way to check this
        // If CryptAcquireCertificatePrivateKey is not defined in the included
        // version of wincapi.h, CRYPT_ACQUIRE_CACHE_FLAG will not be set

#if defined (CRYPT_ACQUIRE_CACHE_FLAG)
        
        // Code provided by Milan Tomic

        //Please note that this example below use CryptAcquireCertificatePrivateKey() function
        //which is not declared in wincrypt.h that ships with VC++ 6. If you would like to run
        //this example you'll need to replace your old wincrypt.h and crypt32.lib with new versions.
        //This example below is compatible with Windows 98/IE 5 and above OS/IE versions.

        else if (_stricmp(argv[paramCount], "--wincer") == 0 || _stricmp(argv[paramCount], "-wc") == 0) {
            WinCAPICryptoProvider * cp;
            PCCERT_CONTEXT          pSignerCert = NULL;
            DWORD                   dwKeySpec;
            HCERTSTORE              hStoreHandle;
            #define MY_ENCODING_TYPE  (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)

            // Obtain default PROV_DSS with default user key container
            if (!CryptAcquireContext(&win32DSSCSP,
                NULL,
                NULL,
                PROV_DSS,
                0)) {
                    cerr << "Error acquiring DSS Crypto Service Provider" << endl;
                    return 2;
            }//*/

            // Open 'Personal' certificate store 
            if (!(hStoreHandle = CertOpenStore(CERT_STORE_PROV_SYSTEM,
                0,
                NULL,
                CERT_SYSTEM_STORE_CURRENT_USER,
                L"MY"))) {
                    cerr << "Error opening 'Personal' store." << endl;
                    return 2;
            }

            // Find desired cerificate
            if (!(pSignerCert = CertFindCertificateInStore(hStoreHandle,
                MY_ENCODING_TYPE,
                0,
                CERT_FIND_SUBJECT_STR_A,
                argv[paramCount+1],
                NULL))) {
                    cerr << "Can't find '" << argv[paramCount+1] << "' certificate in 'Personal' store." << endl;
                    return 2;
            }

            // Now get certificate's private key
            if (!CryptAcquireCertificatePrivateKey(pSignerCert,
                0,
                NULL,
                &win32RSACSP,
                &dwKeySpec,
                NULL)) {
                    cerr << "Can't acquire private key of '" << argv[paramCount+1] << "' certificate." << endl;
                    exit(1);
            }

            cp = new WinCAPICryptoProvider();
            XSECPlatformUtils::SetCryptoProvider(cp);

            HCRYPTKEY k;
            BOOL fResult = CryptGetUserKey(
                win32RSACSP,
                dwKeySpec,
                &k);

            if (!fResult || k == 0) {
                cerr << "Error obtaining default user (AT_SIGNATURE or AT_KEYEXCHANGE) key from windows RSA provider.\n";
                exit(1);
            };

            winKeyRSA = new WinCAPICryptoKeyRSA(win32RSACSP, k);
            key = winKeyRSA;
            paramCount += 2;

            CertFreeCertificateContext(pSignerCert);
            CertCloseStore(hStoreHandle, 0);
        }

#endif /* CRYPT_ACQUIRE_CACHE_FLAG */

#endif /* XSEC_HAVE_WINCAPI */

        else {

            printUsage();
            exit(1);

        }

    }

    // Create and set up the parser

    XercesDOMParser * parser = new XercesDOMParser;
    
    parser->setDoNamespaces(true);
    parser->setCreateEntityReferenceNodes(true);

    // Now parse out file

    bool errorsOccured = false;
    XMLSize_t errorCount = 0;
    try
    {
        parser->parse(argv[argc - 1]);
        errorCount = parser->getErrorCount();
        if (errorCount > 0)
            errorsOccured = true;
    }

    catch (const XMLException& e)
    {
        cerr << "An error occurred during parsing\n   Message: "
             << e.getMessage() << endl;
        errorsOccured = true;
    }


    catch (const DOMException& e)
    {
       cerr << "A DOM error occurred during parsing\n   DOMException code: "
             << e.code << endl;
        errorsOccured = true;
    }

    if (errorsOccured) {

        cout << "Errors during parse" << endl;
        exit (1);

    }

    /*

        Now that we have the parsed file, get the DOM document and start looking at it

    */
    
    DOMNode *doc;       // The document that we parsed

    doc = parser->getDocument();
    DOMDocument *theDOM = parser->getDocument();

    // Find the signature node
    
    DOMNode *sigNode = findDSIGNode(doc, "Signature");

    // Create the signature checker

    if (sigNode == 0) {

        cerr << "Could not find <Signature> node in " << argv[argc-1] << endl;
        exit(1);
    }


    XSECProvider * prov = new XSECProvider;
    DSIGSignature * sig = prov->newSignatureFromDOM(theDOM, sigNode);
     
    // Map out base path of the file
    char * filename=argv[argc-1];
#if XSEC_HAVE_GETCWD_DYN
    char *path = getcwd(NULL, 0);
    char *baseURI = (char*)malloc(strlen(path) + 8 + 1 + strlen(filename) + 1);
#else
    char path[PATH_MAX];
    char baseURI[(PATH_MAX * 2) + 10];
    getcwd(path, PATH_MAX);
#endif
    strcpy(baseURI, "file:///");        

    // Ugly and nasty but quick
    if (filename[0] != '\\' && filename[0] != '/' && filename[1] != ':') {
        strcat(baseURI, path);
        strcat(baseURI, "/");
    } else if (path[1] == ':') {
        path[2] = '\0';
        strcat(baseURI, path);
    }

    strcat(baseURI, filename);

    // Find any ':' and "\" characters
    int lastSlash = 0;
    for (unsigned int i = 8; i < strlen(baseURI); ++i) {
        if (baseURI[i] == '\\') {
            lastSlash = i;
            baseURI[i] = '/';
        }
        else if (baseURI[i] == '/')
            lastSlash = i;
    }

    // The last "\\" must prefix the filename
    baseURI[lastSlash + 1] = '\0';

    sig->getURIResolver()->setBaseURI(MAKE_UNICODE_STRING(baseURI));
#if XSEC_HAVE_GETCWD_DYN
    free(path);
    free(baseURI);
#endif

    try {
        sig->load();
        if (clearKeyInfo == true)
            sig->clearKeyInfo();
        if (key != NULL)
            sig->setSigningKey(key);
        sig->sign();

        // Add any KeyInfo elements

#if defined(XSEC_HAVE_WINCAPI)

        if (winDssKeyInfo == true && winKeyDSA != NULL) {
            char pBuf[1024];
            char qBuf[1024];
            char gBuf[1024];
            char yBuf[1024];

            unsigned int i;
            i = winKeyDSA->getPBase64BigNums((char *) pBuf, 1024);
            pBuf[i] = '\0';
            i = winKeyDSA->getQBase64BigNums((char *) qBuf, 1024);
            qBuf[i] = '\0';
            i = winKeyDSA->getGBase64BigNums((char *) gBuf, 1024);
            gBuf[i] = '\0';
            i = winKeyDSA->getYBase64BigNums((char *) yBuf, 1024);
            yBuf[i] = '\0';

            sig->clearKeyInfo();
            sig->appendDSAKeyValue(
                MAKE_UNICODE_STRING(pBuf),
                MAKE_UNICODE_STRING(qBuf),
                MAKE_UNICODE_STRING(gBuf),
                MAKE_UNICODE_STRING(yBuf));
        }

        if (winRsaKeyInfo == true && winKeyRSA != NULL) {
            char eBuf[1024];
            char mBuf[1024];

            unsigned int i;
            i = winKeyRSA->getExponentBase64BigNums((char *) eBuf, 1024);
            eBuf[i] = '\0';
            i = winKeyRSA->getModulusBase64BigNums((char *) mBuf, 1024);
            mBuf[i] = '\0';

            sig->clearKeyInfo();
            sig->appendRSAKeyValue(
                MAKE_UNICODE_STRING(mBuf),
                MAKE_UNICODE_STRING(eBuf));
        }

#endif
#if defined (XSEC_HAVE_OPENSSL)
        if (certCount > 0) {

            int i;
            // Have some certificates - see if there is already an X509 list
            DSIGKeyInfoList * kiList = sig->getKeyInfoList();
            int kiSize = (int) kiList->getSize();

            for (i = 0; i < kiSize; ++i) {

                if (kiList->item(i)->getKeyInfoType() == DSIGKeyInfo::KEYINFO_X509) {
                    keyInfoX509 = (DSIGKeyInfoX509 *) kiList->item(i);
                    break;
                }
            }

            if (keyInfoX509 == 0) {

                // Not found - need to create
                keyInfoX509 = sig->appendX509Data();

            }

            for (i = 0; i < certCount; ++i) {

                keyInfoX509->appendX509Certificate(certs[i]->getDEREncodingSB().sbStrToXMLCh());

            }

        } /* certCount > 0 */
#endif
        if (x509SubjectName != NULL) {

            int i;
            // Have some certificates - see if there is already an X509 list
            DSIGKeyInfoList * kiList = sig->getKeyInfoList();
            int kiSize = (int) kiList->getSize();

            for (i = 0; i < kiSize; ++i) {

                if (kiList->item(i)->getKeyInfoType() == DSIGKeyInfo::KEYINFO_X509) {
                    keyInfoX509 = (DSIGKeyInfoX509 *) kiList->item(i);
                    break;
                }
            }

            if (keyInfoX509 == 0) {

                // Not found - need to create
                keyInfoX509 = sig->appendX509Data();

            }

            keyInfoX509->setX509SubjectName(MAKE_UNICODE_STRING(x509SubjectName));

        } /* certCount > 0 */
    }

    catch (const XSECException &e) {
        char * m = XMLString::transcode(e.getMsg());
        cerr << "An error occurred during signing operation\n   Message: "
        << m << endl;
        XSEC_RELEASE_XMLCH(m);
        errorsOccured = true;
        exit (1);
    }

    catch (const XSECCryptoException &e) {
        cerr << "A cryptographic error occurred during signature operation\n   Message: "
        << e.getMsg() << endl;
        errorsOccured = true;
        exit(1);
    }

    catch (const NetAccessorException&) {
        cerr << "A network error occurred during signing operation\n" << endl;
        errorsOccured = true;
        exit(1);
    }

    // Print out the result

    DOMPrintFormatTarget* formatTarget = new DOMPrintFormatTarget();
    
    const XMLCh* encNameStr = XMLString::transcode("UTF-8");
    DOMNode *aNode = doc->getFirstChild();
    if (aNode->getNodeType() == DOMNode::ENTITY_NODE)
    {
        const XMLCh* aStr = ((DOMEntity *)aNode)->getInputEncoding();
        if (!strEquals(aStr, ""))
        {
            encNameStr = aStr;
        }
    }
    XMLSize_t lent = XMLString::stringLen(encNameStr);
    gEncodingName = new XMLCh[lent + 1];
    XMLString::copyNString(gEncodingName, encNameStr, lent);
    gEncodingName[lent] = 0;

    gFormatter = new XMLFormatter("UTF-8", 0, formatTarget,
                                          XMLFormatter::NoEscapes, gUnRepFlags);

    cout << doc;

    delete [] gEncodingName;
    XMLCh * toRelease = (XMLCh *) encNameStr;
    XSEC_RELEASE_XMLCH(toRelease);
    delete gFormatter;
    delete formatTarget;

#if defined (_WIN32) && defined (XSEC_HAVE_WINCAPI)
    if (win32DSSCSP != 0)
        CryptReleaseContext(win32DSSCSP,0);
    if (win32RSACSP != 0)
        CryptReleaseContext(win32RSACSP,0);
#endif

    prov->releaseSignature(sig);
    delete parser;
    delete prov;

    XSECPlatformUtils::Terminate();
#ifdef XSEC_HAVE_XALAN
    XalanTransformer::terminate();
    XPathEvaluator::terminate();
#endif
    XMLPlatformUtils::Terminate();

    
    return 0;
}
