/* $Id: TestFirmaSAT.java $ */

/* $Date: 2015-01-13 20:19:00 $ */
/* $Version: 7.0.j0 $ */

/* 
 * Copyright (C) 2014-15 DI Management Services Pty Limited. 
 * All rights reserved. <www.di-mgt.com.au> <www.cryptosys.net>
 */

import net.cryptosys.firmasat.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;


/** The TestFirmaSAT class carries out a series of tests using the methods in the `net.cryptosys.firmasat` package.
  * <p>There is at least one test for each method in the package, including some obscure edge cases that you may not
  * need. All the methods in `net.cryptosys.firmasat` are static, intended to be called directly in a one-off manner.
  * </p>
  */
public final class TestFirmaSAT {
    
    private TestFirmaSAT() {}    // Hide constructor.
    private static final int REQUIRE_MIN_VER = 70000;
    
    // Required test files (see `FirmaSATtestfiles.zip`)
    private static final String[] arrFileNames = new String[] 
    { 
        "ejemplo_v32-base2012.xml",
            "ejemplo_v32-tfd2012.xml",
            "ejemplo_v32-noprefix2012.xml",
            "ejemplo_v32-base-latin1.xml",
            "V3_2-base2012.xml",
            "V3_2_BadCurp.xml",
            "V2_2-base2012.xml",
            "Muestra_v22-base2012.xml",
            "Muestra_v22-signed2012.xml",
            "Muestra_v22-bad-nover.xml",
            "detallista_base2012.xml",
            "Muestra_v2_signed2011.xml",
            "ejemplo_v3_signed2011.xml",
            "Muestra_nomina.xml",
            "Muestra_nomina_tfd.xml",
            "Prueba2Notario_iedu.xml",
            "emisor.cer",
            "emisor.key",
            "pac.key",
            "pac.cer",
            "Ejemplo_Retenciones-base.xml",
            "Ejemplo_Retenciones-signed-tfd.xml",
            "AAA010101AAA201501CT-base.xml",
            "AAA010101AAA201501BN-base.xml",
            "contab-SelloDigitalContElec-signed.xml",
            "ConVolE12345-base.xml",
            "CSD_E12345CV_ACP020530MP5.key",
            "CSD_E12345CV_ACP020530MP5.cer",
    }; 
   
