using System;
using System.Diagnostics;
using System.IO;
using FirmaSAT;

// Some tests using the FirmaSAT .NET interface.
// Requires certain files to exist in the current working directory.

/* 
 **************************** COPYRIGHT NOTICE ****************************
 * Copyright (C) 2010-11 DI Management Services Pty Limited. 
 * All rights reserved. <www.di-mgt.com.au> <www.cryptosys.net>
 *   $Id: TestFirmaSat.cs $
 *   Last updated:
 *   $Date: 2011-12-29 08:28:00 $
 *   $Version: 5.0.0 $
 ************************* END OF COPYRIGHT NOTICE ************************
 */

namespace TestFirmaSATcsharp
{
    /// <summary>
    /// Test examples for FirmaSAT .NET interface
    /// </summary>
    class TestFirmaSATcsharp
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            int n;
            string s, s1;
            string fname;
            char ch;
            string attributeName, elementName;
            string newname, keyfile, password, certfile, certStr;
            // Required test files (see `FirmaSATtestfiles.zip`)
            string[] arrFileNames = new string[] 
            { 
                "Muestra_v2_base2011.xml",
                "Muestra_v2_signed2011.xml",
                "Muestra_v2_bad.xml",
                "detallista_base2011.xml",
                "ejemplo_v3_base2011.xml",
                "ejemplo_v3_tfd.xml",
                "ejemplo_v3_noprefix.xml",
                "V2_2-base.xml",
                "V3_2-base.xml",
                "V3_2_BadCurp.xml",
                "Muestra_v22-base.xml",
                "ejemplo_v32-base.xml",
            }; 

            Console.WriteLine("INTERROGATE THE CORE DIFIRMASAT DLL:");
            n = FirmaSAT.General.Version();
            Console.WriteLine("Version={0}", n);
            Debug.Assert(n >= 500, "Require FirmaSAT v5.0 or higher");
            s = General.CompileTime();
            Console.WriteLine("CompileTime={0}", s);
            s = General.ModuleName();
            Console.WriteLine("ModuleName={0}", s);
            ch = General.LicenceType();
            Console.WriteLine("LicenceType={0}", ch);

            /*...
            Console.WriteLine("\nInterrogate the underlying CryptoSys PKI DLL:");
            n = FirmaSAT.General.PKIVersion();
            Console.WriteLine("PKIVersion={0}", n);
            s = General.PKICompileTime();
            Console.WriteLine("PKICompileTime={0}", s);
            s = General.PKIModuleName();
            Console.WriteLine("PKIModuleName={0}", s);
             ...*/

            // CHECK FOR REQUIRED FILES IN CURRENT WORKING DIRECTORY
            Console.WriteLine("Current working directory is {0}", Directory.GetCurrentDirectory());
            string missingFile = "STOPPED: Required file is missing in current working directory";
            foreach (string fn in arrFileNames)
            {
                if (FileIsNotPresent(fn, missingFile)) return;
            }

            Console.WriteLine("\nFORM THE PIPESTRING FROM AN XML FILE:");
            fname = "Muestra_v2_signed2011.xml";
            s = Sat.MakePipeStringFromXml(fname);
            Console.WriteLine("Sat.MakePipeStringFromXml('{0}')=\n{1}", fname, s);
            Debug.Assert(s.Length > 0, "Sat.MakePipeStringFromXml failed");

            Console.WriteLine("\nSIGN AN XML FILE:");
            fname = "Muestra_v2_base2011.xml";
            newname = "Muestra_v2-new_signed2011.xml";
            keyfile = "aaa010101aaa_CSD_01.key";
            password = "a0123456789";   /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */
            certfile = "aaa010101aaa_CSD_01.cer";
            n = Sat.SignXml(newname, fname, keyfile, password, certfile);
            Console.WriteLine("Sat.SignXml('{0}'-->'{1}') returns {2}", fname, newname, n);
            Debug.Assert(n == 0, "Sat.SignXml failed");
            // Did we make a valid XML file?
            n = Sat.ValidateXml(newname);
            Console.WriteLine("Sat.ValidateXml('{0}') returns {1}", newname, n);
            Debug.Assert(n == 0, "Sat.ValidateXml failed");

            Console.WriteLine("\nVERIFY A SIGNATURE IN AN XML FILE:");
            Console.WriteLine("1. One we know is good:");
            fname = "Muestra_v2_signed2011.xml";
            n = Sat.VerifySignature(fname);
            Console.WriteLine("Sat.VerifySignature('{0}') returns {1}", fname, n);
            Debug.Assert(n == 0, "Sat.VerifySignature failed");

            Console.WriteLine("2. One we just made, so it should be good:");
            fname = newname;
            n = Sat.VerifySignature(fname);
            Console.WriteLine("Sat.VerifySignature('{0}') returns {1}", fname, n);
            Debug.Assert(n == 0, "Sat.VerifySignature failed");

