Imports System Imports System.Diagnostics Imports System.IO Imports System.Text Imports FirmaSAT ' ' * $Id: TestFirmaSat.vb $ ' * 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://www.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. <www.di-mgt.com.au> <www.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` ' ' Ported from C# to VB.NET using icsharpcode.net's SharpDevelop. Namespace TestFirmaSAT ''' <summary> ''' Test examples for FirmaSAT .NET interface ''' </summary> Class TestFirmaSAT Private Const MIN_FSA_VERSION As Integer = 90200 ' Test files required to exist in the current working directory: Private Shared arrFileNames As String() = 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> _ Public Shared Sub Main(args As String()) ' Make sure minimum required version of FirmaSAT is installed... Console.WriteLine("FirmaSAT core Version is " & General.Version()) If General.Version() < MIN_FSA_VERSION Then Console.WriteLine("FATAL ERROR: Require FirmaSAT core version " & MIN_FSA_VERSION & " or later.") Return End If ' CHECK FOR REQUIRED FILES IN CURRENT WORKING DIRECTORY Console.WriteLine("Current working directory is {0}", Directory.GetCurrentDirectory()) Dim missingFile As String = "STOPPED: Required file is missing in current working directory" For Each fn As String In arrFileNames If FileIsNotPresent(fn, missingFile) Then Return End If Next ' Handle command-line arguments ' * [some] just do some tests (default = do all) ' Dim doSome As Boolean = False For iarg As Integer = 0 To args.Length - 1 If args(iarg) = "some" Then doSome = True End If Next '************* ' DO THE TESTS '************* If doSome Then ' 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() End If ' ****************************************** ' FINALLY, DISPLAY QUICK INFO ABOUT THE CORE DLL Console.WriteLine(vbLf & "DETAILS 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(vbLf & "ALL TESTS COMPLETED.") End Sub Private Shared Sub 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() End Sub '******************** ' THE TEST MODULES... '******************** Private Shared Sub test_General() Dim n As Integer Dim ch As Char Dim s As String '**************** ' GENERAL TESTS * '**************** Console.WriteLine(vbLf & "INTERROGATE 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) End Sub Private Shared Sub test_ErrorLookup() Dim s As String '***************** ' ERROR LOOKUP TESTS * '********************* Console.WriteLine(vbLf & "ERROR CODES:") For i As Integer = 0 To 9999 s = General.ErrorLookup(i) If Not s.Equals([String].Empty) Then Console.WriteLine("{0}={1}", i, s) End If Next End Sub Private Shared Sub test_pipestring() Dim s As String Dim fname As String Console.WriteLine(vbLf & "FORM THE PIPESTRING FROM AN XML FILE:") fname = "cfdv33a-base.xml" s = Sat.MakePipeStringFromXml(fname) Console.WriteLine("Sat.MakePipeStringFromXml('{0}')=" & vbLf & "{1}", fname, s) Debug.Assert(s.Length > 0, "Sat.MakePipeStringFromXml failed") End Sub Private Shared Sub test_SignAndVerify() Dim n As Integer Dim fname As String Dim newname As String, keyfile As String, password As String, certfile As String Console.WriteLine(vbLf & "SIGN 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(vbLf & "VERIFY 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") End Sub Private Shared Sub test_Digest() Dim s As String Dim fname As String Console.WriteLine(vbLf & "FORM THE DIGEST OF THE PIPESTRING IN AN XML FILE:") fname = "cfdv33a-signed-tfd.xml" s = Sat.MakeDigestFromXml(fname) Console.WriteLine("Sat.MakeDigestFromXml('{0}')=" & vbLf & "{1}", fname, s) Debug.Assert(s.Length > 0, "Sat.MakeDigestFromXml failed") Console.WriteLine(vbLf & "EXTRACT THE DIGEST FROM THE SIGNATURE IN AN XML FILE:") fname = "cfdv33a-signed-tfd.xml" s = Sat.ExtractDigestFromSignature(fname) Console.WriteLine("Sat.ExtractDigestFromSignature('{0}')=" & vbLf & "{1}", fname, s) Debug.Assert(s.Length > 0, "Sat.ExtractDigestFromSignature failed") End Sub Private Shared Sub test_Validate() Dim n As Integer Dim s As String Dim fname As String Console.WriteLine(vbLf & "TRY 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(vbLf & "VALIDATE 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(vbLf & "Using 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") End Sub Private Shared Sub test_ExtractAttribute() Dim s As String Dim fname As String Dim attributeName As String, elementName As String Console.WriteLine(vbLf & "EXTRACT 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})=" & vbLf & "{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})=" & vbLf & "{1}", fname, s, attributeName, elementName) Debug.Assert(s.Length > 0, "Sat.GetXmlAttribute failed") Console.WriteLine(vbLf & "EXTRACT 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(vbLf & "EXTRACT 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") End Sub Private Shared Sub test_X509Certificate() Dim s As String Dim s1 As String Dim fname As String Console.WriteLine(vbLf & "GET 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(vbLf & "GET CERTIFICATE AS A BASE64 STRING:") fname = "emisor.cer" s = Sat.GetCertAsString(fname) Console.WriteLine("Sat.GetCertAsString('{0}')=" & vbLf & "{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") End Sub Private Shared Sub test_MakeSignature() Dim s As String Dim fname As String Dim keyfile As String, password As String Console.WriteLine(vbLf & "MAKE 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}')=" & vbLf & "{1}", fname, s) Debug.Assert(s.Length > 0, "Sat.MakeSignatureFromXml failed") End Sub Private Shared Sub test_Detallista() Dim n As Integer Dim s As String Dim fname As String Dim keyfile As String, password As String Dim newname As String, certfile As String Dim attributeName As String, elementName As String Console.WriteLine(vbLf & "SIGN 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(vbLf & "EXTRACT 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") End Sub Private Shared Sub test_CertValidity() Dim s As String Dim fname As String Console.WriteLine(vbLf & "GET 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}')=" & vbTab & "{1}", fname, s) Debug.Assert(s.Length > 0, "Sat.GetCertExpiry failed") s = Sat.GetCertStart(fname) Console.WriteLine("Sat.GetCertStart('{0}') =" & vbTab & "{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}')=" & vbTab & "{1}", fname, s) Debug.Assert(s.Length > 0, "Sat.GetCertExpiry failed") s = Sat.GetCertStart(fname) Console.WriteLine("Sat.GetCertStart('{0}') =" & vbTab & "{1}", fname, s) Debug.Assert(s.Length > 0, "Sat.GetCertStart failed") End Sub Private Shared Sub test_CheckKeyAndCert() Dim n As Integer Dim keyfile As String, password As String, certfile As String Console.WriteLine(vbLf & "CHECK 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") End Sub Private Shared Sub test_ReceiptVersion() Dim n As Integer Dim fname As String Console.WriteLine(vbLf & "FIND 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") End Sub Private Shared Sub test_Tfd() Dim n As Integer Dim s As String Dim fname As String Dim newname As String, keyfile As String, password As String, certfile As String Dim s1 As String Console.WriteLine(vbLf & "CREATE 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}') =" & vbLf & "{1}", fname, s) Console.WriteLine(vbLf & "FORM DIGEST OF PIPESTRING FOR TFD:") s = Tfd.MakeDigestFromXml(fname) Console.WriteLine("Tfd.MakeDigestFromXml('{0}')=" & vbLf & "{1}", fname, s) Console.WriteLine(vbLf & "EXTRACT DIGEST FROM TFD SELLOSAT:") ' NB certFile is required for Tfd s1 = Tfd.ExtractDigestFromSignature(fname, certfile) Console.WriteLine("Tfd.ExtractDigestFromSignature('{0}')=" & vbLf & "{1}", fname, s1) ' Check the two digests match Debug.Assert([String].Compare(s, s1, True) = 0, "Digests do not match") Console.WriteLine(vbLf & "PRETEND 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}')=" & vbLf & "{1}", fname, s) s1 = Sat.GetXmlAttribute(fname, "SelloSAT", "TimbreFiscalDigital") ' NB Capital 'S' in Sello for TFD v1.1 Console.WriteLine("Correct=" & vbLf & "{0}", s1) Debug.Assert([String].Compare(s, s1, True) = 0, "selloSAT values do not match") Console.WriteLine(vbLf & "ADD 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)" & vbLf, 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)" & vbLf, newname, n) Debug.Assert(0 = n, "Sat.ValidateXml failed") Console.WriteLine(vbLf & "VERIFY 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") End Sub Private Shared Sub test_BOM() Dim n As Integer Dim fname As String Dim newname As String Console.WriteLine(vbLf & "ADD 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)) End Sub Private Shared Sub test_ConsecutiveElements() Dim s As String Dim fname As String Dim attributeName As String, elementName As String Console.WriteLine(vbLf & "EXTRACT ATTRIBUTES FROM CONSECUTIVE ELEMENTS:") fname = "ejemplo_v32-tfd2015.xml" attributeName = "descripcion" elementName = "cfdi:Concepto" Console.WriteLine("For file '{0}'", fname) Dim eName As String = Nothing For i As Integer = 1 To 10 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 Then ' [2020-07-06] Changed from empty string Exit For End If Console.WriteLine("{0}", s) Next Console.WriteLine() End Sub Private Shared Sub test_PrivateKey() Dim n As Integer Dim s As String Dim fname As String Dim keyfile As String, password As String, certfile As String Dim newname As String Dim newpassword As String Console.WriteLine(vbLf & "GET PRIVATE KEY AS BASE64:") fname = "emisor.key" password = "12345678a" s = Sat.GetKeyAsString(fname, password) Console.WriteLine("Sat.GetKeyAsString({0})=" & vbLf & "{1}" & vbLf, fname, s) Console.WriteLine("Sat.GetKeyAsString('{0}').Length={1}", fname, s.Length) Debug.Assert(s.Length > 0, "Sat.GetKeyAsString failed") Console.WriteLine(vbLf & "WRITE 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(vbLf & "SAVE 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) End Sub Private Shared Sub test_QueryCert() Dim s As String Dim fname As String Console.WriteLine(vbLf & "GET 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(vbLf & "TEST 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(vbLf & "QUERY 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(vbLf & "QUERY 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") End Sub Private Shared Sub test_SignInMemory() Dim n As Integer Dim fname As String Dim keyfile As String, password As String, certfile As String Dim xmlArr As Byte() Dim b As Byte() Console.WriteLine(vbLf & "SIGN 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) Dim s1 As String = "" Dim s2 As String = "" ' Sign it and receive result back as a byte array... b = Sat.SignXmlToBytes(xmlArr, keyfile, password, certfile, 0) If b.Length = 0 Then Console.WriteLine("ERROR={0}", General.LastError()) Else ' Convert this to a string s1 = System.Text.Encoding.UTF8.GetString(b) Console.WriteLine("XML-OUT=" & vbLf & "---" & vbLf & "{0}" & vbLf & "---" & vbLf, s1) Console.WriteLine("From bytes: XML-OUT: {0} bytes --> {1} chars", b.Length, s1.Length) End If ' Sign the file instead and receive result back as a byte array... b = Sat.SignXmlToBytes(fname, keyfile, password, certfile, 0) If b.Length = 0 Then 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)) End If End Sub Private Shared Sub test_UUID() Dim s As String Console.WriteLine(vbLf & "GENERATE 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) End Sub Private Shared Sub test_Retenciones() Dim n As Integer Dim s As String Dim fname As String Console.WriteLine(vbLf & "WORK 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") End Sub Private Shared Sub test_Contabilidad() Dim n As Integer Dim s As String Dim fname As String Dim newname As String, keyfile As String, password As String, certfile As String Console.WriteLine(vbLf & "WORK 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() ->" & vbLf & "{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(vbLf & "SUPPORT 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) End Sub Private Shared Sub test_ConVol() Dim n As Integer Dim fname As String Dim newname As String, keyfile As String, password As String, certfile As String Console.WriteLine(vbLf & "WORK 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") End Sub Private Shared Sub test_InsertCert() Dim n As Integer Dim s As String Dim fname As String Dim newname As String, password As String, certfile As String Dim cerStr As String Dim xmlStr As String Dim keyStr As String Dim newStr As String Dim b As Byte() Console.WriteLine(vbLf & "INSERT 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", Nothing) 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(vbLf & "INSERT 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 Then DisplayError() End If 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 Then DisplayError() End If xmlStr = newStr ' Sign it - all strings, no cert required newStr = System.Text.Encoding.UTF8.GetString(Sat.SignXmlToBytes(xmlStr, keyStr, password, Nothing, 0)) ' Verify that it worked n = Sat.VerifySignature(newStr) Console.WriteLine("Sat.VerifySignature() returns {0} (0 => OK)", n) If n <> 0 Then DisplayError(n) End If Debug.Assert(0 = n, "Sat.VerifySignature failed") End Sub Private Shared Sub test_GetXmlAttribute_Advanced() Dim s As String Dim fname As String Dim attributeName As String, elementName As String Dim xbase As String, xpath As String, val As String Dim i As Integer, j As Integer Console.WriteLine(vbLf & "XPATH 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...") i = 1 While True 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 Then Exit While End If i += 1 End While 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 Then Console.WriteLine("ERROR={0}", General.LastError()) End If Debug.Assert(s.Length = 0, "Sat.GetXmlAttribute failed") Console.WriteLine(vbLf & "ADVANCED 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(vbLf & "USE 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" i = 1 While True ' FOREACH //Conceptos/Concepto[i] element output the value of Importe xpath = xbase & "[" & i & "]" val = Sat.GetXmlAttribute(fname, attributeName, xpath) If Sat.XmlNoMatch() = val Then Exit While End If 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] j = 1 While True Dim xpath1 As String = xpath & "//Traslado[" & j & "]" val = Sat.GetXmlAttribute(fname, attributeName, xpath1) If Sat.XmlNoMatch() = val Then Exit While End If Console.WriteLine("{0}/@{1}='{2}'", xpath1, attributeName, val) j += 1 End While ' FOREACH //Conceptos/Concepto[i]//Retencion[j] element output the value of Importe j = 1 While True Dim xpath1 As String = xpath & "//Retencion[" & j & "]" val = Sat.GetXmlAttribute(fname, attributeName, xpath1) If Sat.XmlNoMatch() = val Then Exit While End If Console.WriteLine("{0}/@{1}='{2}'", xpath1, attributeName, val) j += 1 End While i += 1 End While ' 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] j = 1 While True Dim xpath1 As String = xpath & "//Retencion[" & j & "]" val = Sat.GetXmlAttribute(fname, attributeName, xpath1) If Sat.XmlNoMatch() = val Then Exit While End If Console.WriteLine("{0}/@{1}='{2}'", xpath1, attributeName, val) j += 1 End While ' FOREACH /Comprobante/Impuestos//Traslado[j] element output the value of Importe j = 1 While True Dim xpath1 As String = xpath & "//Traslado[" & j & "]" val = Sat.GetXmlAttribute(fname, attributeName, xpath1) If Sat.XmlNoMatch() = val Then Exit While End If Console.WriteLine("{0}/@{1}='{2}'", xpath1, attributeName, val) j += 1 End While End Sub ''' <summary> ''' Tests passing xmlDoc parameter as a Unicode UTF-16 string. Improved in [v9.2]. ''' </summary> Private Shared Sub test_XMLasUnicodeString() Dim n As Integer Dim s As String Dim fname As String Dim newname As String, password As String Dim xmlStr As String Dim elemName As String, attrName As String Dim digest1 As String, digest2 As String Console.WriteLine(vbLf & "READ 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(vbLf & "GET 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 Then DisplayError() End If 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 Then DisplayError() End If 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 Then DisplayError() End If Debug.Assert(s.Length > 0, "Sat.GetXmlAttribute failed") Console.WriteLine(vbLf & "CHECK XML IS OK USING UTF-16 STRING...") n = Sat.ValidateXml(xmlStr) Console.WriteLine("Sat.ValidateXml returns {0} (expecting 0)", n) If n <> 0 Then DisplayError(n) End If Debug.Assert(0 = n, "Sat.ValidateXml failed") Console.WriteLine(vbLf & "MAKE PIPE STRING USING UTF-16 STRING...") s = Sat.MakePipeStringFromXml(xmlStr) Console.WriteLine("PIPESTRING=" & vbLf & "{0}", s) If s.Length = 0 Then DisplayError() End If Debug.Assert(s.Length > 0, "Sat.MakePipeStringFromXml failed") Console.WriteLine(vbLf & "FORM MESSAGE DIGEST OF PIPE STRING USING UTF-16 STRING...") s = Sat.MakeDigestFromXml(xmlStr) Console.WriteLine("DIGEST={0}", s) If s.Length = 0 Then DisplayError() End If Debug.Assert(s.Length > 0, "Sat.MakeDigestFromXml failed") Console.WriteLine(vbLf & "INSERT 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 Then DisplayError(n) End If 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(vbLf & "SIGN 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 Dim keyStr As String = Sat.GetKeyAsString("emisor.key", password, KeyOption.EncryptedPEM) Console.WriteLine("Key string is {0} characters", keyStr.Length) If keyStr.Length = 0 Then DisplayError() End If ' And the matching cert Dim cerStr As String = Sat.GetCertAsString("emisor.cer") Console.WriteLine("Cert string is {0} characters", cerStr.Length) If cerStr.Length = 0 Then DisplayError() End If 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 Then DisplayError(n) End If 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. Dim xmlArr As Byte() = 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 Dim xmlArrSigned As Byte() = 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(vbLf & "CHECK 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 Then DisplayError(n) End If Debug.Assert(0 = n, "Sat.ValidateXml failed") Console.WriteLine(vbLf & "VALIDATE THE SIGNATURE USING UTF-16 STRING...") n = Sat.VerifySignature(xmlStr) Console.WriteLine("Sat.VerifySignature(xmlStr-signed) returns {0} (expecting 0)", n) If n <> 0 Then DisplayError(n) End If Debug.Assert(0 = n, "Sat.VerifySignature failed") Console.WriteLine(vbLf & "GET 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 Then DisplayError() End If Debug.Assert(s.Length > 0, "Sat.QueryCert failed") Console.WriteLine(vbLf & "MAKE SIGNATURE USING UTF-16 STRING...") s = Sat.MakeSignatureFromXml(xmlStr, keyStr, password) Console.WriteLine("Sello={0}", s) If s.Length = 0 Then DisplayError() End If Debug.Assert(s.Length > 0, "Sat.MakeSignatureFromXml failed") Console.WriteLine(vbLf & "EXTRACT SIGNATURE DIGEST USING UTF-16 STRING...") s = Sat.ExtractDigestFromSignature(xmlStr) Console.WriteLine("digest1={0}", s) If s.Length = 0 Then DisplayError() End If Debug.Assert(s.Length > 0, "Sat.ExtractDigestFromSignature failed") digest1 = s Console.WriteLine(vbLf & "MAKE MESSAGE DIGEST OF PIPE STRING USING UTF-16 STRING...") s = Sat.MakeDigestFromXml(xmlStr) Console.WriteLine("digest2={0}", s) If s.Length = 0 Then DisplayError() End If 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") End Sub '************************************* ' LOCAL UTILITIES... '************************************* Private Shared Function FileIsNotPresent(filePath As String, message As String) As Boolean Dim fi As New FileInfo(filePath) If Not fi.Exists Then Console.WriteLine(vbLf & "{0}: {1}", message, filePath) Return True End If Return False End Function Private Shared Function FileLength(filePath As String) As Long Dim fi As New FileInfo(filePath) If Not fi.Exists Then Return -1 End If Return fi.Length End Function Private Shared Function FileHasBom(filePath As String) As Boolean Const kNBYTES As Integer = 3 Dim buf As Byte() = New Byte(kNBYTES - 1) {} Dim finfo As New FileInfo(filePath) Debug.Assert(finfo.Exists, "File '" & filePath & "' does not exist.") If finfo.Exists Then Dim fsi As FileStream = finfo.OpenRead() Dim br As New BinaryReader(fsi) buf = br.ReadBytes(kNBYTES) br.Close() fsi.Close() End If ' BOM consists of three bytes (0xEF, 0xBB, 0xBF) Return (buf(0) = &Hef AndAlso buf(1) = &Hbb AndAlso buf(2) = &Hbf) End Function '* Display error - overloaded Private Shared Sub DisplayError() Console.WriteLine("ERROR: {0}", General.LastError()) End Sub Private Shared Sub DisplayError(errCode As Integer) Dim s As String = General.LastError() Console.WriteLine("ERROR {0}: {1}: {2}", errCode, General.ErrorLookup(errCode), s) End Sub End Class End Namespace