using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using FirmaSAT;
/*
* $Id: TestFirmaSat.cs $
* Last updated:
* $Date: 2020-08-05 17:01 $
* $Version: 9.2.0 $
*/
/* Some tests using the FirmaSAT .NET interface.
*
* Requires `FirmaSAT` to be installed on your system: available from <https://cryptosys.net/firmasat/>.
* Add a reference to `diFirmaSatNet.dll` installed in `C:\Program Files (x86)\FirmaSAT`.
*
* Test files are in `FirmaSATtestfiles.zip`. These must be in the CWD.
*
* This is a Console Application written for target .NET Framework 2.0 and above.
* Please report any bugs to <https://cryptosys.net/contact>.
*/
/******************************* LICENSE ***********************************
* Copyright (C) 2010-20 David Ireland, DI Management Services Pty Limited.
* All rights reserved. <https://di-mgt.com.au> <https://cryptosys.net>
* The code in this module is licensed under the terms of the MIT license.
* For a copy, see <http://opensource.org/licenses/MIT>
****************************************************************************
*/
/* [2020-07] Re-organised into modular form */
/* NOTE: Requires certain files to exist in the current working directory
* (by default, the same directory as the executable).
* See `arrFileNames` below - these files are in `FirmaSATtestfiles.zip`
*/
namespace TestFirmaSAT
{
/// <summary>
/// Test examples for FirmaSAT .NET interface
/// </summary>
class TestFirmaSAT
{
private const int MIN_FSA_VERSION = 90200;
// Test files required to exist in the current working directory:
private static string[] arrFileNames = new string[]
{
"A7.xml",
"AAA010101AAA201501BN-base.xml",
"AAA010101AAA201501CT-base.xml",
"AAA010101AAA201705CT.xml",
"AC4_SAT.cer",
"cfdv33a-base.xml",
"cfdv33a-base-nocertnum.xml",
"cfdv33a-bad-nover.xml",
"cfdv33a-cce11-min.xml",
"cfdv33a-cce11.xml",
"cfdv33a-detallista-min.xml",
"cfdv33a-detallista.xml",
"cfdv33a-min.xml",
"cfdv33a-nomina12.xml",
"cfdv33a-nomina12B.xml",
"cfdv33a-pagos10-min.xml",
"cfdv33a-pagos10.xml",
"cfdv33a-signed-tfd.xml",
"cfdv33a-signed.xml",
"contab-SelloDigitalContElec-signed.xml",
"ConVolE12345-base.xml",
"ConVolE12345-signed2015.xml",
"CSD_E12345CV_ACP020530MP5.cer",
"CSD_E12345CV_ACP020530MP5.key",
"Ejemplo_Retenciones-base.xml",
"Ejemplo_Retenciones-signed-tfd.xml",
"ejemplo_v32-tfd2015.xml",
"emisor-pem.cer",
"emisor-pem.key",
"emisor.cer",
"emisor.key",
"emisor1024.cer",
"emisor1024.key",
"pac.cer",
"pac.key",
"pac1024.cer",
"pac1024.key",
"V3_2_BadCurp.xml",
};
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
// Make sure minimum required version of FirmaSAT is installed...
Console.WriteLine("FirmaSAT core Version is " + General.Version());
if (General.Version() < MIN_FSA_VERSION) {
Console.WriteLine("FATAL ERROR: Require FirmaSAT core version " + MIN_FSA_VERSION + " or later.");
return;
}
// 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;
}
/* Handle command-line arguments
* [some] just do some tests (default = do all)
*/
bool doSome = false;
for (int iarg = 0; iarg < args.Length; iarg++) {
if (args[iarg] == "some")
doSome = true;
}
//*************
// DO THE TESTS
//*************
if (doSome) // Use "some" in the command line
{ // Do some tests - comment these out as required
Console.WriteLine("Just doing some tests...");
//test_General();
//test_ErrorLookup();
//test_pipestring();
//test_SignAndVerify();
//test_Digest();
//test_Validate();
//test_ExtractAttribute();
//test_X509Certificate();
//test_MakeSignature();
//test_Detallista();
//test_CertValidity();
//test_CheckKeyAndCert();
//test_ReceiptVersion();
//test_Tfd();
//test_BOM();
//test_ExtractConsecutive();
//test_PrivateKey();
//test_QueryCert();
//test_SignInMemory();
//test_UUID();
//test_Retenciones();
//test_Contabilidad();
//test_ConVol();
//test_InsertCert();
//test_GetXmlAttribute_Advanced();
test_XMLasUnicodeString();
}
else {
// Do all the test modules (default)
Console.WriteLine("Doing all tests...");
DoAllTests();
}
// ******************************************
// FINALLY, DISPLAY QUICK INFO ABOUT THE CORE DLL
Console.WriteLine("\nDETAILS OF CORE DLL...");
Console.WriteLine("DLL Version={0} [{1}] Lic={2} Compiled=[{3}] ",
General.Version(), General.Platform(), General.LicenceType(), General.CompileTime());
Console.WriteLine("[{0}]", General.ModuleName());
//********************************************
Console.WriteLine("\nALL TESTS COMPLETED.");
}
static void DoAllTests()
{
test_General();
test_ErrorLookup();
test_pipestring();
test_SignAndVerify();
test_Digest();
test_Validate();
test_ExtractAttribute();
test_X509Certificate();
test_MakeSignature();
test_Detallista();
test_CertValidity();
test_CheckKeyAndCert();
test_ReceiptVersion();
test_Tfd();
test_BOM();
test_ConsecutiveElements();
test_PrivateKey();
test_QueryCert();
test_SignInMemory();
test_UUID();
test_Retenciones();
test_Contabilidad();
test_ConVol();
test_InsertCert();
test_GetXmlAttribute_Advanced();
test_XMLasUnicodeString();
}
//********************
// THE TEST MODULES...
//********************
static void test_General()
{
int n;
char ch;
string s;
//****************
// GENERAL TESTS *
//****************
Console.WriteLine("\nINTERROGATE THE CORE DIFIRMASAT DLL:");
n = FirmaSAT.General.Version();
Console.WriteLine("Version={0}", n);
s = General.CompileTime();
Console.WriteLine("CompileTime={0}", s);
s = General.ModuleName();
Console.WriteLine("ModuleName={0}", s);
s = General.Platform();
Console.WriteLine("Platform={0}", s);
ch = General.LicenceType();
Console.WriteLine("LicenceType={0}", ch);
s = General.Comments();
Console.WriteLine("Comments={0}", s);
}
static void test_ErrorLookup()
{
string s;
//*****************
// ERROR LOOKUP TESTS *
//*********************
Console.WriteLine("\nERROR CODES:");
for (int i = 0; i < 10000; i++) {
s = General.ErrorLookup(i);
if (!s.Equals(String.Empty))
Console.WriteLine("{0}={1}", i, s);
}
}
static void test_pipestring()
{
string s;
string fname;
Console.WriteLine("\nFORM THE PIPESTRING FROM AN XML FILE:");
fname = "cfdv33a-base.xml";
s = Sat.MakePipeStringFromXml(fname);
Console.WriteLine("Sat.MakePipeStringFromXml('{0}')=\n{1}", fname, s);
Debug.Assert(s.Length > 0, "Sat.MakePipeStringFromXml failed");
}
static void test_SignAndVerify()
{
int n;
string fname;
string newname, keyfile, password, certfile;
Console.WriteLine("\nSIGN AN XML FILE:");
fname = "cfdv33a-base.xml";
newname = "cfdv33a_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);
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 = "cfdv33a-signed-tfd.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");
}
static void test_Digest()
{
string s;
string fname;
Console.WriteLine("\nFORM THE DIGEST OF THE PIPESTRING IN AN XML FILE:");
fname = "cfdv33a-signed-tfd.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 = "cfdv33a-signed-tfd.xml";
s = Sat.ExtractDigestFromSignature(fname);
Console.WriteLine("Sat.ExtractDigestFromSignature('{0}')=\n{1}", fname, s);
Debug.Assert(s.Length > 0, "Sat.ExtractDigestFromSignature failed");
}
static void test_Validate()
{
int n;
string s;
string fname;
Console.WriteLine("\nTRY VALIDATING XML FILES:");
Console.WriteLine("1. A valid one:");
fname = "cfdv33a-signed-tfd.xml";
n = Sat.ValidateXml(fname);
Console.WriteLine("Sat.ValidateXml('{0}') returns {1} (expecting 0)", fname, n);
Debug.Assert(n == 0, "Sat.ValidateXml failed");
Console.WriteLine("2. An invalid one (missing version):");
fname = "cfdv33a-bad-nover.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));
Debug.Assert(n != 0, "Sat.ValidateXml should have failed");
Console.WriteLine("\nVALIDATE XML WITH STRICT AND LOOSE OPTIONS:");
Console.WriteLine("Default strict behaviour (Bad attribute..@CURP..is too long):");
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");
}
static void test_ExtractAttribute()
{
string s;
string fname;
string attributeName, elementName;
Console.WriteLine("\nEXTRACT AN ATTRIBUTE FROM AN XML FILE:");
fname = "cfdv33a-signed-tfd.xml";
elementName = "Comprobante";
attributeName = "Sello"; // NB capital 'S' for v3.3
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 = "Fecha";
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("\nEXTRACT AN ATTRIBUTE WITH ACCENTED CHARACTERS:");
fname = "cfdv33a-base.xml";
elementName = "cfdi:Emisor";
attributeName = "Nombre";
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");
Console.WriteLine("\nEXTRACT AN ATTRIBUTE WITH ACCENTED CHARACTERS IN ITS NAME:");
fname = "cfdv33a-nomina12.xml";
elementName = "nomina12:CompensacionSaldosAFavor";
attributeName = "Año";
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");
}
static void test_X509Certificate()
{
string s;
string s1;
string fname;
Console.WriteLine("\nGET DETAILS OF X.509 CERTIFICATE:");
Console.WriteLine("1. From embedded `certificado` in XML");
fname = "cfdv33a-signed-tfd.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 = "emisor.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 = "emisor.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 = "cfdv33a-signed-tfd.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");
}
static void test_MakeSignature()
{
string s;
string fname;
string keyfile, password;
Console.WriteLine("\nMAKE A SIGNATURE FROM A BASE XML FILE:");
fname = "cfdv33a-base.xml";
keyfile = "emisor.key";
password = "12345678a"; /* 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");
}
static void test_Detallista()
{
int n;
string s;
string fname;
string keyfile, password;
string newname, certfile;
string attributeName, elementName;
Console.WriteLine("\nSIGN A DETALLISTA XML FILE:");
fname = "cfdv33a-detallista.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);
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 = "cfdv33a-detallista.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");
}
static void test_CertValidity()
{
string s;
string fname;
Console.WriteLine("\nGET VALIDITY DETAILS OF X.509 CERTIFICATE:");
Console.WriteLine("1. From embedded `certificado` in XML");
fname = "cfdv33a-signed-tfd.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 = "emisor.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");
}
static void test_CheckKeyAndCert()
{
int n;
string keyfile, password, certfile;
Console.WriteLine("\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);
Console.WriteLine("Sat.CheckKeyAndCert({0},{1}) = {2}", keyfile, certfile, n);
Debug.Assert(n == 0, "Sat.CheckKeyAndCert failed");
certfile = "pac.cer";
keyfile = "pac.key";
password = "12345678a";
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 = "cfdv33a-signed-tfd.xml";
keyfile = "emisor.key";
password = "12345678a";
n = Sat.CheckKeyAndCert(keyfile, password, certfile);
Console.WriteLine("Sat.CheckKeyAndCert({0},{1}) = {2}", keyfile, certfile, n);
Debug.Assert(n == 0, "Sat.CheckKeyAndCert failed");
}
static void test_ReceiptVersion()
{
int n;
string fname;
Console.WriteLine("\nFIND THE COMPROBANTE VERSION OF AN XML FILE:");
fname = "cfdv33a-base.xml.";
n = Sat.XmlReceiptVersion(fname);
Console.WriteLine("Sat.XmlReceiptVersion('{0}') = {1}", fname, n);
Debug.Assert(n == 33, "Sat.XmlReceiptVersion failed");
// Older version...
Console.WriteLine("Legacy versions still work...");
fname = "ejemplo_v32-tfd2015.xml";
n = Sat.XmlReceiptVersion(fname);
Console.WriteLine("Sat.XmlReceiptVersion('{0}') = {1}", fname, n);
Debug.Assert(n == 32, "Sat.XmlReceiptVersion failed");
}
static void test_Tfd()
{
int n;
string s;
string fname;
string newname, keyfile, password, certfile;
string s1;
Console.WriteLine("\nCREATE CADENA ORIGINAL DEL TIMBRE FISCAL DIGITAL (PIPESTRING FOR TFD):");
fname = "cfdv33a-signed-tfd.xml";
certfile = "pac.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
// so create a TFD signature string we could paste into the `selloSAT' node
fname = "cfdv33a-signed-tfd.xml";
certfile = "pac.cer";
keyfile = "pac.key";
password = "12345678a";
s = Tfd.MakeSignatureFromXml(fname, keyfile, password);
Console.WriteLine("Tfd.MakeSignatureFromXml('{0}')=\n{1}", fname, s);
s1 = Sat.GetXmlAttribute(fname, "SelloSAT", "TimbreFiscalDigital"); // NB Capital 'S' in Sello for TFD v1.1
Console.WriteLine("Correct=\n{0}", s1);
Debug.Assert(String.Compare(s, s1, true) == 0, "selloSAT values do not match");
Console.WriteLine("\nADD A TFD ELEMENT TO A SIGNED CFDI DOCUMENT USING PAC KEY:");
fname = "cfdv33a-signed.xml";
newname = "cfdv33a_new-tfd.xml";
certfile = "pac.cer";
keyfile = "pac.key";
password = "12345678a";
n = Tfd.AddSignedTfd(newname, fname, keyfile, password, certfile);
Console.WriteLine("Tfd.AddSignedTfd('{0}') returns {1} (expected 0)\n", newname, n);
Debug.Assert(0 == n, "Tfd.AddSignedTfd failed");
// Did we make a valid XML file?
n = Sat.ValidateXml(newname);
Console.WriteLine("Sat.ValidateXml('{0}') returns {1} (expected 0)\n", newname, n);
Debug.Assert(0 == n, "Sat.ValidateXml failed");
Console.WriteLine("\nVERIFY SIGNATURE IN TFD SELLOSAT:");
n = Tfd.VerifySignature(newname, certfile);
Console.WriteLine("Tfd.VerifySignature({0},{1})={2} (expected 0)", newname, certfile, n);
Debug.Assert(n == 0, "Tfd.VerifySignature failed");
}
static void test_BOM()
{
int n;
string fname;
string newname;
Console.WriteLine("\nADD UTF-8 BOM TO EXISTING FILE:");
fname = "cfdv33a-signed-nobom.xml";
Console.WriteLine("FileHasBom('{0}')={1} ", fname, FileHasBom(fname));
newname = "cfdv33a_new-signed-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");
Console.WriteLine("FileHasBom('{0}')={1} ", newname, FileHasBom(newname));
}
static void test_ConsecutiveElements()
{
string s;
string fname;
string attributeName, elementName;
Console.WriteLine("\nEXTRACT ATTRIBUTES FROM CONSECUTIVE ELEMENTS:");
fname = "ejemplo_v32-tfd2015.xml";
attributeName = "descripcion";
elementName = "cfdi:Concepto";
Console.WriteLine("For file '{0}'", fname);
string eName = null;
for (int i = 1; i <= 10; i++) {
eName = elementName + string.Format("[{0:d}]", i);
Console.Write("Sat.GetXmlAttribute({0},{1})=", attributeName, eName);
s = Sat.GetXmlAttribute(fname, attributeName, eName);
if (Sat.XmlNoMatch() == s) { // [2020-07-06] Changed from empty string
break;
}
Console.WriteLine("{0}", s);
}
Console.WriteLine();
}
static void test_PrivateKey()
{
int n;
string s;
string fname;
string keyfile, password, certfile;
string newname;
string newpassword;
Console.WriteLine("\nGET PRIVATE KEY AS BASE64:");
fname = "emisor.key";
password = "12345678a";
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");
Console.WriteLine("\nWRITE PFX FROM PRIVATE KEY AND CERT:");
certfile = "emisor.cer";
keyfile = "emisor.key";
password = "12345678a";
fname = "archivo_new-pfx.txt";
newpassword = "clavedesalida";
n = Sat.WritePfxFile(fname, newpassword, keyfile, password, certfile);
Console.WriteLine("Sat.WritePfxFile()->{0} returns {1}", fname, n);
Debug.Assert(n == 0, "Sat.WritePfxFile failed");
Console.WriteLine("New PFX file is {0} bytes long.", FileLength(fname));
Console.WriteLine("\nSAVE KEYFILE WITH NEW PASSWORD...");
keyfile = "emisor.key";
password = "12345678a";
newname = "emisor_new.key";
newpassword = "password123";
n = Sat.NewKeyFile(newname, newpassword, keyfile, password, 0);
Console.WriteLine("Sat.NewKeyFile() returns {0} (expecting 0)", n);
Debug.Assert(0 == n, "Sat.NewKeyFile failed");
Console.WriteLine("Created new key file '{0}' of length {1} bytes with password '{2}'.", newname, FileLength(newname), newpassword);
Console.WriteLine("Save again in PEM format...");
newname = "emisor_new.pem";
newpassword = "password456";
n = Sat.NewKeyFile(newname, newpassword, keyfile, password, KeyFormat.PEM);
Console.WriteLine("Sat.NewKeyFile(KeyFormat.PEM) returns {0} (expecting 0)", n);
Debug.Assert(0 == n, "Sat.NewKeyFile failed");
Console.WriteLine("Created new key file '{0}' of length {1} bytes with password '{2}'.", newname, FileLength(newname), newpassword);
}
static void test_QueryCert()
{
string s;
string fname;
Console.WriteLine("\nGET RFC AND ORG NAME FROM CERT:");
fname = "emisor.cer";
Console.WriteLine("FILE: {0}", fname);
s = Sat.QueryCert(fname, Query.rfc);
Console.WriteLine("Sat.QueryCert(rfc)={0}", s);
Debug.Assert(s.Length > 0, "Sat.QueryCert(rfc) failed");
s = Sat.QueryCert(fname, Query.organizationName);
Console.WriteLine("Sat.QueryCert(organizationName)='{0}'", s);
Debug.Assert(s.Length > 0, "Sat.QueryCert(organizationName) failed");
fname = "cfdv33a-signed-tfd.xml";
Console.WriteLine("FILE: {0}", fname);
s = Sat.QueryCert(fname, Query.rfc);
Console.WriteLine("Sat.QueryCert(rfc)={0}", s);
Debug.Assert(s.Length > 0, "Sat.QueryCert(rfc) failed");
s = Sat.QueryCert(fname, Query.organizationName);
Console.WriteLine("Sat.QueryCert(organizationName)='{0}'", s);
Debug.Assert(s.Length > 0, "Sat.QueryCert(organizationName) failed");
Console.WriteLine("\nTEST OTHER QUERIES FOR CERT:");
fname = "emisor.cer";
Console.WriteLine("FILE: {0}", fname);
s = Sat.QueryCert(fname, Query.notBefore);
Console.WriteLine("Sat.QueryCert(notBefore)={0}", s);
Debug.Assert(s.Length > 0, "Sat.QueryCert(notBefore) failed");
s = Sat.QueryCert(fname, Query.notAfter);
Console.WriteLine("Sat.QueryCert(notAfter)={0}", s);
Debug.Assert(s.Length > 0, "Sat.QueryCert(notAfter) failed");
s = Sat.QueryCert(fname, Query.serialNumber);
Console.WriteLine("Sat.QueryCert(serialNumber)={0}", s);
Debug.Assert(s.Length > 0, "Sat.QueryCert(serialNumber) failed");
Console.WriteLine("\nQUERY KEY SIZE OF CERTIFICATES...");
fname = "emisor1024.cer";
s = Sat.QueryCert(fname, Query.keySize);
Console.WriteLine("Sat.QueryCert('{0}',keySize)={1}", fname, s);
Debug.Assert(s.Length > 0, "Sat.QueryCert(keySize) failed");
fname = "emisor.cer";
s = Sat.QueryCert(fname, Query.keySize);
Console.WriteLine("Sat.QueryCert('{0}',keySize)={1}", fname, s);
Debug.Assert(s.Length > 0, "Sat.QueryCert(keySize) failed");
fname = "AC4_SAT.cer";
s = Sat.QueryCert(fname, Query.keySize);
Console.WriteLine("Sat.QueryCert('{0}',keySize)={1}", fname, s);
Debug.Assert(s.Length > 0, "Sat.QueryCert(keySize) failed");
Console.WriteLine("\nQUERY SIGNATURE ALGORITHM IN CERTIFICATES...");
fname = "emisor1024.cer";
s = Sat.QueryCert(fname, Query.sigAlg);
Console.WriteLine("Sat.QueryCert('{0}',sigAlg)={1}", fname, s);
Debug.Assert(s.Length > 0, "Sat.QueryCert(sigAlg) failed");
fname = "emisor.cer";
s = Sat.QueryCert(fname, Query.sigAlg);
Console.WriteLine("Sat.QueryCert('{0}',sigAlg)={1}", fname, s);
Debug.Assert(s.Length > 0, "Sat.QueryCert(sigAlg) failed");
}
static void test_SignInMemory()
{
int n;
string fname;
string keyfile, password, certfile;
byte[] xmlArr;
byte[] b;
Console.WriteLine("\nSIGN XML FROM BYTES TO BYTES:");
// 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-----" +
"MIIF+TCCA+GgAwIBAgIUMzAwMDEwMDAwMDAzMDAwMjM3MDgwDQYJKoZIhvcNAQELBQAwggFmMSAwHgY" +
"DVQQDDBdBLkMuIDIgZGUgcHJ1ZWJhcyg0MDk2KTEvMC0GA1UECgwmU2VydmljaW8gZGUgQWRtaW5pc3" +
"RyYWNpw7NuIFRyaWJ1dGFyaWExODA2BgNVBAsML0FkbWluaXN0cmFjacOzbiBkZSBTZWd1cmlkYWQgZ" +
"GUgbGEgSW5mb3JtYWNpw7NuMSkwJwYJKoZIhvcNAQkBFhphc2lzbmV0QHBydWViYXMuc2F0LmdvYi5t" +
"eDEmMCQGA1UECQwdQXYuIEhpZGFsZ28gNzcsIENvbC4gR3VlcnJlcm8xDjAMBgNVBBEMBTA2MzAwMQs" +
"wCQYDVQQGEwJNWDEZMBcGA1UECAwQRGlzdHJpdG8gRmVkZXJhbDESMBAGA1UEBwwJQ295b2Fjw6FuMR" +
"UwEwYDVQQtEwxTQVQ5NzA3MDFOTjMxITAfBgkqhkiG9w0BCQIMElJlc3BvbnNhYmxlOiBBQ0RNQTAeF" +
"w0xNzA1MTgwMzU0NTZaFw0yMTA1MTgwMzU0NTZaMIHlMSkwJwYDVQQDEyBBQ0NFTSBTRVJWSUNJT1Mg" +
"RU1QUkVTQVJJQUxFUyBTQzEpMCcGA1UEKRMgQUNDRU0gU0VSVklDSU9TIEVNUFJFU0FSSUFMRVMgU0M" +
"xKTAnBgNVBAoTIEFDQ0VNIFNFUlZJQ0lPUyBFTVBSRVNBUklBTEVTIFNDMSUwIwYDVQQtExxBQUEwMT" +
"AxMDFBQUEgLyBIRUdUNzYxMDAzNFMyMR4wHAYDVQQFExUgLyBIRUdUNzYxMDAzTURGUk5OMDkxGzAZB" +
"gNVBAsUEkNTRDAxX0FBQTAxMDEwMUFBQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJdU" +
"csHIEIgwivvAantGnYVIO3+7yTdD1tkKopbL+tKSjRFo1ErPdGJxP3gxT5O+ACIDQXN+HS9uMWDYnaU" +
"RalSIF9COFCdh/OH2Pn+UmkN4culr2DanKztVIO8idXM6c9aHn5hOo7hDxXMC3uOuGV3FS4ObkxTV+9" +
"NsvOAV2lMe27SHrSB0DhuLurUbZwXm+/r4dtz3b2uLgBc+Diy95PG+MIu7oNKM89aBNGcjTJw+9k+Wz" +
"JiPd3ZpQgIedYBD+8QWxlYCgxhnta3k9ylgXKYXCYk0k0qauvBJ1jSRVf5BjjIUbOstaQp59nkgHh45" +
"c9gnwJRV618NW0fMeDzuKR0CAwEAAaMdMBswDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCBsAwDQYJKoZ" +
"IhvcNAQELBQADggIBABKj0DCNL1lh44y+OcWFrT2icnKF7WySOVihx0oR+HPrWKBMXxo9KtrodnB1tg" +
"Ix8f+Xjqyphhbw+juDSeDrb99PhC4+E6JeXOkdQcJt50Kyodl9URpCVWNWjUb3F/ypa8oTcff/eMftQ" +
"ZT7MQ1Lqht+xm3QhVoxTIASce0jjsnBTGD2JQ4uT3oCem8bmoMXV/fk9aJ3v0+ZIL42MpY4POGUa/iT" +
"aawklKRAL1Xj9IdIR06RK68RS6xrGk6jwbDTEKxJpmZ3SPLtlsmPUTO1kraTPIo9FCmU/zZkWGpd8ZE" +
"AAFw+ZfI+bdXBfvdDwaM2iMGTQZTTEgU5KKTIvkAnHo9O45SqSJwqV9NLfPAxCo5eRR2OGibd9jhHe8" +
"1zUsp5GdE1mZiSqJU82H3cu6BiE+D3YbZeZnjrNSxBgKTIf8w+KNYPM4aWnuUMl0mLgtOxTUXi9MKnU" +
"ccq3GZLA7bx7Zn211yPRqEjSAqybUMVIOho6aqzkfc3WLZ6LnGU+hyHuZUfPwbnClb7oFFz1PlvGOpN" +
"DsUb0qP42QCGBiTUseGugAzqOP6EYpVPC73gFourmdBQgfayaEvi3xjNanFkPlW1XEYNrYJB4yNjphF" +
"rvWwTY86vL2o8gZN0Utmc5fnoBTfM9r2zVKmEi6FUeJ1iaDaVNv47te9iS1ai4V4vBY8r" +
"-----END CERTIFICATE-----";
keyfile =
"-----BEGIN ENCRYPTED PRIVATE KEY-----" +
"MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI5qDMtGWYa2wCAggA" +
"MBQGCCqGSIb3DQMHBAhFAqj+c0f8JASCBMhNUpNUp57vMu8L3LHBKRBTFl0VE3oq" +
"BIEKBHFYYz063iiS0Y3tPW3cplLTSqG25MdbIQcHCxwmPVYNdetHUjqjeR+TklWg" +
"tnMbLqvdMmmRxAFuHXznHFIa4U+YNedhFm7sdR2DsGFijm3vIpUbvpILtpTrhog/" +
"EHAvZXV6+F86cYc9+LUg3d0DRwJc+sWmk+2xOoXvOvvpnnQqfhQxkSknfITmc+HA" +
"WgHbKLK2q6e2RixjpWn0sA9LslYD0ZDn5uhrce+QEfK97asraFfiteqXf2Ll8B54" +
"Ku/er+O2JEu62vVDFumwMtZOuHKH4NbjOmMzKIwRTKp/1jp6OTGYSKIRiTDXnTET" +
"JwgItHahf7UAoM/qnkJa17Ood4hiCYopMyCXdhyMDJoFhWRanQODaiocb7XpMm1S" +
"EpTtHZeKgEVWSc/obYgSgs4iY497UR2MUVZQSCBdRXCgs5g1c31cCwAZ6r41KMoL" +
"OBVLtRXoT0mc0D6ovlwYuJhqYvuwjdNkWJS7qwXuy8b2ux4t027NGUXmgtb9XQDm" +
"8yJrdTtm0CktWPKe7i2tQtBC2tAjduGAlBrzY+whySRN8KUJQbYKhOBaLXgEPI93" +
"wi/SKHJO13WvfqqjKqrqJwB3tvhjz5E1uDKmDFoivdS76uq+k/xpmF5OWBmypWNV" +
"iw7kgvmH1OeTBKYkUHIL85skL6pdycGnTk3g0AmG9xtPYu6pdSqUv+N8QmTdmmdu" +
"85fDEN0fk2t2BRPANsbIqxopVfj5qIwm+8TbZDdNj8OssxrC5sRy5yDBjV4J+x25" +
"3yaILn7wgUR6Yj6GaHUUF4GISmFZ/PTbnVPDd424w6hGV8NKtUHXq5ms2kJXo6XG" +
"iGqjbdePM53QhdSrxTM5Dt76RcAInky6w5s/7gvT/w7tdbVA/SPhp4xgaT8Crmjb" +
"k3upcSqNI0HuROBxOs0gRRAWXScUZJ0Vd1V0F+C5cG2R1CtGTYeRmIAwLwcWf6Dj" +
"Y1Q+TOe/W3eTatOo+gIozjYDCk5ZNfeQzq4p1ApN6+gzS8kNxtvKOYJogjV74RK/" +
"Xl7u7oLv4SZT7Nl1YRpScW1ouIcNNTP0AC+j2OFZ3YueN8CcmvXbgSW8pYRooTxn" +
"Ffo9sdOL624uwRyb2DwwLO0Vo3aBIEIf8sm9sqocXmwh9sxFPEbTXPCuMSao8Qjy" +
"BOlsCem2589NVZs0h0ipGwdbatcjkgf+hzRoYBdlvHtKHJ8gL/A/Ap8z0+TK5NaV" +
"WUA+zXOZRZ66NYfs18DEbJKjwOcnnsLcfAMYoSn697148sL4JBv8IOmM6QXfxCl/" +
"0yU0d5/876L5jOL56lfH0eBk8s2nioAl3yRBl2wlihWi39sA0bsdHFKYEX+LqPBB" +
"CAdxZAvXCCJcdEdxOXSgEiFAmW9+IXFT/WJeGcZ4OmCd3Qf0fxGqFXA/9hIUumWd" +
"e6s0wN8LjXuFZQaMDaaVIGXKguP3OijsfBF0PYzI+L6CfUi2BLaYNJTlbQxbncmW" +
"2PKeDiypgt3ZY1PKV66o5OAJEAkV3vf9cRwXE5T8GwZHA+wx2rWC98hkH15xfI9q" +
"EsYulVdcXWzCF58HFQjUoDon0e/QMukS0eNgq9ipmoKAWKyy7+TQw7Xx3MmqkGlL" +
"HGM=" +
"-----END ENCRYPTED PRIVATE KEY-----";
password = "12345678a";
// Check key matches certificate
n = Sat.CheckKeyAndCert(keyfile, password, certfile);
Console.WriteLine("Sat.CheckKeyAndCert(STRINGS) = {0}", n);
Debug.Assert(n == 0, "Sat.CheckKeyAndCert failed");
// Read in a UTF-8-encoded XML file into a byte array
fname = "cfdv33a-base.xml";
xmlArr = File.ReadAllBytes(fname);
Console.WriteLine("File '{0}'-->{1} UTF-8 bytes", fname, xmlArr.Length);
string s1 = "";
string s2 = "";
// Sign it and receive result back as a byte array...
b = Sat.SignXmlToBytes(xmlArr, keyfile, password, certfile, 0);
if (b.Length == 0) {
Console.WriteLine("ERROR={0}", General.LastError());
} else {
// Convert this to a string
s1 = System.Text.Encoding.UTF8.GetString(b);
Console.WriteLine("XML-OUT=\n---\n{0}\n---\n", s1);
Console.WriteLine("From bytes: XML-OUT: {0} bytes --> {1} chars", b.Length, s1.Length);
}
// Sign the file instead and receive result back as a byte array...
b = Sat.SignXmlToBytes(fname, keyfile, password, certfile, 0);
if (b.Length == 0)
Console.WriteLine("ERROR={0}", General.LastError());
else {
// Convert this to a string
s2 = System.Text.Encoding.UTF8.GetString(b);
Console.WriteLine("From file: XML-OUT: {0} bytes --> {1} chars", b.Length, s2.Length);
Debug.Assert(s1.Equals(s2));
}
}
static void test_UUID()
{
string s;
Console.WriteLine("\nGENERATE 3 UUIDs:");
s = Sat.Uuid();
Console.WriteLine("UUID={0}", s);
s = Sat.Uuid();
Console.WriteLine("UUID={0}", s);
s = Sat.Uuid();
Console.WriteLine("UUID={0}", s);
}
static void test_Retenciones()
{
int n;
string s;
string fname;
Console.WriteLine("\nWORK WITH A `RETENCIONES` DOCUMENT:");
fname = "Ejemplo_Retenciones-base.xml";
Console.WriteLine("FILE={0}", fname);
n = Sat.XmlReceiptVersion(fname);
Console.WriteLine("Sat.XmlReceiptVersion() returns ID={0} (expecting 1010)", n);
Debug.Assert(1010 == n, "Sat.XmlReceiptVersion failed");
s = Sat.MakeDigestFromXml(fname);
Console.WriteLine("Sat.MakeDigestFromXml() -> {0}", s);
Debug.Assert(s.Length > 0, "Sat.MakeDigestFromXml failed");
// Use new [v6.0] option to find name of root element
s = Sat.GetXmlAttribute(fname, "", "");
Console.WriteLine("File root element is '{0}'", s);
Debug.Assert(s.Length > 0, "Sat.GetXmlAttribute('','') failed");
}
static void test_Contabilidad()
{
int n;
string s;
string fname;
string newname, keyfile, password, certfile;
Console.WriteLine("\nWORK WITH `CONTABILIDAD` DOCUMENTS:");
fname = "AAA010101AAA201501CT-base.xml";
Console.WriteLine("CATALOGOCUENTAS FILE={0}", fname);
n = Sat.XmlReceiptVersion(fname);
Console.WriteLine("Sat.XmlReceiptVersion() returns ID={0} (expecting 2011)", n);
Debug.Assert(2011 == n, "Sat.XmlReceiptVersion failed");
Console.WriteLine("SIGN A CATALOGOCUENTAS DOCUMENT...");
newname = "AAA010101AAA201501CT_new-signed.xml";
keyfile = "emisor.key";
certfile = "emisor.cer";
password = "12345678a";
n = Sat.SignXml(newname, fname, keyfile, password, certfile);
Console.WriteLine("Sat.SignXml() returns {0} (expecting 0)", n);
Debug.Assert(0 == n, "Sat.SignXml failed");
n = Sat.VerifySignature(newname);
Console.WriteLine("Sat.VerifySignature() returns {0} (expecting 0)", n);
Debug.Assert(0 == n, "Sat.VerifySignature failed");
fname = "AAA010101AAA201501BN-base.xml";
Console.WriteLine("BALANZA FILE={0}", fname);
n = Sat.XmlReceiptVersion(fname);
Console.WriteLine("Sat.XmlReceiptVersion() returns ID={0} (expecting 2111)", n);
Debug.Assert(2111 == n, "Sat.XmlReceiptVersion failed");
Console.WriteLine("MAKE THE SIGNATURE STRING FOR BALANZA...");
s = Sat.MakeSignatureFromXml(fname, keyfile, password);
Console.WriteLine("Sat.MakeSignatureFromXml() ->\n{0}", s);
Debug.Assert(s.Length > 0, "Sat.MakeSignatureFromXml() failed");
fname = "contab-SelloDigitalContElec-signed.xml";
Console.WriteLine("SELLODIGITALCONTELEC FILE={0}", fname);
n = Sat.XmlReceiptVersion(fname);
Console.WriteLine("Sat.XmlReceiptVersion() returns ID={0} (expecting 2511)", n);
Debug.Assert(2511 == n, "Sat.XmlReceiptVersion failed");
Console.WriteLine("VERIFY SIGNATURE FOR SELLODIGITALCONTELEC USING PAC CERTIFICATE...");
n = Sat.VerifySignature(fname, "pac1024.cer");
Console.WriteLine("Sat.VerifySignature() returns {0} (expecting 0)", n);
Debug.Assert(0 == n, "Sat.VerifySignature failed");
// NEW IN [v8.1]
Console.WriteLine("\nSUPPORT FOR CONTABILIDAD V1.3...");
fname = "AAA010101AAA201705CT.xml";
Console.WriteLine("FILE: {0}", fname);
s = Sat.GetXmlAttribute(fname, "", "");
Console.WriteLine("Doc type is '{1}'", fname, s);
n = Sat.ValidateXml(fname);
Console.WriteLine("Sat.ValidateXml() returns {0} (0 => OK)", n);
Debug.Assert(0 == n, "Sat.ValidateXml failed");
n = Sat.VerifySignature(fname);
Console.WriteLine("Sat.VerifySignature() returns {0} (0 => OK)", n);
Debug.Assert(0 == n, "Sat.VerifySignature failed");
n = Sat.XmlReceiptVersion(fname);
Console.WriteLine("Sat.XmlReceiptVersion() returns {0} (expecting 2013)", n);
s = Sat.DefaultDigestAlg(fname);
Console.WriteLine("Default digest algorithm is '{0}'", s);
}
static void test_ConVol()
{
int n;
string fname;
string newname, keyfile, password, certfile;
Console.WriteLine("\nWORK WITH `CONTROLESVOLUMETRICOS` DOCUMENT:");
fname = "ConVolE12345-base.xml";
Console.WriteLine("FILE={0}", fname);
n = Sat.XmlReceiptVersion(fname);
Console.WriteLine("Sat.XmlReceiptVersion() returns ID={0} (expecting 4011)", n);
Debug.Assert(4011 == n, "Sat.XmlReceiptVersion failed");
Console.WriteLine("SIGN A CONVOL DOCUMENT WITH BIGFILE FLAG...");
newname = "ConVolE12345_new-signed.xml";
// Use key and cert provided for ConVol tests
keyfile = "CSD_E12345CV_ACP020530MP5.key";
certfile = "CSD_E12345CV_ACP020530MP5.cer";
password = "12345678a";
n = Sat.SignXmlEx(newname, fname, keyfile, password, certfile, SignOptions.BigFile);
Console.WriteLine("Sat.SignXmlEx(BigFile) returns {0} (expecting 0)", n);
Debug.Assert(0 == n, "Sat.SignXmlEx(BigFile) failed");
n = Sat.VerifySignature(newname);
Console.WriteLine("Sat.VerifySignature() returns {0} (expecting 0)", n);
Debug.Assert(0 == n, "Sat.VerifySignature failed");
}
static void test_InsertCert()
{
int n;
string s;
string fname;
string newname, password, certfile;
string cerStr;
string xmlStr;
string keyStr;
string newStr;
byte[] b;
Console.WriteLine("\nINSERT CERTIFICATE INTO XML...");
// Take an XML file without a Nocertificado...
fname = "cfdv33a-base-nocertnum.xml";
Console.WriteLine("Start file '{0}'.NoCertificado=[{1}]", fname, Sat.GetXmlAttribute(fname, "NoCertificado", "cfdi:Comprobante"));
Console.WriteLine("Start file '{0}'.Certificado={1} bytes", fname, Sat.GetXmlAttribute(fname, "Certificado", "cfdi:Comprobante").Length);
// Insert certificate details...
certfile = "emisor.cer";
newname = "cfdv33a_new-base-pluscert.xml";
n = Sat.InsertCert(newname, fname, certfile);
Console.WriteLine("Sat.InsertCert() returns {0} (expecting 0)", n);
Debug.Assert(0 == n, "Sat.InsertCert failed");
fname = newname;
Console.WriteLine("Inter file '{0}'.NoCertificado=[{1}]", fname, Sat.GetXmlAttribute(fname, "NoCertificado", "cfdi:Comprobante"));
Console.WriteLine("Inter file '{0}'.Certificado={1} bytes", fname, Sat.GetXmlAttribute(fname, "Certificado", "cfdi:Comprobante").Length);
// Sign it - no need to specify the certificate
newname = "cfdv33a_new-signed-pluscert.xml";
n = Sat.SignXml(newname, fname, "emisor.key", "12345678a", null);
Console.WriteLine("Sat.SignXml() returns {0} (expecting 0)", n);
Debug.Assert(0 == n, "Sat.SignXml failed");
// Verify that it worked
n = Sat.VerifySignature(newname);
Console.WriteLine("Sat.VerifySignature() returns {0} (0 => OK)", n);
Debug.Assert(0 == n, "Sat.VerifySignature failed");
Console.WriteLine("\nINSERT CERTIFICATE INTO XML USING STRINGS ONLY...");
// Read in data to strings
password = "12345678a";
xmlStr = File.ReadAllText("cfdv33a-base-nocertnum.xml");
cerStr = Sat.GetCertAsString("emisor.cer");
keyStr = Sat.GetKeyAsString("emisor.key", password, KeyOption.EncryptedPEM);
Console.WriteLine("String lengths: XML={0}, CER={1}, KEY={2}", xmlStr.Length, cerStr.Length, keyStr.Length);
// Show we can use just strings
n = Sat.CheckKeyAndCert(keyStr, password, cerStr);
Console.WriteLine("Sat.CheckKeyAndCert() returns {0} (0 => OK)", n);
s = Sat.GetXmlAttribute(xmlStr, "Moneda", "cfdi:Comprobante");
Console.WriteLine("Moneda in XML=[{0}]", s);
// Insert certificate details. Note (string) -> (bytes) -> (string)
b = Sat.InsertCertToBytes(xmlStr, cerStr);
Console.WriteLine("Sat.InsertCertToBytes() returns byte array of length {0}", b.Length);
if (b.Length == 0) DisplayError();
Debug.Assert(b.Length > 0);
newStr = System.Text.Encoding.UTF8.GetString(b);
Console.WriteLine("XML string has length {0}", newStr.Length);
// Check we have a NoCertificado attribute...
s = Sat.GetXmlAttribute(newStr, "NoCertificado", "cfdi:Comprobante");
Console.WriteLine("NoCertificado=[{0}]", s);
if (s.Length == 0) DisplayError();
xmlStr = newStr;
// Sign it - all strings, no cert required
newStr = System.Text.Encoding.UTF8.GetString(Sat.SignXmlToBytes(xmlStr, keyStr, password, null, 0));
// Verify that it worked
n = Sat.VerifySignature(newStr);
Console.WriteLine("Sat.VerifySignature() returns {0} (0 => OK)", n);
if (n != 0) DisplayError(n);
Debug.Assert(0 == n, "Sat.VerifySignature failed");
}
static void test_GetXmlAttribute_Advanced()
{
string s;
string fname;
string attributeName, elementName;
string xbase, xpath, val;
int i, j;
Console.WriteLine("\nXPATH EXPRESSIONS FOR XMLGETATTRIBUTE...");
fname = "A7.xml";
Console.WriteLine("FILE: {0}", fname);
elementName = "/Comprobante";
attributeName = "Version";
s = Sat.GetXmlAttribute(fname, attributeName, elementName);
Debug.Assert(s.Length > 0, "Sat.GetXmlAttribute failed");
Console.WriteLine("elemname='{0}' attributeName='{1}' attrValue='{2}'", elementName, attributeName, s);
elementName = "/Comprobante/Conceptos/Concepto[2]/Impuestos/Traslados/Traslado[1]";
attributeName = "Importe";
s = Sat.GetXmlAttribute(fname, attributeName, elementName);
Debug.Assert(s.Length > 0, "Sat.GetXmlAttribute failed");
Console.WriteLine("elemname='{0}' attributeName='{1}' attrValue='{2}'", elementName, attributeName, s);
elementName = "/Comprobante/Conceptos/Concepto[1]/Impuestos/Retenciones/Retencion[2]";
attributeName = "Importe";
s = Sat.GetXmlAttribute(fname, attributeName, elementName);
Debug.Assert(s.Length > 0, "Sat.GetXmlAttribute failed");
Console.WriteLine("elemname='{0}' attributeName='{1}' attrValue='{2}'", elementName, attributeName, s);
// Same as above but shorter
elementName = "//Conceptos/Concepto[1]//Retencion[2]";
attributeName = "Importe";
s = Sat.GetXmlAttribute(fname, attributeName, elementName);
Debug.Assert(s.Length > 0, "Sat.GetXmlAttribute failed");
Console.WriteLine("elemname='{0}' attributeName='{1}' attrValue='{2}'", elementName, attributeName, s);
// Test for existence
Console.WriteLine("Check for existence...");
elementName = "/Comprobante/Conceptos/Concepto[3]";
attributeName = "Importe";
s = Sat.GetXmlAttribute(fname, attributeName, elementName);
Debug.Assert(s.Length > 0, "Sat.GetXmlAttribute failed");
Console.WriteLine("elemname='{0}' attributeName='{1}' attrValue='{2}'", elementName, attributeName, s);
Console.WriteLine("Loop through all Concepto elements until no match...");
for (i = 1; true; i++) {
elementName = "//Conceptos/Concepto" + "[" + i + "]";
s = Sat.GetXmlAttribute(fname, attributeName, elementName);
Console.WriteLine("elemname='{0}' attributeName='{1}' attrValue='{2}'", elementName, attributeName, s);
if (Sat.XmlNoMatch() == s)
break;
}
Console.WriteLine("Invalid path expression...");
elementName = "/";
attributeName = "Version";
Console.WriteLine("elemname='{0}' attributeName='{1}'", elementName, attributeName);
s = Sat.GetXmlAttribute(fname, attributeName, elementName);
if (s.Length == 0) {
Console.WriteLine("ERROR={0}", General.LastError());
}
Debug.Assert(s.Length == 0, "Sat.GetXmlAttribute failed");
Console.WriteLine("\nADVANCED XMLGETATTRIBUTE 'NOMATCH' FEATURES...");
s = Sat.XmlNoMatch();
Console.WriteLine("Sat.XmlNoMatch() returns '{0}'", s);
Console.WriteLine("Set a new value to be returned...");
Sat.SetXmlNoMatch("##No coinciden##");
s = Sat.XmlNoMatch();
Console.WriteLine("Sat.XmlNoMatch() returns '{0}'", s);
fname = "cfdv33a-signed-tfd.xml";
Console.WriteLine("FILE: {0}", fname);
elementName = "/Comprobante/notthere";
s = Sat.GetXmlAttribute(fname, "", elementName);
Console.WriteLine("Sat.GetXmlAttribute({0}) returns '{1}'", elementName, s);
elementName = "/Comprobante/Impuestos";
s = Sat.GetXmlAttribute(fname, "", elementName);
Console.WriteLine("Sat.GetXmlAttribute({0}) returns '{1}'", elementName, s);
Console.WriteLine("\nUSE XPATH TO FIND ALL ATTRIBUTES NAMED 'IMPORTE'...");
fname = "A7.xml";
Console.WriteLine("FILE: {0}", fname);
// Output all attributes named "Importe" in a <Traslado> or <Retencion> element.
attributeName = "Importe";
// First look at each <Concepto> in the <Conceptos> element.
// (We can use either "/Comprobante/Conceptos" or "//Conceptos")
xbase = "//Conceptos/Concepto";
for (i = 1; true; i++) {
// FOREACH //Conceptos/Concepto[i] element output the value of Importe
xpath = xbase + "[" + i + "]";
val = Sat.GetXmlAttribute(fname, attributeName, xpath);
if (Sat.XmlNoMatch() == val) break;
Console.WriteLine("{0}/@{1}='{2}'", xpath, attributeName, val);
// FOREACH //Conceptos/Concepto[i]//Traslado[j] element output the value of Importe
// Long xpath is /Comprobante/Conceptos/Concepto[i]/Impuestos/Traslados/Traslado[j]
for (j = 1; true; j++) {
string xpath1 = xpath + "//Traslado[" + j + "]";
val = Sat.GetXmlAttribute(fname, attributeName, xpath1);
if (Sat.XmlNoMatch() == val) break;
Console.WriteLine("{0}/@{1}='{2}'", xpath1, attributeName, val);
}
// FOREACH //Conceptos/Concepto[i]//Retencion[j] element output the value of Importe
for (j = 1; true; j++) {
string xpath1 = xpath + "//Retencion[" + j + "]";
val = Sat.GetXmlAttribute(fname, attributeName, xpath1);
if (Sat.XmlNoMatch() == val) break;
Console.WriteLine("{0}/@{1}='{2}'", xpath1, attributeName, val);
}
}
// Now look in the /Comprobante/Impuestos element.
// NB we cannot use "//Impuestos" here
xpath = "/Comprobante/Impuestos";
// FOREACH /Comprobante/Impuestos//Retencion[j] element output the value of Importe
// Long xpath is /Comprobante/Impuestos/Retenciones/Retencion[j]
for (j = 1; true; j++) {
string xpath1 = xpath + "//Retencion[" + j + "]";
val = Sat.GetXmlAttribute(fname, attributeName, xpath1);
if (Sat.XmlNoMatch() == val) break;
Console.WriteLine("{0}/@{1}='{2}'", xpath1, attributeName, val);
}
// FOREACH /Comprobante/Impuestos//Traslado[j] element output the value of Importe
for (j = 1; true; j++) {
string xpath1 = xpath + "//Traslado[" + j + "]";
val = Sat.GetXmlAttribute(fname, attributeName, xpath1);
if (Sat.XmlNoMatch() == val) break;
Console.WriteLine("{0}/@{1}='{2}'", xpath1, attributeName, val);
}
}
/// <summary>
/// Tests passing xmlDoc parameter as a Unicode UTF-16 string. Improved in [v9.2].
/// </summary>
static void test_XMLasUnicodeString()
{
int n;
string s;
string fname;
string newname, password;
string xmlStr;
string elemName, attrName;
string digest1, digest2;
Console.WriteLine("\nREAD IN A FILE TO A UNICODE UTF-16 STRING THEN PASS THE STRING AS XML DOC...");
fname = "cfdv33a-nomina12B.xml";
Console.WriteLine("FILE: {0}", fname);
// Read in file to a Unicode text string
xmlStr = File.ReadAllText(fname, Encoding.UTF8);
Console.WriteLine("xmlStr is {0} characters", xmlStr.Length);
Console.WriteLine("\nGET ATTRIBUTE VALUE USING UTF-16 STRING...");
// Attribute name contains non-ASCII character 'ü', Antigüedad="P3Y2M23D"
elemName = "nomina12:Receptor";
attrName = "Antigüedad";
s = Sat.GetXmlAttribute(xmlStr, attrName, elemName);
Console.WriteLine("{0}/@{1}='{2}'", elemName, attrName, s);
if (s.Length == 0) DisplayError();
Debug.Assert(s.Length > 0, "Sat.GetXmlAttribute failed");
// Attribute name contains non-ASCII character 'ñ', Año="2016"
elemName = "nomina12:CompensacionSaldosAFavor";
attrName = "Año";
s = Sat.GetXmlAttribute(xmlStr, attrName, elemName);
Console.WriteLine("{0}/@{1}='{2}'", elemName, attrName, s);
if (s.Length == 0) DisplayError();
Debug.Assert(s.Length > 0, "Sat.GetXmlAttribute failed");
// Attribute value contains non-ASCII character 'í', Sindicalizado="Sí"
elemName = "nomina12:Receptor";
attrName = "Sindicalizado";
s = Sat.GetXmlAttribute(xmlStr, attrName, elemName);
Console.WriteLine("{0}/@{1}='{2}'", elemName, attrName, s);
if (s.Length == 0) DisplayError();
Debug.Assert(s.Length > 0, "Sat.GetXmlAttribute failed");
Console.WriteLine("\nCHECK XML IS OK USING UTF-16 STRING...");
n = Sat.ValidateXml(xmlStr);
Console.WriteLine("Sat.ValidateXml returns {0} (expecting 0)", n);
if (n != 0) DisplayError(n);
Debug.Assert(0 == n, "Sat.ValidateXml failed");
Console.WriteLine("\nMAKE PIPE STRING USING UTF-16 STRING...");
s = Sat.MakePipeStringFromXml(xmlStr);
Console.WriteLine("PIPESTRING=\n{0}", s);
if (s.Length == 0) DisplayError();
Debug.Assert(s.Length > 0, "Sat.MakePipeStringFromXml failed");
Console.WriteLine("\nFORM MESSAGE DIGEST OF PIPE STRING USING UTF-16 STRING...");
s = Sat.MakeDigestFromXml(xmlStr);
Console.WriteLine("DIGEST={0}", s);
if (s.Length == 0) DisplayError();
Debug.Assert(s.Length > 0, "Sat.MakeDigestFromXml failed");
Console.WriteLine("\nINSERT CERTIFICATE INTO XML USING UTF-16 STRING AS INPUT...");
newname = "cfdv33a-nomina12B-plus-cert.xml";
n = Sat.InsertCert(newname, xmlStr, "emisor.cer");
Console.WriteLine("Sat.InsertCert returns {0} (expecting 0)", n);
if (n != 0) DisplayError(n);
Debug.Assert(0 == n, "Sat.InsertCert failed");
Console.WriteLine("Created new file '{0}' length = {1}", newname, FileLength(newname));
// Query this new file for certificate details
s = Sat.QueryCert(newname, Query.organizationName);
Console.WriteLine("organizationName('{0}')='{1}'", newname, s);
Console.WriteLine("\nSIGN AN XML FILE WORKING ENTIRELY IN MEMORY...:");
Console.WriteLine("Read in private key and matching certificate to memory...");
password = "12345678a"; /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */
// Read in the private key
string keyStr = Sat.GetKeyAsString("emisor.key", password, KeyOption.EncryptedPEM);
Console.WriteLine("Key string is {0} characters", keyStr.Length);
if (keyStr.Length == 0) DisplayError();
// And the matching cert
string cerStr = Sat.GetCertAsString("emisor.cer");
Console.WriteLine("Cert string is {0} characters", cerStr.Length);
if (cerStr.Length == 0) DisplayError();
Console.WriteLine("Check that the key and certificate are matched...");
n = Sat.CheckKeyAndCert(keyStr, password, cerStr);
Console.WriteLine("Sat.CheckKeyAndCert() returns {0} (expecting 0)", n);
if (n != 0) DisplayError(n);
Debug.Assert(0 == n, "Sat.CheckKeyAndCert failed");
// xmlStr already contains the unsigned XML doc, but we need XML data in bytes.
// As a test, we will force encoding of *input* string to be Latin-1
// but note the *output* will still be UTF-8.
byte[] xmlArr = System.Text.Encoding.GetEncoding("iso-8859-1").GetBytes(xmlStr);
Console.WriteLine("Unsigned xmlArr is {0} bytes", xmlArr.Length);
// Sign XML document with all parameters passed in memory
byte[] xmlArrSigned = Sat.SignXmlToBytes(xmlArr, keyStr, password, cerStr, 0);
Console.WriteLine("Sat.SignXmlToBytes(xmlArr) returns {0} bytes", xmlArrSigned.Length);
// Convert bytes back to Unicode string
// NB xmlStr now contains the *signed* document
xmlStr = System.Text.Encoding.UTF8.GetString(xmlArrSigned);
Console.WriteLine("xmlStr-signed is {0} characters", xmlStr.Length);
Console.WriteLine("\nCHECK SIGNED XML IS OK USING UTF-16 STRING...");
n = Sat.ValidateXml(xmlStr);
Console.WriteLine("Sat.ValidateXml(xmlStr-signed) returns {0} (expecting 0)", n);
if (n != 0) DisplayError(n);
Debug.Assert(0 == n, "Sat.ValidateXml failed");
Console.WriteLine("\nVALIDATE THE SIGNATURE USING UTF-16 STRING...");
n = Sat.VerifySignature(xmlStr);
Console.WriteLine("Sat.VerifySignature(xmlStr-signed) returns {0} (expecting 0)", n);
if (n != 0) DisplayError(n);
Debug.Assert(0 == n, "Sat.VerifySignature failed");
Console.WriteLine("\nGET CERTIFICATE ORGNAME USING UTF-16 STRING...");
// This time we have a result...
s = Sat.QueryCert(xmlStr, Query.organizationName);
Console.WriteLine("organizationName={0}", s);
if (s.Length == 0) DisplayError();
Debug.Assert(s.Length > 0, "Sat.QueryCert failed");
Console.WriteLine("\nMAKE SIGNATURE USING UTF-16 STRING...");
s = Sat.MakeSignatureFromXml(xmlStr, keyStr, password);
Console.WriteLine("Sello={0}", s);
if (s.Length == 0) DisplayError();
Debug.Assert(s.Length > 0, "Sat.MakeSignatureFromXml failed");
Console.WriteLine("\nEXTRACT SIGNATURE DIGEST USING UTF-16 STRING...");
s = Sat.ExtractDigestFromSignature(xmlStr);
Console.WriteLine("digest1={0}", s);
if (s.Length == 0) DisplayError();
Debug.Assert(s.Length > 0, "Sat.ExtractDigestFromSignature failed");
digest1 = s;
Console.WriteLine("\nMAKE MESSAGE DIGEST OF PIPE STRING USING UTF-16 STRING...");
s = Sat.MakeDigestFromXml(xmlStr);
Console.WriteLine("digest2={0}", s);
if (s.Length == 0) DisplayError();
Debug.Assert(s.Length > 0, "Sat.MakeDigestFromXml failed");
digest2 = s;
// The last two should match (ignore case)
n = string.Compare(digest1, digest2, true);
Console.WriteLine("String.Compare(digest1, digest2, ignoreCase) returns {0} (expecting 0)", n);
Debug.Assert(0 == n, "Digest values do not match");
}
//*************************************
// 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;
}
static long FileLength(string filePath)
{
FileInfo fi = new FileInfo(filePath);
if (!fi.Exists)
{
return -1;
}
return fi.Length;
}
static bool FileHasBom(string filePath)
{
const int kNBYTES = 3;
byte[] buf = new byte[kNBYTES];
FileInfo finfo = new FileInfo(filePath);
Debug.Assert(finfo.Exists, "File '" + filePath + "' does not exist.");
if (finfo.Exists)
{
FileStream fsi = finfo.OpenRead();
BinaryReader br = new BinaryReader(fsi);
buf = br.ReadBytes(kNBYTES);
br.Close();
fsi.Close();
}
/* BOM consists of three bytes (0xEF, 0xBB, 0xBF) */
return (buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF);
}
/** Display error - overloaded */
static void DisplayError()
{
Console.WriteLine("ERROR: {0}", General.LastError());
}
static void DisplayError(int errCode)
{
string s = General.LastError();
Console.WriteLine("ERROR {0}: {1}: {2}", errCode, General.ErrorLookup(errCode), s);
}
}
}