            Console.WriteLine("\nFORM THE DIGEST OF THE PIPESTRING IN AN XML FILE:");
            fname = "Muestra_v2_signed2011.xml";
            s = Sat.MakeDigestFromXml(fname);
            Console.WriteLine("Sat.MakeDigestFromXml('{0}')=\n{1}", fname, s);
            Debug.Assert(s.Length > 0, "Sat.MakeDigestFromXml failed");

            Console.WriteLine("\nEXTRACT THE DIGEST FROM THE SIGNATURE IN AN XML FILE:");
            fname = "Muestra_v2_signed2011.xml";
            s = Sat.ExtractDigestFromSignature(fname);
            Console.WriteLine("Sat.ExtractDigestFromSignature('{0}')=\n{1}", fname, s);
            Debug.Assert(s.Length > 0, "Sat.ExtractDigestFromSignature failed");

            Console.WriteLine("\nTRY VALIDATING XML FILES:");
            Console.WriteLine("1. A valid one:");
            fname = "Muestra_v2_signed2011.xml";
            n = Sat.ValidateXml(fname);
            Console.WriteLine("Sat.ValidateXml('{0}') returns {1}", fname, n);
            Debug.Assert(n == 0, "Sat.ValidateXml failed");

            Console.WriteLine("2. An invalid one (missing version):");
            fname = "Muestra_v2_bad.xml";
            n = Sat.ValidateXml(fname);
            Console.WriteLine("Sat.ValidateXml('{0}') returns {1}", fname, n);
            s = Sat.LastError();
            Console.WriteLine("ErrorLookup({0})={1}", n, General.ErrorLookup(n));
            Console.WriteLine("LastError={0}", s);

            Console.WriteLine("3. An invalid one (empty noCertificado):");
            fname = "ejemplo_v32-base.xml";
            n = Sat.ValidateXml(fname);
            Console.WriteLine("Sat.ValidateXml('{0}') returns {1}", fname, n);
            s = Sat.LastError();
            Console.WriteLine("ErrorLookup({0})={1}", n, General.ErrorLookup(n));
            Console.WriteLine("LastError={0}", s);

            Console.WriteLine("\nEXTRACT AN ATTRIBUTE FROM AN XML FILE:");
            fname = "Muestra_v2_signed2011.xml";
            elementName = "Comprobante";
            attributeName = "sello";
            s = Sat.GetXmlAttribute(fname, attributeName, elementName);
            Console.WriteLine("Sat.GetXmlAttribute('{0}',{2},{3})=\n{1}", fname, s, attributeName, elementName);
            Debug.Assert(s.Length > 0, "Sat.GetXmlAttribute failed");

            elementName = "Comprobante";
            attributeName = "formaDePago";
            s = Sat.GetXmlAttribute(fname, attributeName, elementName);
            Console.WriteLine("Sat.GetXmlAttribute('{0}',{2},{3})=\n{1}", fname, s, attributeName, elementName);
            Debug.Assert(s.Length > 0, "Sat.GetXmlAttribute failed");

            Console.WriteLine("\nGET DETAILS OF X.509 CERTIFICATE:");
            Console.WriteLine("1. From embedded `certificado` in XML");
            fname = "Muestra_v2_signed2011.xml";
            s = Sat.GetCertNumber(fname);
            Console.WriteLine("Sat.GetCertNumber('{0}')={1}", fname, s);
            Debug.Assert(s.Length > 0, "Sat.GetCertNumber failed");
            s = Sat.GetCertExpiry(fname);
            Console.WriteLine("Sat.GetCertExpiry('{0}')={1}", fname, s);
            Debug.Assert(s.Length > 0, "Sat.GetCertExpiry failed");

            Console.WriteLine("2. From X.509 file");
            fname = "aaa010101aaa_CSD_01.cer";
            s = Sat.GetCertNumber(fname);
            Console.WriteLine("Sat.GetCertNumber('{0}')={1}", fname, s);
            Debug.Assert(s.Length > 0, "Sat.GetCertNumber failed");
            s = Sat.GetCertExpiry(fname);
            Console.WriteLine("Sat.GetCertExpiry('{0}')={1}", fname, s);
            Debug.Assert(s.Length > 0, "Sat.GetCertExpiry failed");

            Console.WriteLine("\nGET CERTIFICATE AS A BASE64 STRING:");
            fname = "aaa010101aaa_CSD_01.cer";
            s = Sat.GetCertAsString(fname);
            Console.WriteLine("Sat.GetCertAsString('{0}')=\n{1}", fname, s);
            Debug.Assert(s.Length > 0, "Sat.GetCertAsString failed");
            Console.WriteLine("Sat.GetCertAsString('{0}').Length={1}", fname, s.Length);
            // Compare against string from XML file
            fname = "Muestra_v2_signed2011.xml";
            s1 = Sat.GetCertAsString(fname);
            Console.WriteLine("Sat.GetCertAsString('{0}').Length={1}", fname, s1.Length);
            Debug.Assert(s1.Length > 0, "Sat.GetCertAsString failed");
            Debug.Assert(String.Compare(s, s1, true) == 0, "Sat.GetCertAsString failed");