    public static void main(String[] args) {
        int n;
        String s;
        char ch;
        String fname, newname;
        String certfile, keyfile, password, newpassword;
        String elementName, attributeName;
        String certStr, s1;
        boolean hasBOM;
        byte[] xmlArr;
        String xmlStr;
        String digest;
        byte[] b;
         
        // Print some system properties to get started...
        System.out.println("\nPRINT SOME SYSTEM PROPERTIES...");
        System.out.println("java.version=" + System.getProperty("java.version"));
        System.out.println("os.arch=" + System.getProperty("os.arch"));
        System.out.println("os.name=" + System.getProperty("os.name"));
        System.out.println("os.version=" + System.getProperty("os.version"));
        System.out.println("file.encoding=" + System.getProperty("file.encoding"));
        
        System.out.println("\nTEST THE GENERAL CLASS...");
        n = General.version();
        System.out.println("General.version()=" + n);
        assert n >= REQUIRE_MIN_VER : "Require FirmaSAT v5.4.0 or higher";
        s = General.moduleName();
        System.out.println("General.moduleName()=" + s);
        //displayChars(s);
        s = General.platform();
        System.out.println("General.platform()=" + s);
        s = General.compileTime();
        System.out.println("General.compileTime()=" + s);
        //displayChars(s);
        ch = General.licenceType();
        System.out.println("General.licenceType()=" + ch);
        s = General.lastError();
        System.out.println("General.lastError()='" + s + "' (expecting empty)");
        
        // CHECK FOR REQUIRED FILES IN CURRENT WORKING DIRECTORY
        System.out.println("Current working directory is "+System.getProperty("user.dir") );
        String missingFile = "STOPPED: Required file is missing in current working directory";
        for (String fn : arrFileNames) {
            if (fileIsNotPresent(fn, missingFile)) return;
        }


        System.out.println("\nLOOKUP MESSAGES FOR SOME ERROR CODES...");
        n = 1;
        s = General.errorLookup(n);
        System.out.println("General.errorLookup("+n+")=" + s);
        n = 17;
        s = General.errorLookup(n);
        System.out.println("General.errorLookup("+n+")=" + s);
        n = 9999;
        s = General.errorLookup(n);
        System.out.println("General.errorLookup("+n+")=" + s);

        // TEST THE METHODS IN THE SAT CLASS...
       
        System.out.println("\nFORM THE PIPESTRING FROM AN XML FILE:");
        fname = "ejemplo_v32-signed2012.xml";
        s = Sat.makePipeStringFromXml(fname);
        System.out.println("Sat.makePipeStringFromXml('"+fname+"')=\n" + s);
        assert s.length() > 0 : "Sat.makePipeStringFromXml failed";
        //displayChars(s);
        
        System.out.println("\nSIGN AN XML FILE:");
        fname = "ejemplo_v32-base2012.xml";
        newname = "ejemplo_v32-new_signed.xml";
        keyfile = "emisor.key";
        password = "12345678a";   /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */
        certfile = "emisor.cer";
        n = Sat.signXml(newname, fname, keyfile, password, certfile);
        System.out.println("Sat.signXml('"+fname+"'-->'"+newname+"') returns "+n);
        assert n == 0 : "Sat.signXml failed";
        // Did we make a valid XML file?
        n = Sat.validateXml(newname);
        System.out.println("Sat.validateXml('"+newname+"') returns "+n);
        assert n == 0 : "Sat.validateXml failed";
        
        System.out.println("\nVERIFY A SIGNATURE IN AN XML FILE:");
        System.out.println("1. One we know is good:");
        fname = "ejemplo_v32-signed2012.xml";
        n = Sat.verifySignature(fname);
        System.out.println("Sat.verifySignature('"+fname+"') returns "+n);
        assert n == 0 :  "Sat.verifySignature failed";
        
        System.out.println("2. One we just made, so it should be good:");
        fname = newname;
        n = Sat.verifySignature(fname);
        System.out.println("Sat.verifySignature('"+fname+"') returns "+n);
        assert n == 0 :  "Sat.verifySignature failed";

        System.out.println("\nFORM THE DIGEST OF THE PIPESTRING IN AN XML FILE:");
        fname = "ejemplo_v32-signed2012.xml";
        s = Sat.makeDigestFromXml(fname);
        System.out.println("Sat.makeDigestFromXml('"+ fname+"')=\n"+ s);
        assert s.length() > 0 : "Sat.makeDigestFromXml failed";
        
        System.out.println("\nEXTRACT THE DIGEST FROM THE SIGNATURE IN AN XML FILE:");
        fname = "ejemplo_v32-signed2012.xml";
        s = Sat.extractDigestFromSignature(fname);
        System.out.println("Sat.extractDigestFromSignature('"+ fname+"')=\n"+ s);
        assert s.length() > 0 : "Sat.extractDigestFromSignature failed";

        System.out.println("\nTRY VALIDATING XML FILES:");
        System.out.println("1. A valid one:");
        fname = "Muestra_v22-signed2012.xml";
        n = Sat.validateXml(fname);
        System.out.println("Sat.validateXml('"+ fname+"') returns "+ n);
        assert n == 0 : "Sat.validateXml failed";

        System.out.println("2. An invalid one (missing version):");
        fname = "Muestra_v22-bad-nover.xml";
        n = Sat.validateXml(fname);
        System.out.println("Sat.validateXml('"+ fname+"') returns "+ n);
        s = General.lastError();
        System.out.println("ErrorLookup("+ n+")="+ General.errorLookup(n));
        System.out.println("LastError="+ s);
        assert n != 0 : "Sat.validateXml should have failed";
        
        System.out.println("3. An invalid one (empty noCertificado):");
        fname = "Muestra_v22-base2012-nocert.xml";
        n = Sat.validateXml(fname);
        System.out.println("Sat.validateXml('"+ fname+"') returns "+ n);
        s = General.lastError();
        System.out.println("ErrorLookup("+ n+")="+ General.errorLookup(n));
        System.out.println("LastError="+ s);
        assert n != 0 : "Sat.validateXml should have failed";

        System.out.println("\nEXTRACT AN ATTRIBUTE FROM AN XML FILE:");
        fname = "ejemplo_v32-signed2012.xml";
        elementName = "Comprobante";
        attributeName = "sello";
        s = Sat.getXmlAttribute(fname, attributeName, elementName);
        System.out.println("Sat.getXmlAttribute('"+ fname +"',"+ attributeName +","+ elementName +")=\n"+ s );
        assert s.length() > 0 : "Sat.getXmlAttribute failed";
        
        elementName = "Comprobante";
        attributeName = "formaDePago";
        s = Sat.getXmlAttribute(fname, attributeName, elementName);
        System.out.println("Sat.getXmlAttribute('"+ fname +"',"+ attributeName +","+ elementName +")=\n"+ s );
        assert s.length() > 0 : "Sat.getXmlAttribute failed";
        
        System.out.println("\nGET DETAILS OF X.509 CERTIFICATE:");
        System.out.println("1. From embedded `certificado` in XML");
        fname = "ejemplo_v32-signed2012.xml";
        s = Sat.getCertNumber(fname);
        System.out.println("Sat.getCertNumber('"+ fname +"')="+ s );
        assert s.length() > 0 : "Sat.getCertNumber failed";
        s = Sat.getCertExpiry(fname);
        System.out.println("Sat.getCertExpiry('"+ fname +"')="+ s );
        assert s.length() > 0 : "Sat.getCertExpiry failed";
        
        System.out.println("2. From X.509 file");
        fname = "emisor.cer";
        s = Sat.getCertNumber(fname);
        System.out.println("Sat.getCertNumber('"+ fname +"')="+ s );
        assert s.length() > 0 : "Sat.getCertNumber failed";
        s = Sat.getCertExpiry(fname);
        System.out.println("Sat.getCertExpiry('"+ fname +"')="+ s );
        assert s.length() > 0 : "Sat.getCertExpiry failed";

        System.out.println("\nGET CERTIFICATE AS A BASE64 STRING:");
        fname = "emisor.cer";
        s = Sat.getCertAsString(fname);
        System.out.println("Sat.getCertAsString('"+ fname +"')=\n"+ s );
        assert s.length() > 0 : "Sat.getCertAsString failed";
        System.out.println("Sat.getCertAsString('"+ fname +"').length()="+ s.length() );
        // Compare against String from XML file
        fname = "Muestra_v22-signed2012.xml";
        s1 = Sat.getCertAsString(fname);
        System.out.println("Sat.getCertAsString('"+ fname +"').length()="+ s1.length() );
        assert s1.length() > 0 : "Sat.getCertAsString failed";
        assert s.equalsIgnoreCase(s1): "Sat.getCertAsString failed";
        // Keep this String for later
        certStr = s1;
        
        System.out.println("\nMAKE A SIGNATURE FROM A BASE XML FILE:");
        fname = "ejemplo_v32-base2012.xml";
        keyfile = "emisor.key";
        password = "12345678a";   /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */
        s = Sat.makeSignatureFromXml(fname, keyfile, password);
        System.out.println("Sat.makeSignatureFromXml('"+ fname +"')=\n"+ s );
        assert s.length() > 0 : "Sat.makeSignatureFromXml failed";
        
        System.out.println("\nSIGN A DETALLISTA XML FILE:");
        fname = "detallista_base2012.xml";
        newname = "detallista-new_signed.xml";
        keyfile = "emisor.key";
        password = "12345678a";   /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */
        certfile = "emisor.cer";
        n = Sat.signXml(newname, fname, keyfile, password, certfile);
        System.out.println("Sat.signXml('"+ fname +"'-->'"+ newname +"') returns "+ n );
        assert n == 0 : "Sat.signXml failed";
        // Did we make a valid XML file?
        n = Sat.validateXml(newname);
        System.out.println("Sat.validateXml('"+ newname +"') returns "+ n );
        assert n == 0 : "Sat.validateXml failed";
        n = Sat.verifySignature(newname);
        System.out.println("Sat.verifySignature('"+ newname +"') returns "+ n );
        assert n == 0 : "Sat.verifySignature failed";
        
        System.out.println("\nEXTRACT AN ATTRIBUTE FROM A DETALLISTA XML FILE:");
        fname = "detallista-new_signed.xml";
        elementName = "detallista:detallista";
        attributeName = "documentStructureVersion";
        s = Sat.getXmlAttribute(fname, attributeName, elementName);
        System.out.println("Sat.getXmlAttribute('"+ fname +"',"+ attributeName +","+ elementName +")="+ s );
        assert s.length() > 0 : "Sat.getXmlAttribute failed";
        assert s.equalsIgnoreCase("AMC8.1") : "Invalid detallista.documentStructureVersion";
        
        System.out.println("\nEXTRACT AN ATTRIBUTE WITH ACCENTED CHARACTERS:");
        fname = "Muestra_v22-signed2012.xml";
        elementName = "Domicilio";
        attributeName = "pais";
        s = Sat.getXmlAttribute(fname, attributeName, elementName);
        System.out.println("Sat.getXmlAttribute('"+ fname +"',"+ attributeName +","+ elementName +")="+ s );
        assert s.length() > 0 : "Sat.getXmlAttribute failed";
        assert s.equalsIgnoreCase("México") : "Invalid Domicilio/@pais attribute";
        
        // SIGN CFDI Version="3.2" XML
        System.out.println("\nSIGN CFDi VERSION 3.2 XML FILE:");
        fname = "ejemplo_v32-base2012.xml";
        newname = "ejemplo_v32-new_signed.xml";
        keyfile = "emisor.key";
        password = "12345678a";   /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */
        certfile = "emisor.cer";
        n = Sat.signXml(newname, fname, keyfile, password, certfile);
        System.out.println("Sat.signXml('"+ fname +"'-->'"+ newname +"') returns "+ n );
        assert n == 0 : "Sat.signXml failed";
        // Did we make a valid XML file?
        n = Sat.validateXml(newname);
        System.out.println("Sat.validateXml('"+ newname +"') returns "+ n );
        assert n == 0 : "Sat.validateXml failed";
        
        System.out.println("\nVERIFY A SHA-1 SIGNATURE IN AN XML FILE:");
        fname = newname;
        n = Sat.verifySignature(fname);
        System.out.println("Sat.verifySignature('"+ fname +"') returns "+ n );
        assert n == 0 : "Sat.verifySignature failed";
        
        System.out.println("\nEXTRACT THE DIGEST FROM THE SIGNATURE IN AN XML FILE:");
        fname = "ejemplo_v32-new_signed.xml";
        s = Sat.extractDigestFromSignature(fname);
        System.out.println("Sat.extractDigestFromSignature('"+ fname +"')=\n"+ s );
        assert s.length() > 0 : "Sat.extractDigestFromSignature failed";
        
        System.out.println("Using a separate certificate file:");
        fname = "ejemplo_v32-new_signed.xml";
        certfile = "emisor.cer";
        s = Sat.extractDigestFromSignature(fname, certfile);
        System.out.println("Sat.extractDigestFromSignature('"+ fname +"', certfile)=\n"+ s );
        assert s.length() > 0 : "Sat.extractDigestFromSignature failed";
        
        // Use certStr we extracted from emisor.cer above
        System.out.println("Using a separate certificate stored as a String:");
        fname = "ejemplo_v32-new_signed.xml";
        s = Sat.extractDigestFromSignature(fname, certStr);
        System.out.println("Sat.extractDigestFromSignature('"+ fname +"', certStr)=\n"+ s );
        assert s.length() > 0 : "Sat.extractDigestFromSignature failed";

        System.out.println("\nFORM THE SHA-1 DIGEST OF THE PIPESTRING IN AN XML FILE:");
        fname = "ejemplo_v32-new_signed.xml";
        s1 = Sat.makeDigestFromXml(fname, Sat.HashAlgorithm.Sha1);
        System.out.println("Sat.makeDigestFromXml('"+ fname +"')=\n"+ s1 );
        assert s1.length() > 0 : "Sat.makeDigestFromXml failed";
        // Check the two digests match
        assert s.equalsIgnoreCase(s1) : "Digests do not match";

        System.out.println("\nGET VALIDITY DETAILS OF X.509 CERTIFICATE:");
        System.out.println("1. From embedded `certificado` in XML");
        fname = "ejemplo_v32-signed2012.xml";
        s = Sat.getCertExpiry(fname);
        System.out.println("Sat.getCertExpiry('"+ fname +"')=\t"+ s );
        assert s.length() > 0 : "Sat.getCertExpiry failed";
        s = Sat.getCertStart(fname);
        System.out.println("Sat.getCertStart('"+ fname +"') =\t"+ s );
        assert s.length() > 0 : "Sat.getCertStart failed";
        
        System.out.println("2. From X.509 file");
        fname = "emisor.cer";
        s = Sat.getCertExpiry(fname);
        System.out.println("Sat.getCertExpiry('"+ fname +"')=\t"+ s );
        assert s.length() > 0 : "Sat.getCertExpiry failed";
        s = Sat.getCertStart(fname);
        System.out.println("Sat.getCertStart('"+ fname +"') =\t"+ s );
        assert s.length() > 0 : "Sat.getCertStart failed";

        System.out.println("\nCHECK PRIVATE KEY MATCHES PUBLIC KEY IN CERTIFICATE:");
        certfile = "emisor.cer";
        keyfile = "emisor.key";
        password = "12345678a";   /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */
        n = Sat.checkKeyAndCert(keyfile, password, certfile);
        System.out.println("Sat.checkKeyAndCert("+ keyfile +","+ certfile +") = "+ n );
        assert n == 0 : "Sat.checkKeyAndCert failed";
        
        certfile = "pac.cer";
        keyfile = "pac.key";
        password = "12345678a";
        n = Sat.checkKeyAndCert(keyfile, password, certfile);
        System.out.println("Sat.checkKeyAndCert("+ keyfile +","+ certfile +") = "+ n );
        assert n == 0 : "Sat.checkKeyAndCert failed";
        
        // Get embedded certificate from XML doc
        certfile = "ejemplo_v32-signed2012.xml";
        keyfile = "emisor.key";
        password = "12345678a";
        n = Sat.checkKeyAndCert(keyfile, password, certfile);
        System.out.println("Sat.checkKeyAndCert("+ keyfile +","+ certfile +") = "+ n );
        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.
        // These strings are from `emisor-pem.cer` and `emisor-pem.key`, respectively
        certfile =
            "-----BEGIN CERTIFICATE-----" +
            "MIIEdDCCA1ygAwIBAgIUMjAwMDEwMDAwMDAxMDAwMDU4NjcwDQYJKoZIhvcNAQEF" +
            "BQAwggFvMRgwFgYDVQQDDA9BLkMuIGRlIHBydWViYXMxLzAtBgNVBAoMJlNlcnZp" +
            "Y2lvIGRlIEFkbWluaXN0cmFjacOzbiBUcmlidXRhcmlhMTgwNgYDVQQLDC9BZG1p" +
            "bmlzdHJhY2nDs24gZGUgU2VndXJpZGFkIGRlIGxhIEluZm9ybWFjacOzbjEpMCcG" +
            "CSqGSIb3DQEJARYaYXNpc25ldEBwcnVlYmFzLnNhdC5nb2IubXgxJjAkBgNVBAkM" +
            "HUF2LiBIaWRhbGdvIDc3LCBDb2wuIEd1ZXJyZXJvMQ4wDAYDVQQRDAUwNjMwMDEL" +
            "MAkGA1UEBhMCTVgxGTAXBgNVBAgMEERpc3RyaXRvIEZlZGVyYWwxEjAQBgNVBAcM" +
            "CUNveW9hY8OhbjEVMBMGA1UELRMMU0FUOTcwNzAxTk4zMTIwMAYJKoZIhvcNAQkC" +
            "DCNSZXNwb25zYWJsZTogSMOpY3RvciBPcm5lbGFzIEFyY2lnYTAeFw0xMjA3Mjcx" +
            "NzAyMDBaFw0xNjA3MjcxNzAyMDBaMIHbMSkwJwYDVQQDEyBBQ0NFTSBTRVJWSUNJ" +
            "T1MgRU1QUkVTQVJJQUxFUyBTQzEpMCcGA1UEKRMgQUNDRU0gU0VSVklDSU9TIEVN" +
            "UFJFU0FSSUFMRVMgU0MxKTAnBgNVBAoTIEFDQ0VNIFNFUlZJQ0lPUyBFTVBSRVNB" +
            "UklBTEVTIFNDMSUwIwYDVQQtExxBQUEwMTAxMDFBQUEgLyBIRUdUNzYxMDAzNFMy" +
            "MR4wHAYDVQQFExUgLyBIRUdUNzYxMDAzTURGUk5OMDkxETAPBgNVBAsTCFVuaWRh" +
            "ZCAxMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2TTQSPONBOVxpXv9wLYo8" +
            "jezBrb34i/tLx8jGdtyy27BcesOav2c1NS/Gdv10u9SkWtwdy34uRAVe7H0a3VMR" +
            "LHAkvp2qMCHaZc4T8k47Jtb9wrOEh/XFS8LgT4y5OQYo6civfXXdlvxWU/gdM/e6" +
            "I2lg6FGorP8H4GPAJ/qCNwIDAQABox0wGzAMBgNVHRMBAf8EAjAAMAsGA1UdDwQE" +
            "AwIGwDANBgkqhkiG9w0BAQUFAAOCAQEATxMecTpMbdhSHo6KVUg4QVF4Op2IBhiM" +
            "aOrtrXBdJgzGotUFcJgdBCMjtTZXSlq1S4DG1jr8p4NzQlzxsdTxaB8nSKJ4KEMg" +
            "IT7E62xRUj15jI49qFz7f2uMttZLNThipunsN/NF1XtvESMTDwQFvas/Ugig6qwE" +
            "fSZc0MDxMpKLEkEePmQwtZD+zXFSMVa6hmOu4M+FzGiRXbj4YJXn9Myjd8xbL/c+" +
            "9UIcrYoZskxDvMxc6/6M3rNNDY3OFhBK+V/sPMzWWGt8S1yjmtPfXgFs1t65AZ2h" +
            "cTwTAuHrKwDatJ1ZPfa482ZBROAAX1waz7WwXp0gso7sDCm2/yUVww==" +
            "-----END CERTIFICATE-----"; 
        keyfile =
            "-----BEGIN ENCRYPTED PRIVATE KEY-----" +
            "MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI3V0iJrMlAI0CAggA" +
            "MBQGCCqGSIb3DQMHBAgmAnxnceS9FgSCAoBeM47Z7bIErpBCcTTUqChTzglQ2Om+" +
            "Nw4Nv0YZZkw8T0iLknP5J+p0EJHDQzATqHC1VZmG8+S23yJWSpYKzikQ0EdQCg3y" +
            "pl6QP55wdZ6DmkJQPo4cE+Z9elLT4QDRH//bJbZlnUtwKlu0ldMFNlBdAz/vYM4C" +
            "mad/cYIaR2tEJrJQvOiT4Z+c5Thf/3kLr5ohtVi7IDCFrIuSk0y0994rW4yGXQT7" +
            "/licNrOSDnBIWYP1poYa5YBFw6KMqA+uC7xwu6XGpdMJ3pyaogZUlw0MwL0mnWNI" +
            "DUkKm0lBrM0bEXZJgVbMzBokUupOMq6RAIOb6NXRREAw6mpyiekMNUE2ajXB2Xlj" +
            "1O12LqBEPMfdjeeQ1oakUuAfrTKiFIREcSW8472Xcmzu40u+zTCJVefpgNb6FF6V" +
            "L3QH3LZ+F2+A92rXn1gZETlHn9MjMfZyh7NUUbZ+BCpq0U2lyARbTZ2T+LvHP1gk" +
            "2oEDB/X+otXuK0cMxj8yG4tc5g7w7xo8GCMAu8zmKQa4pNd5+vND4JgwIaSWC5yW" +
            "EB0/E5qszMij2l1Ibni3Uj9HB8baf0ZU9hMahP4hjF1hFb4HfRxcmYOGyfldMCnr" +
            "1zQ0IZXCS5ki/xdrvxgkj6CEl9dXutPBrbg/mIPJLIkJPGQ2T78tlwvFl5C5i9U9" +
            "sOha2UmVXtDwg0zwWqLgS/6oQNYouQQlFbeH3jEYgHGENqunsIb0Nt4HBSQq6NgH" +
            "XGK7WRaLeDPIDr7j85cBdycuKXPH4Vtb8qx9dH4veKKz6ymbiHtXY63+3J4Geh6t" +
            "IzLduuX0CBiLob0R+gorwUK28i6bS373/d/GQoWOdBmSSws2BaHEGBmU" +
            "-----END ENCRYPTED PRIVATE KEY-----";
        password = "12345678a";
        n = Sat.checkKeyAndCert(keyfile, password, certfile);
        System.out.println("Sat.checkKeyAndCert(STRINGS) = "+ n );
        assert n == 0 : "Sat.checkKeyAndCert failed";
        
        System.out.println("\nFIND THE COMPROBANTE VERSION OF AN XML FILE:");
        fname = "Muestra_v22-base2012.xml";
        n = Sat.xmlReceiptVersion(fname);
        System.out.println("Sat.xmlReceiptVersion('"+ fname +"') = "+ n );
        assert n == 22 : "Sat.xmlReceiptVersion failed";
        fname = "ejemplo_v32-base2012.xml";
        n = Sat.xmlReceiptVersion(fname);
        System.out.println("Sat.xmlReceiptVersion('"+ fname +"') = "+ n );
        assert n == 32 : "Sat.xmlReceiptVersion failed";
        // Older versions still work... 
        fname = "Muestra_v2_signed2011.xml";
        n = Sat.xmlReceiptVersion(fname);
        System.out.println("Sat.xmlReceiptVersion('"+ fname +"') = "+ n );
        assert n == 2 : "Sat.xmlReceiptVersion failed";
        fname = "ejemplo_v3_signed2011.xml";
        n = Sat.xmlReceiptVersion(fname);
        System.out.println("Sat.xmlReceiptVersion('"+ fname +"') = "+ n );
        assert n == 3 : "Sat.xmlReceiptVersion failed";
        // Show we can cope with CFDi XML with no prefixes
        fname = "ejemplo_v32-noprefix2012.xml";
        n = Sat.xmlReceiptVersion(fname);
        System.out.println("Sat.xmlReceiptVersion('"+ fname +"') = "+ n );
        assert n == 32 : "Sat.xmlReceiptVersion failed";

        System.out.println("\nCREATE CADENA ORIGINAL DEL TIMBRE FISCAL DIGITAL (PIPESTRING FOR TFD):");
        fname = "ejemplo_v32-tfd2012.xml";
        s = Tfd.makePipeStringFromXml(fname);
        System.out.println("Tfd.makePipeStringFromXml('"+fname+"')=\n" + s);
        assert s.length() > 0 : "Tfd.makePipeStringFromXml failed";

        System.out.println("\nFORM DIGEST OF PIPESTRING FOR TFD:");
        s = Tfd.makeDigestFromXml(fname);
        System.out.println("Tfd.makeDigestFromXml('"+ fname +"')=\n"+ s );
        assert s.length() > 0 : "Tfd.makeDigestFromXml failed";
            
        System.out.println("\nEXTRACT DIGEST FROM TFD SELLOSAT:");
        // NB certFile is required for Tfd
        certfile = "pac.cer";
        s1 = Tfd.extractDigestFromSignature(fname, certfile);
        System.out.println("Tfd.extractDigestFromSignature('"+ fname +"')=\n"+ s1 );
            
        // Check the two digests match
        assert s.equalsIgnoreCase(s1) : "Digests do not match";

        System.out.println("\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
        // so create a TFD signature String we could paste into the `selloSAT' node
        fname = "ejemplo_v32-tfd2012.xml";
        certfile = "pac.cer";
        keyfile = "pac.key";
        password = "12345678a";
        s = Tfd.makeSignatureFromXml(fname, keyfile, password);
        System.out.println("Tfd.makeSignatureFromXml('"+ fname +"')=\n"+ s );
        s1 = Sat.getXmlAttribute(fname, "selloSAT", "TimbreFiscalDigital");
        System.out.println("Correct=\n"+ s1 );
        assert s.equalsIgnoreCase(s1) : "selloSAT values do not match";

        System.out.println("\nVERIFY SIGNATURE IN TFD SELLOSAT:");
        n = Tfd.verifySignature(fname, certfile);
        System.out.println("Tfd.verifySignature("+ fname +","+ certfile +")="+ n +" (expected 0)");
        assert n == 0 : "Tfd.verifySignature failed";
            
        System.out.println("\nADD UTF-8 BOM TO EXISTING FILE:");
        fname = "Muestra_v22-signed2012.xml";
        newname = "Muestra_v22-with_BOM.xml";
        n = Sat.fixBom(newname, fname);
        System.out.println("Sat.fixBom("+ fname +"->"+ newname +")="+ n +" (expected 0)");
        assert n == 0 : "Sat.fixBom failed";

        System.out.println("\nEXTRACT ATTRIBUTES FROM CONSECUTIVE ELEMENTS:");
        fname = "ejemplo_v32-base2012.xml";
        attributeName = "descripcion";
        elementName = "cfdi:Concepto";
        System.out.println("For file '"+ fname +"'");
        String eName = null;
        for (int i = 1; i <= 100; i++) { 
            // Loop until we get an empty string returned...
            eName = elementName + String.format("[%d]", i);
            System.out.print("Sat.getXmlAttribute("+attributeName+","+eName+")=");
            s = Sat.getXmlAttribute(fname, attributeName, eName);
            if ((s.length() == 0)) {
                break;
            }
            System.out.println(s);
        }
        System.out.println();
        
        System.out.println("\nVALIDATE XML WITH STRICT AND LOOSE OPTIONS:");
        System.out.println("Default strict behaviour (Bad attribute..@CURP..is too long):");
        fname = "V3_2_BadCurp.xml";
        n = Sat.validateXml(fname);
        System.out.println("Sat.validateXml('"+ fname +"') returns "+ n );
        s = General.lastError();
        System.out.println("ErrorLookup("+ n +")="+ General.errorLookup(n) );
        System.out.println("LastError="+ s );
        assert n != 0 : "Sat.validateXml failed to fail";
        
        System.out.println("\nUsing XmlOption.Loose:");
        n = Sat.validateXml(fname, Sat.XmlOption.Loose);
        System.out.println("Sat.validateXml('"+ fname +"', Loose) returns "+ n );
        assert n == 0 : "Sat.validateXml failed";
        
        System.out.println("\nGET PRIVATE KEY AS BASE64:");
        fname = "emisor.key";
        password = "12345678a";
        s = Sat.getKeyAsString(fname, password);
        System.out.println("Sat.getKeyAsString("+ fname +")=\n"+ s +"\n");
        System.out.println("Sat.getKeyAsString('"+ fname +"').length()="+ s.length() );
        assert s.length() > 0 : "Sat.getKeyAsString failed";

        System.out.println("\nWRITE PFX FROM PRIVATE KEY AND CERT:");
        certfile = "emisor.cer";
        keyfile = "emisor.key";
        password = "12345678a";
        fname = "archivo_pfx.pem";
        newpassword = "clavedesalida";
        n = Sat.writePfxFile(fname, newpassword, keyfile, password, certfile);
        System.out.println("Sat.writePfxFile()->"+ fname +" returns "+ n );
        assert n == 0 : "Sat.writePfxFile failed";
        System.out.println("New PFX file is "+ fileLength(fname) +" bytes long.");

        System.out.println("\nGET RFC AND ORG NAME FROM CERT:");
        fname = "emisor.cer";
        System.out.println("FILE: "+ fname );
        s = Sat.queryCert(fname, Sat.Query.rfc);
        System.out.println("Sat.queryCert(rfc)="+ s );
        assert s.length() > 0 : "Sat.queryCert(rfc) failed";
        s = Sat.queryCert(fname, Sat.Query.organizationName);
        System.out.println("Sat.queryCert(organizationName)='"+ s +"'");
        assert s.length() > 0 : "Sat.queryCert(organizationName) failed";
        fname = "ejemplo_v32-signed2012.xml";
        System.out.println("FILE: "+ fname );
        s = Sat.queryCert(fname, Sat.Query.rfc);
        System.out.println("Sat.queryCert(rfc)="+ s );
        assert s.length() > 0 : "Sat.queryCert(rfc) failed";
        s = Sat.queryCert(fname, Sat.Query.organizationName);
        System.out.println("Sat.queryCert(organizationName)='"+ s +"'");
        assert s.length() > 0 : "Sat.queryCert(organizationName) failed";

        System.out.println("\nTEST OTHER QUERIES FOR CERT:");
        fname = "emisor.cer";
        System.out.println("FILE: "+ fname );
        s = Sat.queryCert(fname, Sat.Query.notBefore);
        System.out.println("Sat.queryCert(notBefore)="+ s );
        assert s.length() > 0 : "Sat.queryCert(notBefore) failed";
        s = Sat.queryCert(fname, Sat.Query.notAfter);
        System.out.println("Sat.queryCert(notAfter)="+ s );
        assert s.length() > 0 : "Sat.queryCert(notAfter) failed";
        s = Sat.queryCert(fname, Sat.Query.serialNumber);
        System.out.println("Sat.queryCert(serialNumber)="+ s );
        assert s.length() > 0 : "Sat.queryCert(serialNumber) failed";
        
        System.out.println("\nTEST NOMINA FEATURE:");
        fname = "Muestra_nomina.xml";
        System.out.println("FILE: "+ fname );
        s = Sat.makePipeStringFromXml(fname);
        System.out.println("Sat.makePipeStringFromXml=\n"+ s );
        assert s.length() > 0 : "Sat.makePipeStringFromXml failed";
        
        newname = "Muestra_nomina2014-signed.xml";
        certfile = "emisor.cer";
        keyfile = "emisor.key";
        password = "12345678a";
        n = Sat.signXml(newname, fname, keyfile, password, certfile);
        System.out.println("Sat.signXml('"+ fname +"'-->'"+ newname +"') returns "+ n );
        assert n == 0 : "Sat.signXml failed";
        // Did we make a valid XML file?
        n = Sat.validateXml(newname);
        System.out.println("Sat.validateXml('"+ newname +"') returns "+ n +" ==> "+ (n==0 ? "OK" : "FAILED") );
        assert n == 0 : "Sat.validateXml failed";
        // Check file has a BOM
        hasBOM = FileHasBom(newname);
        System.out.println("File '"+ newname +"' "+ (hasBOM ? "has a BOM" : "does NOT have a BOM") );
        assert hasBOM : "Expecting to find a BOM";

        System.out.println("\nSIGN FILE WITHOUT BOM:");
        newname = "Muestra_nomina2014-signed-no-bom.xml";
        n = Sat.signXml(newname, fname, keyfile, password, certfile, Sat.SIGN_NO_BOM);
        System.out.println("Sat.signXml('"+ fname +"'-->'"+ newname +"') returns "+ n );
        assert n == 0 : "Sat.signXml failed";
        // Did we make a valid XML file?
        n = Sat.validateXml(newname);
        System.out.println("Sat.validateXml('"+ newname +"') returns "+ n +" ==> "+ (n == 0 ? "OK" : "FAILED") );
        assert n == 0 : "Sat.validateXml failed";
        // Check file does not have a BOM
        hasBOM = FileHasBom(newname);
        System.out.println("File '"+ newname +"' "+ (hasBOM ? "has a BOM" : "does NOT have a BOM") );
        assert !hasBOM : "Not expecting to find a BOM";

        System.out.println("\nSIGN FILE WITHOUT BOM AND EMPTY ELEMENTS:");
        newname = "Muestra_nomina2014-signed-no-bom-empty-elems.xml";
        n = Sat.signXml(newname, fname, keyfile, password, certfile, Sat.SIGN_NO_BOM + Sat.SIGN_USE_EMPTY_ELEMENTS);
        System.out.println("Sat.signXml('"+ fname +"'-->'"+ newname +"') returns "+ n );
        assert n == 0 : "Sat.signXml failed";
        // Did we make a valid XML file?
        n = Sat.validateXml(newname);
        System.out.println("Sat.validateXml('"+ newname +"') returns "+ n +" ==> "+ (n == 0 ? "OK" : "FAILED") );
        assert n == 0 : "Sat.validateXml failed";
        // Check file does not have a BOM
        hasBOM = FileHasBom(newname);
        System.out.println("File '"+ newname +"' "+ (hasBOM ? "has a BOM" : "does NOT have a BOM") );
        assert !hasBOM : "Not expecting to find a BOM";

        System.out.println("\nSIGN A `NOTARIOSPUBLICUS` XML FILE:");
        fname = "Prueba2Notario_iedu.xml";
        newname = "Prueba2Notario_iedu-signed.xml";
        certfile = "emisor.cer";
        keyfile = "emisor.key";
        password = "12345678a";
        n = Sat.signXml(newname, fname, keyfile, password, certfile);
        System.out.println("Sat.signXml('"+ fname +"'-->'"+ newname +"') returns "+ n );
        assert n == 0 : "Sat.signXml failed";
        // Did we make a valid XML file?
        n = Sat.validateXml(newname);
        System.out.println("Sat.validateXml('"+ newname +"') returns "+ n +" ==> "+ (n == 0 ? "OK" : "FAILED") );
        assert n == 0 : "Sat.validateXml failed";

        System.out.println("\nTEST TFD IN NOMINA XML:");
        // One we made earlier using pac.key/pac.cer
        fname = "Muestra_nomina_tfd.xml";
        certfile = "pac.cer";
        s = Tfd.makePipeStringFromXml(fname);
        System.out.println("FILE: "+ fname );
        System.out.println("Tfd.makePipeStringFromXml(TFD)=\n"+ s );
        assert s.length() > 0 : "Tfd.makePipeStringFromXml(TFD) failed";
        n = Tfd.verifySignature(fname, certfile);
        System.out.println("Tfd.verifySignature('"+ fname +"') returns "+ n +" ==> "+ (n == 0 ? "OK" : "FAILED") );
        assert n == 0 : "Tfd.verifySignature failed";

        System.out.println("\nADD A TFD ELEMENT TO A SIGNED CFDI DOCUMENT USING PAC KEY:");
        fname = "Prueba2Notario_iedu-signed.xml";
        newname = "Prueba2Notario_iedu-tfd.xml";
        certfile = "pac.cer";
        keyfile = "pac.key";
        password = "12345678a";
        n = Tfd.addSignedTfd(newname, fname, keyfile, password, certfile);
        System.out.println("Tfd.addSignedTfd('"+ fname +"'-->'"+ newname +"') returns "+ n );
        assert n == 0 : "Tfd.addSignedTfd failed";
        // Did we make a valid XML file?
        n = Sat.validateXml(newname);
        System.out.println("Sat.validateXml('"+ newname +"') returns "+ n +" ==> "+ (n == 0 ? "OK" : "FAILED") );
        assert n == 0 : "Sat.validateXml failed";
        // Does it have a valid selloSAT in the TFD?
        n = Tfd.verifySignature(newname, certfile);
        System.out.println("Tfd.verifySignature('"+ newname +"') returns "+ n +" ==> "+ (n == 0 ? "OK" : "FAILED") );
        // Extract digests from the two signatures: `sello` and `selloSAT`
        digest = Sat.extractDigestFromSignature(newname);
        assert digest.length() > 0;
        System.out.println("DIGEST(sello, '"+ newname +"')=\n"+ digest );
        // Note that the TFD selloSAT will be different each time it is created
        digest = Tfd.extractDigestFromSignature(newname, certfile);
        assert digest.length() > 0;
        System.out.println("DIGEST(selloSAT, '"+ newname +"')=\n"+ digest );
                
        System.out.println("\nSIGN XML FROM BYTES TO STRING:");
        // These strings are from `emisor-pem.cer` and `emisor-pem.key`, respectively
        certfile =
            "-----BEGIN CERTIFICATE-----" +
            "MIIEdDCCA1ygAwIBAgIUMjAwMDEwMDAwMDAxMDAwMDU4NjcwDQYJKoZIhvcNAQEF" +
            "BQAwggFvMRgwFgYDVQQDDA9BLkMuIGRlIHBydWViYXMxLzAtBgNVBAoMJlNlcnZp" +
            "Y2lvIGRlIEFkbWluaXN0cmFjacOzbiBUcmlidXRhcmlhMTgwNgYDVQQLDC9BZG1p" +
            "bmlzdHJhY2nDs24gZGUgU2VndXJpZGFkIGRlIGxhIEluZm9ybWFjacOzbjEpMCcG" +
            "CSqGSIb3DQEJARYaYXNpc25ldEBwcnVlYmFzLnNhdC5nb2IubXgxJjAkBgNVBAkM" +
            "HUF2LiBIaWRhbGdvIDc3LCBDb2wuIEd1ZXJyZXJvMQ4wDAYDVQQRDAUwNjMwMDEL" +
            "MAkGA1UEBhMCTVgxGTAXBgNVBAgMEERpc3RyaXRvIEZlZGVyYWwxEjAQBgNVBAcM" +
            "CUNveW9hY8OhbjEVMBMGA1UELRMMU0FUOTcwNzAxTk4zMTIwMAYJKoZIhvcNAQkC" +
            "DCNSZXNwb25zYWJsZTogSMOpY3RvciBPcm5lbGFzIEFyY2lnYTAeFw0xMjA3Mjcx" +
            "NzAyMDBaFw0xNjA3MjcxNzAyMDBaMIHbMSkwJwYDVQQDEyBBQ0NFTSBTRVJWSUNJ" +
            "T1MgRU1QUkVTQVJJQUxFUyBTQzEpMCcGA1UEKRMgQUNDRU0gU0VSVklDSU9TIEVN" +
            "UFJFU0FSSUFMRVMgU0MxKTAnBgNVBAoTIEFDQ0VNIFNFUlZJQ0lPUyBFTVBSRVNB" +
            "UklBTEVTIFNDMSUwIwYDVQQtExxBQUEwMTAxMDFBQUEgLyBIRUdUNzYxMDAzNFMy" +
            "MR4wHAYDVQQFExUgLyBIRUdUNzYxMDAzTURGUk5OMDkxETAPBgNVBAsTCFVuaWRh" +
            "ZCAxMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2TTQSPONBOVxpXv9wLYo8" +
            "jezBrb34i/tLx8jGdtyy27BcesOav2c1NS/Gdv10u9SkWtwdy34uRAVe7H0a3VMR" +
            "LHAkvp2qMCHaZc4T8k47Jtb9wrOEh/XFS8LgT4y5OQYo6civfXXdlvxWU/gdM/e6" +
            "I2lg6FGorP8H4GPAJ/qCNwIDAQABox0wGzAMBgNVHRMBAf8EAjAAMAsGA1UdDwQE" +
            "AwIGwDANBgkqhkiG9w0BAQUFAAOCAQEATxMecTpMbdhSHo6KVUg4QVF4Op2IBhiM" +
            "aOrtrXBdJgzGotUFcJgdBCMjtTZXSlq1S4DG1jr8p4NzQlzxsdTxaB8nSKJ4KEMg" +
            "IT7E62xRUj15jI49qFz7f2uMttZLNThipunsN/NF1XtvESMTDwQFvas/Ugig6qwE" +
            "fSZc0MDxMpKLEkEePmQwtZD+zXFSMVa6hmOu4M+FzGiRXbj4YJXn9Myjd8xbL/c+" +
            "9UIcrYoZskxDvMxc6/6M3rNNDY3OFhBK+V/sPMzWWGt8S1yjmtPfXgFs1t65AZ2h" +
            "cTwTAuHrKwDatJ1ZPfa482ZBROAAX1waz7WwXp0gso7sDCm2/yUVww==" +
            "-----END CERTIFICATE-----";
        keyfile =
            "-----BEGIN ENCRYPTED PRIVATE KEY-----" +
            "MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI3V0iJrMlAI0CAggA" +
            "MBQGCCqGSIb3DQMHBAgmAnxnceS9FgSCAoBeM47Z7bIErpBCcTTUqChTzglQ2Om+" +
            "Nw4Nv0YZZkw8T0iLknP5J+p0EJHDQzATqHC1VZmG8+S23yJWSpYKzikQ0EdQCg3y" +
            "pl6QP55wdZ6DmkJQPo4cE+Z9elLT4QDRH//bJbZlnUtwKlu0ldMFNlBdAz/vYM4C" +
            "mad/cYIaR2tEJrJQvOiT4Z+c5Thf/3kLr5ohtVi7IDCFrIuSk0y0994rW4yGXQT7" +
            "/licNrOSDnBIWYP1poYa5YBFw6KMqA+uC7xwu6XGpdMJ3pyaogZUlw0MwL0mnWNI" +
            "DUkKm0lBrM0bEXZJgVbMzBokUupOMq6RAIOb6NXRREAw6mpyiekMNUE2ajXB2Xlj" +
            "1O12LqBEPMfdjeeQ1oakUuAfrTKiFIREcSW8472Xcmzu40u+zTCJVefpgNb6FF6V" +
            "L3QH3LZ+F2+A92rXn1gZETlHn9MjMfZyh7NUUbZ+BCpq0U2lyARbTZ2T+LvHP1gk" +
            "2oEDB/X+otXuK0cMxj8yG4tc5g7w7xo8GCMAu8zmKQa4pNd5+vND4JgwIaSWC5yW" +
            "EB0/E5qszMij2l1Ibni3Uj9HB8baf0ZU9hMahP4hjF1hFb4HfRxcmYOGyfldMCnr" +
            "1zQ0IZXCS5ki/xdrvxgkj6CEl9dXutPBrbg/mIPJLIkJPGQ2T78tlwvFl5C5i9U9" +
            "sOha2UmVXtDwg0zwWqLgS/6oQNYouQQlFbeH3jEYgHGENqunsIb0Nt4HBSQq6NgH" +
            "XGK7WRaLeDPIDr7j85cBdycuKXPH4Vtb8qx9dH4veKKz6ymbiHtXY63+3J4Geh6t" +
            "IzLduuX0CBiLob0R+gorwUK28i6bS373/d/GQoWOdBmSSws2BaHEGBmU" +
            "-----END ENCRYPTED PRIVATE KEY-----";
        password = "12345678a";
        // Read in a UTF-8-encoded XML file into a byte array
        fname = "ejemplo_v32-base2012.xml";
        xmlArr = readABinaryFile(fname);
        System.out.println("File '"+ fname +"'-->"+ xmlArr.length +" UTF-8 bytes");
        // Sign it and receive result back as a byte array...
        b = Sat.signXmlBytesToBytes(xmlArr, keyfile, password, certfile, 0);
        if (b.length == 0) {
            System.out.println("ERROR="+ General.lastError() );
        }
        else {  // Put in a String and print it...
            try {
                s = new String(b, "UTF-8");
            } catch (UnsupportedEncodingException ex) {
                throw new AssertionError(ex);
            }
            System.out.println("XML-OUT=\n---\n"+ s +"\n---\n");
        }
        assert b.length > 0 : "Sat.signXmlBytesToString failed";
               
        System.out.println("\nSIGN XML FROM BYTES TO STRING USING EMPTY-ELEMENT TAGS:");
        // Sign it and receive result back as a String...
        b = Sat.signXmlBytesToBytes(xmlArr, keyfile, password, certfile, Sat.SIGN_USE_EMPTY_ELEMENTS);
        if (b.length == 0) {
            System.out.println("ERROR="+ General.lastError() );
        }
        else {  // Put in a String and print it...
            try {
                s = new String(b, "UTF-8");
            } catch (UnsupportedEncodingException ex) {
                throw new AssertionError(ex);
            }
            System.out.println("XML-OUT(UseEmptyElements)=\n---\n"+ s +"\n---\n");
        }
        assert b.length > 0 : "Sat.signXmlBytesToString(UseEmptyElements) failed";
        
        System.out.println("\nGENERATE 3 FRESH UUIDs:");
        s = Sat.uuid();
        System.out.println("Sat.uuid()=" + s);
        assert s.length() > 0 : "Sat.uuid failed";
        System.out.println("Sat.uuid()=" + Sat.uuid());
        System.out.println("Sat.uuid()=" + Sat.uuid());

        System.out.println("\nPASS XML DATA AS A STRING:");
        // Most methods will accept the XML data in a String instead of a filename, *but* ...
        System.out.println("Non-US-ASCII characters in String *must* be written as XML entities when XML encoding is UTF-8");
        // This test XML is not a valid CFDi document. It's just a short string showing how to pass XML data 
        // that happens to work with getXmlAttribute() and xmlReceiptVersion().
        xmlStr = "<?xml version='1.0' encoding='UTF-8'?><Comprobante version='3.2' pais='M&#xC9;XICO' />";
        System.out.println("Dummy test XML string=["+ xmlStr +"]");
        n = Sat.xmlReceiptVersion(xmlStr);
        if (n == 0)
            System.out.println("ERROR="+ General.lastError() );
        else
            System.out.println("xmlReceiptVersion()="+ n );
        assert n == 32 : "Sat.xmlReceiptVersion(xmlStr) failed";
        
        s = Sat.getXmlAttribute(xmlStr, "pais", "Comprobante");
        if (s.length() == 0)
            System.out.println("ERROR="+ General.lastError() );
        else
            System.out.println("getXmlAttribute(Combrobante/@pais)="+ s );
        assert s.length() > 0 : "Sat.getXmlAttribute(xmlStr) failed";
               
        // New in [v6.0]
        System.out.println("\nWORK WITH A `RETENCIONES` DOCUMENT:");
        fname = "Ejemplo_Retenciones-base.xml";
        System.out.println("FILE="+ fname );
        n = Sat.xmlReceiptVersion(fname);
        System.out.println("Sat.xmlReceiptVersion() returns "+ n +" (expecting 1010)");
        assert 1010 == n : "Sat.xmlReceiptVersion failed";
        s = Sat.makeDigestFromXml(fname);
        System.out.println("Sat.makeDigestFromXml() -> "+ s );
        assert s.length() > 0 : "Sat.makeDigestFromXml failed";
        // Use new [v6.0] option to find name of root element
        s = Sat.getXmlAttribute(fname, "", "");
        System.out.println("File root element is '"+ s +"'");
        assert s.length() > 0 : "Sat.getXmlAttribute('','') failed";

        // New in [v7.0]
        System.out.println("\nWORK WITH `CONTABILIDAD` DOCUMENTS:");
        fname = "AAA010101AAA201501CT-base.xml";
        System.out.println("CATALOGOCUENTAS FILE="+ fname );
        n = Sat.xmlReceiptVersion(fname);
        System.out.println("Sat.xmlReceiptVersion() returns ID="+ n +" (expecting 2011)");
        assert 2011 == n : "Sat.xmlReceiptVersion failed";
        System.out.println("SIGN A CATALOGOCUENTAS DOCUMENT...");
        newname = "AAA010101AAA201501CT.xml";
        keyfile = "emisor.key";
        certfile = "emisor.cer";
        password = "12345678a";
        n = Sat.signXml(newname, fname, keyfile, password, certfile);
        System.out.println("Sat.signXml() returns "+ n +" (expecting 0)");
        assert 0 == n : "Sat.signXml failed";
        n = Sat.verifySignature(newname);
        System.out.println("Sat.verifySignature() returns "+ n +" (expecting 0)");
        assert 0 == n : "Sat.verifySignature failed";
        
        fname = "AAA010101AAA201501BN-base.xml";
        System.out.println("BALANZA FILE="+ fname );
        n = Sat.xmlReceiptVersion(fname);
        System.out.println("Sat.xmlReceiptVersion() returns ID="+ n +" (expecting 2111)");
        assert 2111 == n : "Sat.xmlReceiptVersion failed";
        System.out.println("MAKE THE SIGNATURE STRING FOR BALANZA...");
        s = Sat.makeSignatureFromXml(fname, keyfile, password);
        System.out.println("Sat.makeSignatureFromXml() ->\n"+ s );
        assert s.length() > 0 : "Sat.makeSignatureFromXml() failed";
        
        fname = "contab-SelloDigitalContElec-signed.xml";
        System.out.println("SELLODIGITALCONTELEC FILE="+ fname );
        n = Sat.xmlReceiptVersion(fname);
        System.out.println("Sat.xmlReceiptVersion() returns ID="+ n +" (expecting 2511)");
        assert 2511 == n : "Sat.xmlReceiptVersion failed";
        System.out.println("VERIFY SIGNATURE FOR SELLODIGITALCONTELEC USING PAC CERTIFICATE...");
        n = Sat.verifySignature(fname, "pac.cer");
        System.out.println("Sat.verifySignature() returns "+ n +" (expecting 0)");
        assert 0 == n : "Sat.verifySignature failed";
        
        System.out.println("\nWORK WITH `CONTROLESVOLUMETRICOS` DOCUMENT:");
        fname = "ConVolE12345-base.xml";
        System.out.println("FILE="+ fname );
        n = Sat.xmlReceiptVersion(fname);
        System.out.println("Sat.xmlReceiptVersion() returns ID="+ n +" (expecting 4011)");
        assert 4011 == n : "Sat.xmlReceiptVersion failed";
        
        System.out.println("SIGN A CONVOL DOCUMENT WITH BIGFILE FLAG...");
        newname = "ConVolE12345-signed.xml";
        // Use key and cert provided for ConVol tests
        keyfile = "CSD_E12345CV_ACP020530MP5.key";
        certfile = "CSD_E12345CV_ACP020530MP5.cer";
        password = "12345678a";
        n = Sat.signXml(newname, fname, keyfile, password, certfile, Sat.SIGN_BIGFILE);
        System.out.println("Sat.signXmlEx(BigFile) returns "+ n +" (expecting 0)");
        assert 0 == n : "Sat.signXmlEx(BigFile) failed";
        n = Sat.verifySignature(newname);
        System.out.println("Sat.verifySignature() returns "+ n +" (expecting 0)");
        assert 0 == n : "Sat.verifySignature failed";

        
        // ******************************************
        // FINALLY, SHOW CURRENT VERSION FOR CORE DLL
        System.out.println("\nFirmaSAT DLL Version="+ General.version() +" ["+ General.compileTime() +"]");
        

    }
 
//**********************************    
// INTERNAL METHODS USED LOCALLY...  
//**********************************    

    /** Return TRUE if the file is present else print message and return FALSE */
    private static boolean fileIsNotPresent(String filePath, String message) {
        File f = new File(filePath);
        if (!f.exists()) {
            System.out.println("\n"+ message +": "+ filePath );
            return true;
        }
        return false;
    }
    
    /** Returns the length of a file in bytes or -1 if not found */
    private static long fileLength(String filePath) {
        File f = new File(filePath);
        if (!f.exists()) {
            return -1;
        }
        return f.length();
    }
    
    /** Reads a binary file into a byte array */
    private static byte[] readABinaryFile(String fileName) {
        byte[] b = new byte[0];
        File file = new File(fileName);
        if (file.exists()) {
            b = new byte[(int) file.length()];
            try {
                FileInputStream f = new FileInputStream(file);
                f.read(b);
                f.close();
            }
            catch (FileNotFoundException ex) {
                System.out.println("Cannot find file '" + fileName + "'");
                ex.printStackTrace();
            }
            catch (IOException ex) {
                System.out.println("Error reading from file '" + fileName + "'");
                ex.printStackTrace();
            }
        }
        return b;
    }
     
    /** Returns TRUE if file has a UTF-8 Byte Order Mark */
    private static boolean FileHasBom(String fileName) {
        final int kNBYTES = 3;
        byte[] buf = new byte[kNBYTES];
        File file = new File(fileName);
        try {
            FileInputStream f = new FileInputStream(file);
            f.read(buf);
            f.close();
        }
        catch (FileNotFoundException ex) {
            System.out.println("Cannot find file '" + fileName + "'");
            ex.printStackTrace();
        }
        catch (IOException ex) {
            System.out.println("Error reading from file '" + fileName + "'");
            ex.printStackTrace();
        }
        /* BOM consists of three bytes (0xEF, 0xBB, 0xBF) [NB (byte) cast here!] */
        return (buf[0] == (byte)0xEF && buf[1] == (byte)0xBB && buf[2] == (byte)0xBF);
    }
    
//    /** Display all the chars in a string in hex format */
//    private static void displayChars(String s) {
//        char[] chArr = s.toCharArray();
//        for (int i = 0; i < chArr.length; i++) {
//            System.out.printf("%02x ", (byte)chArr[i]);
//        }
//        System.out.println("");
//    }
  
}