            Console.WriteLine("\nMAKE A SIGNATURE FROM A BASE XML FILE:");
            fname = "Muestra_v2_base2011.xml";
            keyfile = "aaa010101aaa_CSD_01.key";
            password = "a0123456789";   /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */
            s = Sat.MakeSignatureFromXml(fname, keyfile, password);
            Console.WriteLine("Sat.MakeSignatureFromXml('{0}')=\n{1}", fname, s);
            Debug.Assert(s.Length > 0, "Sat.MakeSignatureFromXml failed");

            Console.WriteLine("\nSIGN A DETALLISTA XML FILE:");
            fname = "detallista_base2011.xml";
            newname = "detallista-new_signed.xml";
            keyfile = "aaa010101aaa_CSD_01.key";
            password = "a0123456789";   /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */
            certfile = "aaa010101aaa_CSD_01.cer";
            n = Sat.SignXml(newname, fname, keyfile, password, certfile);
            Console.WriteLine("Sat.SignXml('{0}'-->'{1}') returns {2}", fname, newname, n);
            Debug.Assert(n == 0, "Sat.SignXml failed");
            // Did we make a valid XML file?
            n = Sat.ValidateXml(newname);
            Console.WriteLine("Sat.ValidateXml('{0}') returns {1}", newname, n);
            Debug.Assert(n == 0, "Sat.ValidateXml failed");
            n = Sat.VerifySignature(newname);
            Console.WriteLine("Sat.VerifySignature('{0}') returns {1}", newname, n);
            Debug.Assert(n == 0, "Sat.VerifySignature failed");

            Console.WriteLine("\nEXTRACT AN ATTRIBUTE FROM A DETALLISTA XML FILE:");
            fname = "detallista-new_signed.xml";
            elementName = "detallista:detallista";
            attributeName = "documentStructureVersion";
            s = Sat.GetXmlAttribute(fname, attributeName, elementName);
            Console.WriteLine("Sat.GetXmlAttribute('{0}',{2},{3})={1}", fname, s, attributeName, elementName);
            Debug.Assert(s.Length > 0, "Sat.GetXmlAttribute failed");
            Debug.Assert(String.Compare(s, "AMC8.1") == 0, "Invalid detallista.documentStructureVersion");

            Console.WriteLine("\nEXTRACT AN ATTRIBUTE WITH ACCENTED CHARACTERS:");
            fname = "Muestra_v2_signed2011.xml";
            elementName = "DomicilioFiscal";
            attributeName = "referencia";
            s = Sat.GetXmlAttribute(fname, attributeName, elementName);
            Console.WriteLine("Sat.GetXmlAttribute('{0}',{2},{3})={1}", fname, s, attributeName, elementName);
            Debug.Assert(s.Length > 0, "Sat.GetXmlAttribute failed");
            Debug.Assert(String.Compare(s, "ÁÉÍÓÚÑ áéíóñ") == 0, "Invalid detallista.
            documentStructureVersion");

            // [NEW IN VERSION 3.0] WILL NOW SIGN CFDI Version="3.0" XML
            Console.WriteLine("\nSIGN CFDi VERSION 3.0 XML FILE:");
            fname = "ejemplo_v3_base2011.xml";
            newname = "ejemplo_v3-new_signed.xml";
            keyfile = "aaa010101aaa_CSD_01.key";
            password = "a0123456789";   /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */
            certfile = "aaa010101aaa_CSD_01.cer";
            n = Sat.SignXml(newname, fname, keyfile, password, certfile, 0);
            Console.WriteLine("Sat.SignXml('{0}'-->'{1}') returns {2}", fname, newname, n);
            Debug.Assert(n == 0, "Sat.SignXml failed");
            // Did we make a valid XML file?
            n = Sat.ValidateXml(newname);
            Console.WriteLine("Sat.ValidateXml('{0}') returns {1}", newname, n);
            Debug.Assert(n == 0, "Sat.ValidateXml failed");

            Console.WriteLine("\nVERIFY A SHA-1 SIGNATURE IN AN XML FILE:");
            fname = newname;
            n = Sat.VerifySignature(fname);
            Console.WriteLine("Sat.VerifySignature('{0}') returns {1}", fname, n);
            Debug.Assert(n == 0, "Sat.VerifySignature failed");

            Console.WriteLine("\nEXTRACT THE DIGEST FROM THE SIGNATURE IN AN XML FILE:");
            fname = "ejemplo_v3-new_signed.xml";
            s = Sat.ExtractDigestFromSignature(fname);
            Console.WriteLine("Sat.ExtractDigestFromSignature('{0}')=\n{1}", fname, s);
            Debug.Assert(s.Length > 0, "Sat.ExtractDigestFromSignature failed");

            Console.WriteLine("Using a separate certificate:");
            fname = "ejemplo_v3-new_signed.xml";
            certStr = "MIIE/TCCA+
            WgAwIBAgIUMzAwMDEwMDAwMDAxMDAwMDA4MDAwDQYJKoZIhvcNAQEFBQAwggFvMRgwFgYDVQQDDA9BLkMuIGRlIHBydWViYXMxLzAtBgNVBAo
            MJlNlcnZpY2lvIGRlIEFkbWluaXN0cmFjacOzbiBUcmlidXRhcmlhMTgwNgYDVQQLDC9BZG1pbmlzdHJhY2nDs24gZGUgU2VndXJpZGFkIGRl
            IGxhIEluZm9ybWFjacOzbjEpMCcGCSqGSIb3DQEJARYaYXNpc25ldEBwcnVlYmFzLnNhdC5nb2IubXgxJjAkBgNVBAkMHUF2LiBIaWRhbGdvI
            Dc3LCBDb2wuIEd1ZXJyZXJvMQ4wDAYDVQQRDAUwNjMwMDELMAkGA1UEBhMCTVgxGTAXBgNVBAgMEERpc3RyaXRvIEZlZGVyYWwxEjAQBgNVBA
            cMCUNveW9hY8OhbjEVMBMGA1UELRMMU0FUOTcwNzAxTk4zMTIwMAYJKoZIhvcNAQkCDCNSZXNwb25zYWJsZTogSMOpY3RvciBPcm5lbGFzIEF
            yY2lnYTAeFw0xMDA3MzAxNjU4NDBaFw0xMjA3MjkxNjU4NDBaMIGWMRIwEAYDVQQDDAlNYXRyaXogU0ExEjAQBgNVBCkMCU1hdHJpeiBTQTES
            MBAGA1UECgwJTWF0cml6IFNBMSUwIwYDVQQtExxBQUEwMTAxMDFBQUEgLyBBQUFBMDEwMTAxQUFBMR4wHAYDVQQFExUgLyBBQUFBMDEwMTAxS
            ERGUlhYMDExETAPBgNVBAsMCFVuaWRhZCAxMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDD0ltQNthUNUfzq0t1GpIyapjzOn1W5fGM5G
            /pQyMluCzP9YlVAgBjGgzwYp9Z0J9gadg3y2ZrYDwvv8b72goyRnhnv3bkjVRKlus6LDc00K7Jl23UYzNGlXn5+
            i0HxxuWonc2GYKFGsN4rFWKVy3Fnpv8Z2D7dNqsVyT5HapEqwIDAQABo4HqMIHnMAwGA1UdEwEB/wQCMAAwCwYDVR0PBAQDAgbAMB0GA1UdDg
            QWBBSYodSwRczzj5H7mcO3+mAyXz+
            y0DAuBgNVHR8EJzAlMCOgIaAfhh1odHRwOi8vcGtpLnNhdC5nb2IubXgvc2F0LmNybDAzBggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGGF2h0
            dHA6Ly9vY3NwLnNhdC5nb2IubXgvMB8GA1UdIwQYMBaAFOtZfQQimlONnnEaoFiWKfU54KDFMBAGA1UdIAQJMAcwBQYDKgMEMBMGA1UdJQQMM
            AoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBBQUAA4IBAQArHQEorApwqumSn5EqDOAjbezi8fLco1cYES/PD+
            LQRM1Vb1g7VLE3hR4S5NNBv0bMwwWAr0WfL9lRRj0PMKLorO8y4TJjRU8MiYXfzSuKYL5Z16kW8zlVHw7CtmjhfjoIMwjQo3prifWxFv7VpfI
            BstKKShU0qB6KzUUNwg2Ola4t4gg2JJcBmyIAIInHSGoeinR2V1tQ10aRqJdXkGin4WZ75yMbQH4L0NfotqY6bpF2CqIY3aogQyJGhUJji4gY
            nS2DvHcyoICwgawshjSaX8Y0Xlwnuh6EusqhqlhTgwPNAPrKIXCmOWtqjlDhho/lhkHJMzuTn8AoVapbBUnj";
            s = Sat.ExtractDigestFromSignature(fname, certStr);
            Console.WriteLine("Sat.ExtractDigestFromSignature('{0}', certStr)=\n{1}", fname, s);
            Debug.Assert(s.Length > 0, "Sat.ExtractDigestFromSignature failed");

            Console.WriteLine("\nFORM THE SHA-1 DIGEST OF THE PIPESTRING IN AN XML FILE:");
            fname = "ejemplo_v3-new_signed.xml";
            s1 = Sat.MakeDigestFromXml(fname, HashAlgorithm.Sha1);
            Console.WriteLine("Sat.MakeDigestFromXml('{0}')=\n{1}", fname, s1);
            Debug.Assert(s1.Length > 0, "Sat.MakeDigestFromXml failed");
            // Check the two digests match
            Debug.Assert(String.Compare(s, s1, true) == 0, "Digests do not match");

            // [v3.0] NEW FUNCTIONS ADDED...

            Console.WriteLine("\nGET VALIDITY DETAILS OF X.509 CERTIFICATE:");
            Console.WriteLine("1. From embedded `certificado` in XML");
            fname = "Muestra_v2_signed2011.xml";
            s = Sat.GetCertExpiry(fname);
            Console.WriteLine("Sat.GetCertExpiry('{0}')=\t{1}", fname, s);
            Debug.Assert(s.Length > 0, "Sat.GetCertExpiry failed");
            s = Sat.GetCertStart(fname);
            Console.WriteLine("Sat.GetCertStart('{0}') =\t{1}", fname, s);
            Debug.Assert(s.Length > 0, "Sat.GetCertStart failed");

            Console.WriteLine("2. From X.509 file");
            fname = "aaa010101aaa_CSD_01.cer";
            s = Sat.GetCertExpiry(fname);
            Console.WriteLine("Sat.GetCertExpiry('{0}')=\t{1}", fname, s);
            Debug.Assert(s.Length > 0, "Sat.GetCertExpiry failed");
            s = Sat.GetCertStart(fname);
            Console.WriteLine("Sat.GetCertStart('{0}') =\t{1}", fname, s);
            Debug.Assert(s.Length > 0, "Sat.GetCertStart failed");

            Console.WriteLine("\nCHECK PRIVATE KEY MATCHES PUBLIC KEY IN CERTIFICATE:");
            certfile = "aaa010101aaa_CSD_01.cer";
            keyfile = "aaa010101aaa_CSD_01.key";
            password = "a0123456789";   /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */
            n = Sat.CheckKeyAndCert(keyfile, password, certfile);
            Console.WriteLine("Sat.CheckKeyAndCert({0},{1}) = {2}", keyfile, certfile, n);
            Debug.Assert(n == 0, "Sat.CheckKeyAndCert failed");

            certfile = "aaa010101aaa_CSD_02.cer";
            keyfile = "aaa010101aaa_CSD_02.key";
            password = "a0123456789";
            n = Sat.CheckKeyAndCert(keyfile, password, certfile);
            Console.WriteLine("Sat.CheckKeyAndCert({0},{1}) = {2}", keyfile, certfile, n);
            Debug.Assert(n == 0, "Sat.CheckKeyAndCert failed");

            // Get embedded certificate from XML doc
            certfile = "Muestra_v2_signed2011.xml";
            keyfile = "aaa010101aaa_CSD_01.key";
            password = "a0123456789";
            n = Sat.CheckKeyAndCert(keyfile, password, certfile);
            Console.WriteLine("Sat.CheckKeyAndCert({0},{1}) = {2}", keyfile, certfile, n);
            Debug.Assert(n == 0, "Sat.CheckKeyAndCert failed");

            // We can pass key file and certificate as "PEM" strings.
            // The "BEGIN/END" encapsulation is optional for a certificate,
            // but is required for the encrypted private key.
            certfile =
                "-----BEGIN CERTIFICATE-----" +
                "MIIE/TCCA+WgAwIBAgIUMzAwMDEwMDAwMDAxMDAwMDA4MDAwDQYJKoZIhvcNAQEF" +
                "BQAwggFvMRgwFgYDVQQDDA9BLkMuIGRlIHBydWViYXMxLzAtBgNVBAoMJlNlcnZp" +
                "Y2lvIGRlIEFkbWluaXN0cmFjacOzbiBUcmlidXRhcmlhMTgwNgYDVQQLDC9BZG1p" +
                "bmlzdHJhY2nDs24gZGUgU2VndXJpZGFkIGRlIGxhIEluZm9ybWFjacOzbjEpMCcG" +
                "CSqGSIb3DQEJARYaYXNpc25ldEBwcnVlYmFzLnNhdC5nb2IubXgxJjAkBgNVBAkM" +
                "HUF2LiBIaWRhbGdvIDc3LCBDb2wuIEd1ZXJyZXJvMQ4wDAYDVQQRDAUwNjMwMDEL" +
                "MAkGA1UEBhMCTVgxGTAXBgNVBAgMEERpc3RyaXRvIEZlZGVyYWwxEjAQBgNVBAcM" +
                "CUNveW9hY8OhbjEVMBMGA1UELRMMU0FUOTcwNzAxTk4zMTIwMAYJKoZIhvcNAQkC" +
                "DCNSZXNwb25zYWJsZTogSMOpY3RvciBPcm5lbGFzIEFyY2lnYTAeFw0xMDA3MzAx" +
                "NjU4NDBaFw0xMjA3MjkxNjU4NDBaMIGWMRIwEAYDVQQDDAlNYXRyaXogU0ExEjAQ" +
                "BgNVBCkMCU1hdHJpeiBTQTESMBAGA1UECgwJTWF0cml6IFNBMSUwIwYDVQQtExxB" +
                "QUEwMTAxMDFBQUEgLyBBQUFBMDEwMTAxQUFBMR4wHAYDVQQFExUgLyBBQUFBMDEw" +
                "MTAxSERGUlhYMDExETAPBgNVBAsMCFVuaWRhZCAxMIGfMA0GCSqGSIb3DQEBAQUA" +
                "A4GNADCBiQKBgQDD0ltQNthUNUfzq0t1GpIyapjzOn1W5fGM5G/pQyMluCzP9YlV" +
                "AgBjGgzwYp9Z0J9gadg3y2ZrYDwvv8b72goyRnhnv3bkjVRKlus6LDc00K7Jl23U" +
                "YzNGlXn5+i0HxxuWonc2GYKFGsN4rFWKVy3Fnpv8Z2D7dNqsVyT5HapEqwIDAQAB" +
                "o4HqMIHnMAwGA1UdEwEB/wQCMAAwCwYDVR0PBAQDAgbAMB0GA1UdDgQWBBSYodSw" +
                "Rczzj5H7mcO3+mAyXz+y0DAuBgNVHR8EJzAlMCOgIaAfhh1odHRwOi8vcGtpLnNh" +
                "dC5nb2IubXgvc2F0LmNybDAzBggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGGF2h0" +
                "dHA6Ly9vY3NwLnNhdC5nb2IubXgvMB8GA1UdIwQYMBaAFOtZfQQimlONnnEaoFiW" +
                "KfU54KDFMBAGA1UdIAQJMAcwBQYDKgMEMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0G" +
                "CSqGSIb3DQEBBQUAA4IBAQArHQEorApwqumSn5EqDOAjbezi8fLco1cYES/PD+LQ" +
                "RM1Vb1g7VLE3hR4S5NNBv0bMwwWAr0WfL9lRRj0PMKLorO8y4TJjRU8MiYXfzSuK" +
                "YL5Z16kW8zlVHw7CtmjhfjoIMwjQo3prifWxFv7VpfIBstKKShU0qB6KzUUNwg2O" +
                "la4t4gg2JJcBmyIAIInHSGoeinR2V1tQ10aRqJdXkGin4WZ75yMbQH4L0NfotqY6" +
                "bpF2CqIY3aogQyJGhUJji4gYnS2DvHcyoICwgawshjSaX8Y0Xlwnuh6EusqhqlhT" +
                "gwPNAPrKIXCmOWtqjlDhho/lhkHJMzuTn8AoVapbBUnj" +
                "-----END CERTIFICATE-----";
            keyfile =
                "-----BEGIN ENCRYPTED PRIVATE KEY-----" +
                "MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQInuASOYqnkMoCAggA" +
                "MBQGCCqGSIb3DQMHBAhXp9z9DSW2PgSCAoAQVlA5ii7OMeprchNs4wwgE2ReNZrw" +
                "yVvFISJpnCN4Y906w/MYvpoc5J8q5eIZi9R5Y+FPBaCkprN0w54ypMYQX9oPuSlk" +
                "KW5C7LCq5d1mx4ZeGDmqCNXesehi/kY/0Mt/m8CB90x8dwKVE3bLJVwi+IgbgLf5" +
                "zStnIXs/LTiA3a9B0Yo8Oe6J4q+Kt0Dlg0+JfSekP76BmImpScc6lUClOX54pCCr" +
                "gOOZpdJK7G9u/UpxfVdAQeL2GaLE3tfxlEHuFtHmkXMy51OuSv7er+9FuNMxCYGa" +
                "rLiUybDuFlvcV7++9aVql/Qao5gGgKZ1oeCJNb1DZ4xdKJedtx0Kzrp3JmTeSNmG" +
                "yzkbczFEM1m2GhPQGD6uadqMCguIwlHHm46s/jWBdEjhuQaedbwcQUiOn20dsfh5" +
                "F1Ld3aMDqAXaJdqhqsXrehMXDdHoTV8c6x66cDXAqiJGng6jySDe/sNMtlX1fHiw" +
                "6mjq12Wu5E8bmlzHXy8BO4zJS9qgYec34UK30sFk3ZhBXM8ALq2cn1JkGxDDkqjv" +
                "dOOocARME+dxNHxw6rw0UESi0i5GtwYRL8eLHuPPJN7zcQG8FytGI8CsTMYSe72L" +
                "v0EFacZv7UOQde0vLww8A4EieDLxMu25mZAZ9DebFQwSHtiS4NK5PHFTk4Tm2vEr" +
                "X/HZfMQmDQbqG8v/cNNKGX/OGcIVu9eLY7T55OG1n1O2bofVkjZ0gzgAo7ZCWMqX" +
                "f/17dljEIQ/b9iHESwri/Lw5g9cQBBAx23pNPP2SteGhvQg50ZyrxGA5/an1ti2Z" +
                "u5nQ+7YpPyrKxjERRjk8B8i2qLxDrJuaDHfAOWTvdVoGX9/s7M2p68B6" +
                "-----END ENCRYPTED PRIVATE KEY-----";
            password = "a0123456789";
            n = Sat.CheckKeyAndCert(keyfile, password, certfile);
            Console.WriteLine("Sat.CheckKeyAndCert(STRINGS) = {0}", n);
            Debug.Assert(n == 0, "Sat.CheckKeyAndCert failed");

            Console.WriteLine("\nFIND THE COMPROBANTE VERSION OF AN XML FILE:");
            fname = "Muestra_v2_base2011.xml";
            n = Sat.XmlReceiptVersion(fname);
            Console.WriteLine("Sat.XmlReceiptVersion('{0}') = {1}", fname, n);
            Debug.Assert(n == 2, "Sat.XmlReceiptVersion failed");
            fname = "ejemplo_v3_base2011.xml";
            n = Sat.XmlReceiptVersion(fname);
            Console.WriteLine("Sat.XmlReceiptVersion('{0}') = {1}", fname, n);
            Debug.Assert(n == 3, "Sat.XmlReceiptVersion failed");
            // New in [v5.0]...
            fname = "Muestra_v22-base.xml";
            n = Sat.XmlReceiptVersion(fname);
            Console.WriteLine("Sat.XmlReceiptVersion('{0}') = {1}", fname, n);
            Debug.Assert(n == 22, "Sat.XmlReceiptVersion failed");
            fname = "ejemplo_v32-base.xml";
            n = Sat.XmlReceiptVersion(fname);
            Console.WriteLine("Sat.XmlReceiptVersion('{0}') = {1}", fname, n);
            Debug.Assert(n == 32, "Sat.XmlReceiptVersion failed");

            // NEW IN [v4.0] ...

            fname = "ejemplo_v3_noprefix.xml";
            n = Sat.XmlReceiptVersion(fname);
            Console.WriteLine("Sat.XmlReceiptVersion('{0}') = {1}", fname, n);
            Debug.Assert(n == 3, "Sat.XmlReceiptVersion failed");

            Console.WriteLine("\nCREATE CADENA ORIGINAL DEL TIMBRE FISCAL DIGITAL (PIPESTRING FOR TFD):");
            fname = "ejemplo_v3_tfd.xml";
            certfile = "aaa010101aaa_CSD_02.cer";
            s = Tfd.MakePipeStringFromXml(fname);
            Console.WriteLine("Tfd.MakePipeStringFromXml('{0}') =\n{1}", fname, s);

            Console.WriteLine("\nFORM DIGEST OF PIPESTRING FOR TFD:");
            s = Tfd.MakeDigestFromXml(fname);
            Console.WriteLine("Tfd.MakeDigestFromXml('{0}')=\n{1}", fname, s);

            Console.WriteLine("\nEXTRACT DIGEST FROM TFD SELLOSAT:");
            // NB certFile is required for Tfd
            s1 = Tfd.ExtractDigestFromSignature(fname, certfile);
            Console.WriteLine("Tfd.ExtractDigestFromSignature('{0}')=\n{1}", fname, s1);

            // Check the two digests match
            Debug.Assert(String.Compare(s, s1, true) == 0, "Digests do not match");

            Console.WriteLine("\nPRETEND WE ARE A PAC WITH A KEY ALLOWED TO SIGN THE TFD:");
            // Pretend we are a PAC with a key allowed to sign the TFD
            fname = "ejemplo_v3_tfd.xml";
            certfile = "aaa010101aaa_CSD_02.cer";
            keyfile = "aaa010101aaa_CSD_02.key";
            password = "a0123456789";
            s = Tfd.MakeSignatureFromXml(fname, keyfile, password);
            Console.WriteLine("Tfd.MakeSignatureFromXml('{0}')=\n{1}", fname, s);
            s1 = Sat.GetXmlAttribute(fname, "selloSAT", "TimbreFiscalDigital");
            Console.WriteLine("Correct=\n{0}", s1);
            Debug.Assert(String.Compare(s, s1, true) == 0, "selloSAT values do not match");

            Console.WriteLine("\nVERIFY SIGNATURE IN TFD SELLOSAT:");
            n = Tfd.VerifySignature(fname, certfile);
            Console.WriteLine("Tfd.VerifySignature({0},{1})={2} (expected 0)", fname, certfile, n);
            Debug.Assert(n == 0, "Tfd.VerifySignature failed");

            Console.WriteLine("\nADD UTF-8 BOM TO EXISTING FILE:");
            fname = "Muestra_v2_signed2011.xml";
            newname = "Muestra_v2_with_BOM.xml";
            n = Sat.FixBom(newname, fname);
            Console.WriteLine("Sat.FixBom({0}->{1})={2} (expected 0)", fname, newname, n);
            Debug.Assert(n == 0, "Sat.FixBom failed");

            // NEW IN [v5.0]... 

            Console.WriteLine("\nEXTRACT ATTRIBUTES FROM CONSECUTIVE ELEMENTS:");
            fname = "ejemplo_v3_base2011.xml";
            attributeName = "descripcion";
            elementName = "cfdi:Concepto";
            Console.WriteLine("For file '{0}'", fname);
            int i = 0;
            string eName = null;
            for (i = 1; i <= 100; i++)
            {
                eName = elementName + string.Format("[{0:d}]", i);
                Console.Write("Sat.GetXmlAttribute({0},{1})=", attributeName, eName);
                s = Sat.GetXmlAttribute(fname, attributeName, eName);
                if ((s.Length == 0))
                {
                    break;
                }
                Console.WriteLine("{0}", s);
            }
            Console.WriteLine();

            Console.WriteLine("\nDETECT VERSION 2.2 OR 3.2 AUTOMATICALLY AND SIGN:");
            certfile = "aaa010101aaa_CSD_01.cer";
            keyfile = "aaa010101aaa_CSD_01.key";
            password = "a0123456789";

            fname = "Muestra_v22-base.xml";
            newname = "Muestra_v22-new_signed.xml";
            n = Sat.SignXml(newname, fname, keyfile, password, certfile);
            Console.WriteLine("Sat.SignXml('{0}'-->'{1}') returns {2}", fname, newname, n);
            Debug.Assert(n == 0, "Sat.SignXml failed");
            // Did we make a valid XML file?
            n = Sat.ValidateXml(newname);
            Console.WriteLine("Sat.ValidateXml('{0}') returns {1}", newname, n);
            Debug.Assert(n == 0, "Sat.ValidateXml failed");

            fname = "V2_2-base.xml";
            newname = "V2_2-new_signed.xml";
            n = Sat.SignXml(newname, fname, keyfile, password, certfile);
            Console.WriteLine("Sat.SignXml('{0}'-->'{1}') returns {2}", fname, newname, n);
            Debug.Assert(n == 0, "Sat.SignXml failed");
            // Did we make a valid XML file?
            n = Sat.ValidateXml(newname);
            Console.WriteLine("Sat.ValidateXml('{0}') returns {1}", newname, n);
            Debug.Assert(n == 0, "Sat.ValidateXml failed");

            fname = "ejemplo_v32-base.xml";
            newname = "ejemplo_v32-new_signed.xml";
            n = Sat.SignXml(newname, fname, keyfile, password, certfile);
            Console.WriteLine("Sat.SignXml('{0}'-->'{1}') returns {2}", fname, newname, n);
            Debug.Assert(n == 0, "Sat.SignXml failed");
            // Did we make a valid XML file?
            n = Sat.ValidateXml(newname);
            Console.WriteLine("Sat.ValidateXml('{0}') returns {1}", newname, n);
            Debug.Assert(n == 0, "Sat.ValidateXml failed");

            fname = "V3_2-base.xml";
            newname = "V3_2-new_signed.xml";
            n = Sat.SignXml(newname, fname, keyfile, password, certfile);
            Console.WriteLine("Sat.SignXml('{0}'-->'{1}') returns {2}", fname, newname, n);
            Debug.Assert(n == 0, "Sat.SignXml failed");
            // Did we make a valid XML file?
            n = Sat.ValidateXml(newname);
            Console.WriteLine("Sat.ValidateXml('{0}') returns {1}", newname, n);
            Debug.Assert(n == 0, "Sat.ValidateXml failed");

            Console.WriteLine("\nVALIDATE XML WITH STRICT AND LOOSE OPTIONS:");
            Console.WriteLine("Default strict behaviour (badly formed CURP attribute):");
            fname = "V3_2_BadCurp.xml";
            n = Sat.ValidateXml(fname);
            Console.WriteLine("Sat.ValidateXml('{0}') returns {1}", fname, n);
            s = Sat.LastError();
            Console.WriteLine("ErrorLookup({0})={1}", n, General.ErrorLookup(n));
            Console.WriteLine("LastError={0}", s);
            Debug.Assert(n != 0, "Sat.ValidateXml failed to fail");

            Console.WriteLine("\nUsing XmlOption.Loose:");
            n = Sat.ValidateXml(fname, XmlOption.Loose);
            Console.WriteLine("Sat.ValidateXml('{0}', Loose) returns {1}", fname, n);
            Debug.Assert(n == 0, "Sat.ValidateXml failed");

            Console.WriteLine("\nGET PRIVATE KEY AS BASE64:");
            fname = "aaa010101aaa_CSD_01.key";
            password = "a0123456789";
            s = Sat.GetKeyAsString(fname, password);
            Console.WriteLine("Sat.GetKeyAsString({0})=\n{1}\n", fname, s);
            Console.WriteLine("Sat.GetKeyAsString('{0}').Length={1}", fname, s.Length);
            Debug.Assert(s.Length > 0, "Sat.GetKeyAsString failed");

            // Finally, show current version for core DLL
            Console.WriteLine("\nFirmaSAT Version={0} [{1}]", General.Version(), General.CompileTime());

            /*---
              Console.WriteLine("\nDISPLAY ALL POSSIBLE ERROR MESSAGES:");
              for (i = 0; i < 10000; i++)
              {
                  s = General.ErrorLookup(i);
                  if (s.Length > 0)
                      Console.WriteLine("{0}={1}", i, s);
              }
             ---*/

        }

        // LOCAL UTILITIES...

        static bool FileIsNotPresent(string filePath, string message)
        {
            FileInfo fi = new FileInfo(filePath);
            if (!fi.Exists)
            {
                Console.WriteLine("\n{0}: {1}", message, filePath);
                return true;
            }
            return false;
        }
    }
}