/* $Id: TestFirmaSat.c $ */ // Some tests using the FirmaSAT C/C++ interface. // This uses plain-old ANSI C. // Requires certain files to exist in the current working directory. /******************************* 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> * Last updated: * $Date: 2020-08-05 23:43 $ * $Version: 9.2.0 $ **************************************************************************** */ /* Turn off MSCV warnings about fopen and sprintf */ #if defined(_MSC_VER) #define _CRT_SECURE_NO_DEPRECATE #endif #include <stdio.h> #include <stdlib.h> #include <string.h> #include "diFirmaSat2.h" #ifdef NDEBUG /* Make sure assertion testing is turned on */ #undef NDEBUG #endif #include <assert.h> /* Cope with case-insensitive string comparisons */ #if defined(_MSC_VER) #include <string.h> #define stricmp _stricmp #define strnicmp _strnicmp #elif defined(unix) || defined (linux) || defined(__linux) || defined(__APPLE__) #include <strings.h> #define stricmp strcasecmp #define strnicmp strncasecmp #endif /* Link to the DLL library - MSVC ONLY */ #ifdef _MSC_VER #if _WIN64 #if _DEBUG #define MYLIBDIR "../x64/Debug/" #else #define MYLIBDIR "../x64/Release/" #endif #else #if _DEBUG #define MYLIBDIR "../Debug/" #else #define MYLIBDIR "../Release/" #endif #endif #pragma comment(lib, MYLIBDIR "diFirmaSat2.lib") #endif /* prototype */ void display_all_errors(void); long MIN_VERSION = 90200; /********************/ /* HELPER UTILITIES */ /********************/ static void disp_error(long nErrCode) { char msg[1024]; long nchars; printf("Error code %ld", nErrCode); nchars = SAT_ErrorLookup(msg, sizeof(msg)-1, nErrCode); if (nchars > 0) printf(": %s", msg); nchars = SAT_LastError(msg, sizeof(msg)-1); if (nchars > 0) printf(" : %s", msg); putchar('\n'); } int file_exists(const char *fname) { FILE *fp; fp = fopen(fname, "rb"); if (fp == NULL) return 0; fclose(fp); return 1; } static long file_length(const char *fname) /* Returns the length of the file in bytes or -1 on error */ { long flen = -1; FILE *fp; fp = fopen(fname, "rb"); if (fp) { int x = fseek(fp, 0, SEEK_END); if (0 == x) flen = ftell(fp); else flen = -1; fclose(fp); } return flen; } /** Returns 1 if file with name `fname` has a UTF-8 BOM, or 0 if not, or -1 if error */ static int file_has_bom(const char *fname) { FILE *fp; int has_bom = 0; unsigned char buf[3]; fp = fopen(fname, "rb"); if (!fp) return -1; // Read first 3 bytes if (fread(buf, 1, 3, fp) == 3) { if (buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF) has_bom = 1; } fclose(fp); return has_bom; } /** Reads a binary file into a null-terminated string. * @returns Pointer to allocated buffer or NULL if error * @remark User must free allocated memory */ static char *file_read_to_string(const char *fname) { FILE *fp; long flen; char *buf; flen = file_length(fname); if (flen <= 0) return NULL; fp = fopen(fname, "rb"); if (!fp) return NULL; buf = malloc(flen+1); fread(buf, 1, flen, fp); buf[flen] = '\0'; return buf; } /** Print the first `head_len` bytes and last `tail_len` bytes of a string */ static void pr_head_tail(const char *s, size_t head_len, size_t tail_len) { const char *cp; size_t i; cp = s; for (i = 0; i < head_len; i++) putchar(*cp++); printf("\n...\n"); cp = &s[strlen(s) - tail_len]; while (*cp) putchar(*cp++); printf("\n"); } /** Print the first `head_len` bytes and last `tail_len` bytes of a text file */ static void pr_head_tail_file(const char *fname, size_t head_len, size_t tail_len) { char *buf; buf = file_read_to_string(fname); pr_head_tail(buf, head_len, tail_len); free(buf); } static int required_files_exist(void) // Check for required files in current working directory { const char *arrFiles[] = { "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", NULL }; const char *fname; int i; for (i = 0, fname = arrFiles[i]; fname; fname = arrFiles[++i]) { if (!file_exists(fname)) { printf("**ERROR: File %s is missing.\n", fname); return 0; } } return 1; } /**********************/ /* DO THE BUSINESS... */ /**********************/ int main(void) { long ret, nchars, n; char ch; char *buf = "", *buf1 = ""; char *fname; char *newname, *keyfile, *password, *certfile; char *attributeName, *elementName; char digest[SAT_MAX_HASH_CHARS+1]; /* Digest hex length plus one. */ char digest1[SAT_MAX_HASH_CHARS+1]; char numstr[21]; char cdate[64]; int i, j; char eName[64]; char qrybuf[256]; char *query, *newpassword; int has_bom; char *keyfiledata, *certfiledata; char *xmlstring, *newxmlstring; char uuid[37]; char attrbuf[128]; char *xbase; char xpath[256], xpath1[256]; /* Check if all required files exist in the CWD */ if (!required_files_exist()) { printf("Required file cannot be found in current working directory.\n"); exit(1); } printf("INTERROGATE THE CORE diFirmaSAT2 DLL:\n"); ret = SAT_Version(); printf("Version=%ld\n", ret); if (ret < MIN_VERSION) { printf("ERROR: STOP. Require FirmaSAT version %ld or higher.\n", MIN_VERSION); exit(1); } ch = (char)SAT_LicenceType(); printf("LicenceType=%c\n", ch); nchars = SAT_ModuleName(NULL, 0, 0); if (nchars > 0) { buf = malloc(nchars+1); nchars = SAT_ModuleName(buf, nchars, 0); printf("Module=%s\n", buf); free(buf); } assert(nchars > 0); nchars = SAT_ModuleName(NULL, 0, SAT_GEN_PLATFORM); if (nchars > 0) { buf = malloc(nchars+1); nchars = SAT_ModuleName(buf, nchars, 64); printf("Platform=%s\n", buf); free(buf); } assert(nchars > 0); nchars = SAT_CompileTime(NULL, 0); if (nchars > 0) { buf = malloc(nchars+1); nchars = SAT_CompileTime(buf, nchars); printf("Compiled=%s\n", buf); free(buf); } assert(nchars > 0); printf("\nFORM THE PIPESTRING FROM AN XML FILE: \n"); fname = "cfdv33a-base.xml"; nchars = SAT_MakePipeStringFromXml(NULL, 0, fname, SAT_ENCODE_UTF8); printf("SAT_MakePipeStringFromXml(UTF8) returns %ld\n", nchars); if (nchars >= 0) { buf = malloc(nchars+1); nchars = SAT_MakePipeStringFromXml(buf, nchars, fname, SAT_ENCODE_UTF8); printf("%s\n", buf); printf("Actual length=%ld\n", nchars); free(buf); } else { disp_error(nchars); } assert(nchars > 0); printf("\nSIGN AN XML FILE: \n"); fname = "cfdv33a-base.xml"; newname = "cfdv33a_new-signed.xml"; keyfile = "emisor.key"; password = "12345678a"; /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */ certfile = "emisor.cer"; ret = SAT_SignXml(newname, fname, keyfile, password, certfile, 0); printf("Sat.SignXml('%s'-->'%s') returns %ld\n", fname, newname, ret); assert (ret == 0); // Did we make a valid XML file? ret = SAT_ValidateXml(newname, 0); printf("SAT_ValidateXml(%s) returns %ld (0=>success)\n", newname, ret); assert (ret == 0); printf("\nVERIFY A SIGNATURE IN AN XML FILE: \n"); printf("1. One we know is good:\n"); fname = "cfdv33a-signed-tfd.xml"; ret = SAT_VerifySignature(fname, NULL, 0); printf("Sat.VerifySignature('%s') returns %ld\n", fname, ret); assert (ret == 0); printf("2. One we just made, so it should be good:\n"); fname = newname; ret = SAT_VerifySignature(fname, NULL, 0); printf("Sat.VerifySignature('%s') returns %ld\n", fname, ret); assert (ret == 0); printf("\nFORM THE DIGEST OF THE PIPESTRING IN AN XML FILE: \n"); fname = "cfdv33a-base.xml"; ret = SAT_MakeDigestFromXml(digest, sizeof(digest)-1, fname, 0); printf("SAT_MakeDigestFromXml returns %ld\n", ret); if (ret < 0) disp_error(ret); printf("Digest=%s\n", digest); assert (ret > 0); printf("\nEXTRACT THE DIGEST FROM THE SIGNATURE IN AN XML FILE: \n"); fname = "cfdv33a-signed-tfd.xml"; ret = SAT_ExtractDigestFromSignature(digest1, sizeof(digest1)-1, fname, NULL, 0); printf("SAT_ExtractDigestFromSignature returns %ld\n", ret); if (ret < 0) disp_error(ret); printf("Digest=%s\n", digest1); assert (ret > 0); /* check that the two digests match (caution: upper- vs lower-case) */ assert (stricmp(digest, digest1) == 0); printf("\nVALIDATE THE STRUCTURE OF XML FILES:\n"); printf("1. A valid one:\n"); fname = "cfdv33a-signed-tfd.xml"; ret = SAT_ValidateXml(fname, 0); printf("SAT_ValidateXml(%s) returns %ld (0=>success)\n", fname, ret); assert (ret == 0); printf("2. An invalid one (missing version):\n"); fname = "cfdv33a-bad-nover.xml"; ret = SAT_ValidateXml(fname, 0); printf("SAT_ValidateXml(%s) returns %ld (expected error)\n", fname, ret); assert (ret < 0); disp_error(ret); printf("\nEXTRACT AN ATTRIBUTE FROM AN XML FILE: \n"); fname = "cfdv33a-signed-tfd.xml"; elementName = "Comprobante"; attributeName = "Sello"; // NB Capital letter 'S' nchars = SAT_GetXmlAttribute(NULL, 0, fname, attributeName, elementName); if (nchars >= 0) { buf = malloc(nchars+1); nchars = SAT_GetXmlAttribute(buf, nchars, fname, attributeName, elementName); printf("SAT_GetXmlAttribute('%s','%s','%s')=\n%s\n", fname, attributeName, elementName, buf); free(buf); } else { disp_error(nchars); } assert(nchars >= 0); printf("\nGET DETAILS OF X.509 CERTIFICATE:\n"); printf("1. From embedded `certificado` in XML\n"); fname = "cfdv33a-signed-tfd.xml.xml"; printf("Input file: %s\n", fname); ret = SAT_GetCertNumber(numstr, sizeof(numstr)-1, fname, 0); printf("SAT_GetCertNumber returns %ld\n", ret); if (ret < 0) disp_error(ret); printf("serialNumber=%s\n", numstr); ret = SAT_GetCertExpiry(numstr, sizeof(numstr)-1, fname, 0); printf("SAT_GetCertExpiry returns %ld\n", ret); if (ret < 0) disp_error(ret); printf("notAfter=%s\n", numstr); printf("2. From X.509 certificate file\n"); fname = "emisor.cer"; printf("Input file: %s\n", fname); ret = SAT_GetCertNumber(numstr, sizeof(numstr)-1, fname, 0); printf("SAT_GetCertNumber returns %ld\n", ret); if (ret < 0) disp_error(ret); printf("serialNumber=%s\n", numstr); ret = SAT_GetCertExpiry(numstr, sizeof(numstr)-1, fname, 0); printf("SAT_GetCertExpiry returns %ld\n", ret); if (ret < 0) disp_error(ret); printf("notAfter=%s\n", numstr); printf("\nGET CERTIFICATE AS A BASE64 STRING:\n"); fname = "emisor.cer"; printf("For file %s...\n", fname); nchars = SAT_GetCertAsString(NULL, 0, fname, 0); printf("SAT_GetCertAsString(%s) returns %ld\n", fname, nchars); if (nchars < 0) disp_error(ret); assert(nchars > 0); if (nchars >= 0) { buf = malloc(nchars+1); nchars = SAT_GetCertAsString(buf, nchars, fname, 0); printf("%s\n", buf); printf("Actual length=%ld\n", nchars); //free(buf); // Keep string for later... } else { disp_error(nchars); } assert(nchars > 0); // Compare against string from XML file fname = "cfdv33a-signed-tfd.xml"; printf("For file %s...\n", fname); nchars = SAT_GetCertAsString(NULL, 0, fname, 0); if (nchars < 0) disp_error(ret); assert(nchars > 0); if (nchars >= 0) { buf1 = malloc(nchars+1); nchars = SAT_GetCertAsString(buf1, nchars, fname, 0); printf("Length of cert string=%ld\n", nchars); } else { disp_error(nchars); } assert(nchars > 0); // Compare strings if (strcmp(buf, buf1) == 0) printf("OK, cert strings are equal.\n"); else printf("ERROR: cert strings do not match!\n"); assert(strcmp(buf, buf1) == 0); free(buf); free(buf1); printf("\nMAKE A SIGNATURE FROM A BASE XML FILE: \n"); fname = "cfdv33a-base.xml"; keyfile = "emisor.key"; password = "12345678a"; /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */ nchars = SAT_MakeSignatureFromXml(NULL, 0, fname, keyfile, password); if (nchars >= 0) { buf = malloc(nchars+1); nchars = SAT_MakeSignatureFromXml(buf, nchars, fname, keyfile, password); printf("SAT_MakeSignatureFromXml('%s','%s')=\n%s\n", fname, keyfile, buf); free(buf); } else { disp_error(nchars); } assert(nchars > 0); printf("\nSIGN A DETALLISTA XML FILE: \n"); fname = "cfdv33a-detallista.xml"; newname = "detallista_new-signed.xml"; keyfile = "emisor.key"; password = "12345678a"; /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */ certfile = "emisor.cer"; ret = SAT_SignXml(newname, fname, keyfile, password, certfile, 0); printf("SAT_SignXml(%s) returns %ld (0=>success)\n", fname, ret); if (ret != 0) disp_error(ret); assert(0 == ret); // Did we make a valid XML file? fname = newname; ret = SAT_ValidateXml(fname, 0); printf("SAT_ValidateXml(%s) returns %ld (0=>success)\n", fname, ret); if (ret != 0) disp_error(ret); assert(0 == ret); // Is the signature valid? ret = SAT_VerifySignature(fname, NULL, 0); printf("SAT_VerifySignature(%s) returns %ld (0=>success)\n", fname, ret); if (ret != 0) disp_error(ret); assert(0 == ret); printf("\nEXTRACT AN ATTRIBUTE FROM A DETALLISTA XML FILE: \n"); fname = "cfdv33a-detallista.xml"; elementName = "detallista:detallista"; attributeName = "documentStructureVersion"; nchars = SAT_GetXmlAttribute(NULL, 0, fname, attributeName, elementName); if (nchars >= 0) { buf = malloc(nchars+1); nchars = SAT_GetXmlAttribute(buf, nchars, fname, attributeName, elementName); printf("SAT_GetXmlAttribute('%s','%s','%s')='%s'\n", fname, attributeName, elementName, buf); //free(buf); // Save string for later } else { disp_error(nchars); } assert(nchars > 0); // Check against the correct answer assert(strcmp(buf, "AMC8.1") == 0); free(buf); printf("\nEXTRACT AN ATTRIBUTE WITH ACCENTED CHARACTERS: \n"); fname = "cfdv33a-base.xml"; elementName = "cfdi:Emisor"; attributeName = "Nombre"; nchars = SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, fname, attributeName, elementName); if (nchars >= 0) printf("SAT_GetXmlAttribute('%s','%s','%s')='%s'\n", fname, attributeName, elementName, attrbuf); else disp_error(nchars); printf("\nEXTRACT AN ATTRIBUTE WITH ACCENTED CHARACTERS IN ITS NAME: \n"); printf("(May look 'funny' in Windows console)\n"); fname = "cfdv33a-nomina12.xml"; elementName = "nomina12:CompensacionSaldosAFavor"; attributeName = "Año"; nchars = SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, fname, attributeName, elementName); if (nchars >= 0) printf("SAT_GetXmlAttribute('%s','%s','%s')='%s'\n", fname, attributeName, elementName, attrbuf); else disp_error(nchars); printf("\nCHECK PRIVATE KEY MATCHES PUBLIC KEY IN CERTIFICATE: \n"); certfile = "emisor.cer"; keyfile = "emisor.key"; password = "12345678a"; /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */ ret = SAT_CheckKeyAndCert(keyfile, password, certfile, 0); printf("SAT_CheckKeyAndCert(%s,%s) returns %ld (expected 0)\n", keyfile, certfile, ret); if (ret != 0) disp_error(ret); assert(ret == 0); certfile = "pac.cer"; keyfile = "pac.key"; password = "12345678a"; ret = SAT_CheckKeyAndCert(keyfile, password, certfile, 0); printf("SAT_CheckKeyAndCert(%s,%s) returns %ld (expected 0)\n", keyfile, certfile, ret); if (ret != 0) disp_error(ret); assert(ret == 0); /* Get embedded certificate from XML doc */ certfile = "cfdv33a-signed-tfd.xml"; keyfile = "emisor.key"; password = "12345678a"; ret = SAT_CheckKeyAndCert(keyfile, password, certfile, 0); printf("SAT_CheckKeyAndCert(%s,%s) returns %ld (expected 0)\n", keyfile, certfile, ret); if (ret != 0) disp_error(ret); assert(ret == 0); printf("\nGET RECEIPT (COMPROBANTE) VERSION NUMBER FROM XML FILE: \n"); fname = "cfdv33a-base.xml"; ret = SAT_XmlReceiptVersion(fname, 0); printf("SAT_XmlReceiptVersion(%s)=%ld\n", fname, ret); if (ret <= 0) disp_error(ret); assert(33 == ret); // Older version... fname = "ejemplo_v32-tfd2015.xml"; ret = SAT_XmlReceiptVersion(fname, 0); printf("SAT_XmlReceiptVersion(%s)=%ld\n", fname, ret); if (ret <= 0) disp_error(ret); assert (32 == ret); printf("\nCREATE CADENA ORIGINAL DEL TIMBRE FISCAL DIGITAL (PIPESTRING FOR TFD): \n"); fname = "cfdv33a-signed-tfd.xml"; nchars = SAT_MakePipeStringFromXml(NULL, 0, fname, SAT_TFD); printf("SAT_MakePipeStringFromXml(%s, SAT_TFD) returns %ld\n", fname, nchars); if (nchars >= 0) { buf = malloc(nchars+1); nchars = SAT_MakePipeStringFromXml(buf, nchars, fname, SAT_TFD); printf("%s\n", buf); free(buf); } else { disp_error(nchars); } assert(nchars > 0); printf("\nFORM DIGEST OF PIPESTRING FOR TFD: \n"); fname = "cfdv33a-signed-tfd.xml"; ret = SAT_MakeDigestFromXml(digest, sizeof(digest)-1, fname, SAT_TFD); printf("SAT_MakeDigestFromXml returns %ld\n", ret); if (ret < 0) disp_error(ret); printf("SAT_MakeDigestFromXml(%s, SAT_TFD)=\n%s\n", fname, digest); assert (ret > 0); printf("\nEXTRACT DIGEST FROM TFD SELLOSAT: \n"); fname = "cfdv33a-signed-tfd.xml"; certfile = "pac.cer"; // NB certificate is required for TFD option ret = SAT_ExtractDigestFromSignature(digest1, sizeof(digest1)-1, fname, certfile, SAT_TFD); printf("SAT_ExtractDigestFromSignature returns %ld\n", ret); if (ret < 0) disp_error(ret); printf("SAT_ExtractDigestFromSignature(%s, SAT_TFD)=\n%s\n", fname, digest1); assert (ret > 0); /* check that the two digests match (CAUTION: upper- vs lower-case) */ assert (stricmp(digest, digest1) == 0); printf("\nPRETEND WE ARE A PAC WITH A KEY ALLOWED TO SIGN THE TFD: \n"); fname = "cfdv33a-signed-tfd.xml"; keyfile = "pac.key"; password = "12345678a"; /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */ nchars = SAT_MakeSignatureFromXmlEx(NULL, 0, fname, keyfile, password, SAT_TFD); if (nchars >= 0) { buf = malloc(nchars+1); nchars = SAT_MakeSignatureFromXmlEx(buf, nchars, fname, keyfile, password, SAT_TFD); printf("SAT_MakeSignatureFromXmlEx(%s,%s,SAT_TFD)=\n%s\n", fname, keyfile, buf); // free(buf); // Save for later... } else { disp_error(nchars); } assert(nchars > 0); // Extract the correct signature field from the XML... fname = "cfdv33a-signed-tfd.xml"; elementName = "tfd:TimbreFiscalDigital"; attributeName = "SelloSAT"; // NB Capital 'S' nchars = SAT_GetXmlAttribute(NULL, 0, fname, attributeName, elementName); if (nchars >= 0) { buf1 = malloc(nchars+1); nchars = SAT_GetXmlAttribute(buf1, nchars, fname, attributeName, elementName); printf("SAT_GetXmlAttribute(%s,%s,%s)=\n%s\n", fname, attributeName, elementName, buf1); // free(buf); // Save for later... } else { disp_error(nchars); } assert(nchars > 0); // Check against the correct answer assert(strcmp(buf, buf1) == 0); free(buf); free(buf1); printf("\nVERIFY SIGNATURE IN TFD SELLOSAT: \n"); fname = "cfdv33a-signed-tfd.xml"; certfile = "pac.cer"; ret = SAT_VerifySignature(fname, certfile, SAT_TFD); printf("SAT_VerifySignature(%s,%s,SAT_TFD')=%ld \n(expected 0)\n", fname, certfile, ret); assert (ret == 0); printf("\nADD A TFD ELEMENT TO A SIGNED CFDI DOCUMENT USING PAC KEY: \n"); fname = "cfdv33a-signed.xml"; newname = "cfdv33a_new-tfd.xml"; certfile = "pac.cer"; keyfile = "pac.key"; password = "12345678a"; n = SAT_SignXml(newname, fname, keyfile, password, certfile, SAT_TFD); printf("SAT_SignXml(SAT_TFD, '%s'-->'%s') returns %ld\n", fname, newname, n); assert(n == 0); // Did we make a valid XML file? n = SAT_ValidateXml(newname, 0); printf("SAT_ValidateXml('%s') returns %ld\n", newname, n); assert(n == 0); // Does it have a valid selloSAT? n = SAT_VerifySignature(newname, certfile, SAT_TFD); printf("SAT_VerifySignature('%s', SAT_TFD) returns %ld\n", newname, n); assert(n == 0); // Extract digests from the two signatures: `sello` and `selloSAT` n = SAT_ExtractDigestFromSignature(digest, sizeof(digest) - 1, newname, NULL, 0); assert(n > 0); printf("DIGEST(sello, '%s')=\n%s\n", newname, digest); // Note that the TFD selloSAT will be different each time it is created n = SAT_ExtractDigestFromSignature(digest, sizeof(digest) - 1, newname, certfile, SAT_TFD); assert(n > 0); printf("DIGEST(selloSAT, '%s')=\n%s\n", newname, digest); printf("\nADD UTF-8 BOM TO EXISTING FILE: \n"); fname = "cfdv33a-signed-nobom.xml"; newname = "cfdv33a_new-signed-with-BOM.xml"; n = SAT_FixBOM(newname, fname, 0); printf("SAT_FixBOM(%s->%s})=%ld (expected 0)\n", fname, newname, n); assert(n == 0); printf("\nEXTRACT ATTRIBUTES FROM CONSECUTIVE ELEMENTS:\n"); fname = "ejemplo_v32-tfd2015.xml"; attributeName = "descripcion"; elementName = "cfdi:Concepto"; printf("For file '%s'\n", fname); for (i = 1; i <= 100; i++) { char s[64]; sprintf(eName, "%s[%d]", elementName, i); SAT_GetXmlAttribute(s, sizeof(s)-1, fname, attributeName, eName); printf("SAT_GetXmlAttribute(%s,%s)=%s\n", attributeName, eName, s); if (strlen(s) == 0) { break; } } printf("\nVALIDATE XML WITH STRICT AND LOOSE OPTIONS:\n"); printf("Default strict behaviour (badly formed CURP attribute):\n"); fname = "V3_2_BadCurp.xml"; n = SAT_ValidateXml(fname, 0); printf("SAT_ValidateXml('%s') returns %ld\n", fname, n); disp_error(n); assert(n != 0); printf("\nUsing XmlOption.Loose:\n"); n = SAT_ValidateXml(fname, SAT_XML_LOOSE); printf("SAT_ValidateXml('%s', LOOSE) returns %ld\n", fname, n); assert(n == 0); printf("\nGET PRIVATE KEY AS BASE64:\n"); fname = "emisor.key"; password = "12345678a"; nchars = SAT_GetKeyAsString(NULL, 0, fname, password, 0); assert(nchars > 0); buf = malloc(nchars+1); nchars = SAT_GetKeyAsString(buf, nchars, fname, password, 0); printf("SAT_GetKeyAsString(%s)=\n%s\n", fname, buf); printf("SAT_GetKeyAsString(%s) length=%ld\n", fname, nchars); printf("\nWRITE PFX FROM PRIVATE KEY AND CERT:\n"); fname = "archivo_new-pfx.txt"; certfile = "emisor.cer"; keyfile = "emisor.key"; password = "12345678a"; newpassword = "clavedesalida"; ret = SAT_WritePfxFile(fname, newpassword, keyfile, password, certfile, 0); printf("SAT_WritePfxFile('%s') returns %ld\n", fname, n); assert(ret == 0); printf("New PFX file is %ld bytes\n", file_length(fname)); printf("\nQUERY X.509 CERTIFICATE:\n"); printf("1. From embedded `certificado` in XML\n"); fname = "cfdv33a-signed-tfd.xml"; printf("Input file: %s\n", fname); query = "notAfter"; ret = SAT_QueryCert(qrybuf, sizeof(qrybuf)-1, fname, query, 0); printf("SAT_QueryCert('%s')=[%s]\n", query, qrybuf); if (ret < 0) disp_error(ret); query = "notBefore"; ret = SAT_QueryCert(qrybuf, sizeof(qrybuf)-1, fname, query, 0); printf("SAT_QueryCert('%s')=[%s]\n", query, qrybuf); if (ret < 0) disp_error(ret); query = "serialNumber"; ret = SAT_QueryCert(qrybuf, sizeof(qrybuf)-1, fname, query, 0); printf("SAT_QueryCert('%s')=[%s]\n", query, qrybuf); if (ret < 0) disp_error(ret); query = "rfc"; ret = SAT_QueryCert(qrybuf, sizeof(qrybuf)-1, fname, query, 0); printf("SAT_QueryCert('%s')=[%s]\n", query, qrybuf); if (ret < 0) disp_error(ret); query = "organizationName"; ret = SAT_QueryCert(qrybuf, sizeof(qrybuf)-1, fname, query, 0); printf("SAT_QueryCert('%s')=[%s]\n", query, qrybuf); if (ret < 0) disp_error(ret); printf("2. From X.509 file\n"); fname = "pac.cer"; printf("Input file: %s\n", fname); query = "notAfter"; ret = SAT_QueryCert(qrybuf, sizeof(qrybuf)-1, fname, query, 0); printf("SAT_QueryCert('%s')=[%s]\n", query, qrybuf); if (ret < 0) disp_error(ret); query = "notBefore"; ret = SAT_QueryCert(qrybuf, sizeof(qrybuf)-1, fname, query, 0); printf("SAT_QueryCert('%s')=[%s]\n", query, qrybuf); if (ret < 0) disp_error(ret); query = "serialNumber"; ret = SAT_QueryCert(qrybuf, sizeof(qrybuf)-1, fname, query, 0); printf("SAT_QueryCert('%s')=[%s]\n", query, qrybuf); if (ret < 0) disp_error(ret); query = "rfc"; ret = SAT_QueryCert(qrybuf, sizeof(qrybuf)-1, fname, query, 0); printf("SAT_QueryCert('%s')=[%s]\n", query, qrybuf); if (ret < 0) disp_error(ret); query = "organizationName"; ret = SAT_QueryCert(qrybuf, sizeof(qrybuf)-1, fname, query, 0); printf("SAT_QueryCert('%s')=[%s]\n", query, qrybuf); if (ret < 0) disp_error(ret); printf("\nSIGN FILE WITHOUT BOM: \n"); fname = "cfdv33a-base.xml"; newname = "cfdv33a_new-signed-nobom.xml"; certfile = "emisor.cer"; keyfile = "emisor.key"; password = "12345678a"; n = SAT_SignXml(newname, fname, keyfile, password, certfile, SAT_FILE_NO_BOM); printf("SAT_SignXml('%s'-->'%s') returns %ld\n", fname, newname, n); assert(n == 0); // Did we make a valid XML file? n = SAT_ValidateXml(newname, 0); printf("SAT_ValidateXml('%s') returns %ld\n", newname, n); assert(n == 0); // Check new file does NOT have a BOM has_bom = file_has_bom(newname); printf("File '%s' %s a BOM\n", newname, (1 == has_bom ? "HAS" : "DOES NOT have")); assert(has_bom == 0); printf("\nREAD ENCRYPTED PRIVATE KEY FILE AS PEM STRING:\n"); fname = "emisor.key"; password = "12345678a"; nchars = SAT_GetKeyAsString(NULL, 0, fname, password, SAT_KEY_ENCRYPTED); assert(nchars > 0); buf = malloc(nchars+1); nchars = SAT_GetKeyAsString(buf, nchars, fname, password, SAT_KEY_ENCRYPTED); printf("SAT_GetKeyAsString(%s,SAT_KEY_ENCRYPTED)=\n%s\n", fname, buf); printf("SAT_GetKeyAsString(%s,SAT_KEY_ENCRYPTED) length=%ld\n", fname, nchars); printf("\nSIGN XML TO STRING:\n"); fname = "cfdv33a-base.xml"; // Read an XML file into a string xmlstring = file_read_to_string(fname); assert(xmlstring); printf("String from file '%s' has %ld bytes\n", fname, strlen(xmlstring)); /* 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. */ certfiledata = "-----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-----"; keyfiledata = "-----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 and certificate are matched ret = SAT_CheckKeyAndCert(keyfiledata, password, certfiledata, 0); printf("SAT_CheckKeyAndCert(STRINGS) returns %ld\n", ret); if (ret != 0) disp_error(ret); assert(ret == 0); // Create a new string containing signed XML (UTF-8 encoded) nchars = SAT_SignXmlToString(NULL, 0, xmlstring, keyfiledata, password, certfiledata, 0); printf("SAT_SignXmlToString returns %ld\n", nchars); assert(nchars >= 0); newxmlstring = malloc(nchars+1); nchars = SAT_SignXmlToString(newxmlstring, nchars, xmlstring, keyfiledata, password, certfiledata, 0); printf("Signed XML string has %ld bytes\n", strlen(newxmlstring)); printf("------------\n"); pr_head_tail(newxmlstring, 120, 320); printf("------------\n"); printf("\nPASS XML STRING TO OTHER SAT FUNCTIONS:\n"); n = SAT_ValidateXml(newxmlstring, 0); printf("SAT_ValidateXml(STRING) returns %ld (0=>OK)\n", n); assert(n == 0); n = SAT_XmlReceiptVersion(newxmlstring, 0); printf("SAT_XmlReceiptVersion(STRING) returns %ld (expecting 32)\n", n); assert(n > 0); free(newxmlstring); printf("\nSIGN XML USING EMPTY-ELEMENT TAGS:\n"); nchars = SAT_SignXmlToString(NULL, 0, xmlstring, keyfiledata, password, certfiledata, SAT_XML_EMPTYELEMTAG); printf("SAT_SignXmlToString returns %ld\n", nchars); assert(nchars >= 0); newxmlstring = malloc(nchars+1); nchars = SAT_SignXmlToString(newxmlstring, nchars, xmlstring, keyfiledata, password, certfiledata, SAT_XML_EMPTYELEMTAG); printf("Signed XML string has %ld bytes\n", strlen(newxmlstring)); printf("------------\n"); pr_head_tail(newxmlstring, 120, 262); printf("------------\n"); n = SAT_ValidateXml(newxmlstring, 0); printf("SAT_ValidateXml(STRING) returns %ld (0=>OK)\n", n); assert(n == 0); free(newxmlstring); // Free memory allocated for XML strings free(xmlstring); printf("\nGENERATE THREE UUIDs: \n"); ret = SAT_Uuid(uuid, sizeof(uuid)-1, 0); printf("UUID=%s\n", uuid); ret = SAT_Uuid(uuid, sizeof(uuid)-1, 0); printf("UUID=%s\n", uuid); ret = SAT_Uuid(uuid, sizeof(uuid)-1, 0); printf("UUID=%s\n", uuid); // NEW IN [v6.0]... printf("\nWORK WITH A `RETENCIONES` DOCUMENT: \n"); fname = "Ejemplo_Retenciones-base.xml"; printf("FILE=%s\n", fname); n = SAT_XmlReceiptVersion(fname, 0); printf("SAT_XmlReceiptVersion() returns ID=%ld (expecting 1010)\n", n); assert(1010 == n); n = SAT_MakeDigestFromXml(digest, sizeof(digest)-1, fname, 0); printf("SAT_MakeDigestFromXml() -> '%s')=\n", digest); assert(n > 0); // Use new [v6.0] arguments to find name of root element nchars = SAT_GetXmlAttribute(NULL, 0, fname, "", ""); printf("SAT_GetXmlAttribute('','') returns %ld\n", nchars); if (nchars >= 0) { buf = malloc(nchars+1); nchars = SAT_GetXmlAttribute(buf, nchars, fname, "", ""); printf("File root element is '%s'\n", buf); free(buf); } else { disp_error(nchars); } assert(nchars > 0); // NEW IN [v7.0]... printf("\nWORK WITH `CONTABILIDAD` DOCUMENTS: \n"); fname = "AAA010101AAA201501CT-base.xml"; printf("CATALOGOCUENTAS FILE=%s\n", fname); n = SAT_XmlReceiptVersion(fname, 0); printf("SAT_XmlReceiptVersion() returns ID=%ld (expecting 2011)\n", n); assert(2011 == n); printf("SIGN A CATALOGOCUENTAS DOCUMENT...\n"); newname = "AAA010101AAA201501CT_new-signed.xml"; keyfile = "emisor.key"; certfile = "emisor.cer"; password = "12345678a"; /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */ n = SAT_SignXml(newname, fname, keyfile, password, certfile, 0); printf("SAT_SignXml() returns %ld (expecting 0)\n", n); assert(0 == n); n = SAT_VerifySignature(newname, NULL, 0); printf("SAT_VerifySignature() returns %ld (expecting 0)\n", n); assert(0 == n); fname = "AAA010101AAA201501BN-base.xml"; printf("BALANZA FILE=%s\n", fname); n = SAT_XmlReceiptVersion(fname, 0); printf("SAT_XmlReceiptVersion() returns ID=%ld (expecting 2111)\n", n); assert(2111 == n); printf("MAKE THE SIGNATURE STRING FOR BALANZA...\n"); nchars = SAT_MakeSignatureFromXml(NULL, 0, fname, keyfile, password); if (nchars >= 0) { buf = malloc(nchars+1); nchars = SAT_MakeSignatureFromXml(buf, nchars, fname, keyfile, password); printf("SAT_MakeSignatureFromXml('%s','%s')=\n%s\n", fname, keyfile, buf); free(buf); } else disp_error(nchars); assert(nchars > 0); fname = "contab-SelloDigitalContElec-signed.xml"; printf("SELLODIGITALCONTELEC FILE=%s\n", fname); n = SAT_XmlReceiptVersion(fname, 0); printf("SAT_XmlReceiptVersion() returns ID=%ld (expecting 2511)\n", n); assert(2511 == n); printf("VERIFY SIGNATURE FOR SELLODIGITALCONTELEC USING PAC CERTIFICATE...\n"); n = SAT_VerifySignature(fname, "pac1024.cer", 0); printf("SAT_VerifySignature() returns %ld (expecting 0)\n", n); assert(0 == n); printf("\nWORK WITH `CONTROLESVOLUMETRICOS` DOCUMENT: \n"); fname = "ConVolE12345-base.xml"; printf("FILE=%s\n", fname); n = SAT_XmlReceiptVersion(fname, 0); printf("SAT_XmlReceiptVersion() returns ID=%ld (expecting 4011)\n", n); assert(4011 == n); printf("SIGN A CONVOL DOCUMENT WITH BIGFILE FLAG...\n"); 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"; /* CAUTION: DO NOT HARD-CODE REAL PASSWORDS! */ n = SAT_SignXml(newname, fname, keyfile, password, certfile, SAT_FILE_BIGFILE); printf("SAT_SignXml(BIGFILE) returns %ld (expecting 0)\n", n); assert(0 == n); n = SAT_VerifySignature(newname, NULL, 0); printf("SAT_VerifySignature() returns %ld (expecting 0)\n", n); assert(0 == n); printf("\nQUERY KEY SIZE OF CERTIFICATES...\n"); fname = "emisor1024.cer"; ret = SAT_QueryCert(qrybuf, sizeof(qrybuf)-1, fname, "keySize", 0); assert(ret > 0); printf("SAT_QueryCert('%s',keySize)=%s\n", fname, qrybuf); fname = "emisor.cer"; ret = SAT_QueryCert(qrybuf, sizeof(qrybuf)-1, fname, "keySize", 0); assert(ret > 0); printf("SAT_QueryCert('%s',keySize)=%s\n", fname, qrybuf); fname = "AC4_SAT.cer"; ret = SAT_QueryCert(qrybuf, sizeof(qrybuf)-1, fname, "keySize", 0); assert(ret > 0); printf("SAT_QueryCert('%s',keySize)=%s\n", fname, qrybuf); printf("\nQUERY SIGNATURE ALGORITHM IN CERTIFICATES...\n"); fname = "emisor1024.cer"; ret = SAT_QueryCert(qrybuf, sizeof(qrybuf)-1, fname, "sigAlg", 0); assert(ret > 0); printf("SAT_QueryCert('%s',sigAlg)=%s\n", fname, qrybuf); fname = "emisor.cer"; ret = SAT_QueryCert(qrybuf, sizeof(qrybuf)-1, fname, "sigAlg", 0); assert(ret > 0); printf("SAT_QueryCert('%s',sigAlg)=%s\n", fname, qrybuf); // NEW IN [v8.0] printf("\nREAD IN XML DOC AS 'ASCIIFIED' STRING...\n"); // "Esta es una demostración" -> "Esta es una demostración" fname = "cfdv33a-base.xml"; nchars = SAT_Asciify(NULL, 0, fname, 0); printf("SAT_Asciify() returns %ld\n", nchars); if (nchars >= 0) { xmlstring = malloc(nchars + 1); nchars = SAT_Asciify(xmlstring, nchars, fname, 0); } else { disp_error(nchars); } assert(nchars > 0); printf("ASCIIFIED XML:\n"); pr_head_tail(xmlstring, 1020, 44); printf("Compute digests from XML string and original file...\n"); ret = SAT_MakeDigestFromXml(digest, sizeof(digest) - 1, xmlstring, 0); if (ret < 0) disp_error(ret); printf("Digest(str) =%s\n", digest); assert(ret > 0); // -- this should match the digest of the original UTF-8-encoded file ret = SAT_MakeDigestFromXml(digest1, sizeof(digest1) - 1, fname, 0); if (ret < 0) disp_error(ret); printf("Digest(file)=%s\n", digest1); assert(ret > 0); assert(0 == strcmp(digest, digest1)); free(xmlstring); printf("\nINSERT CERTIFICATE DETAILS INTO XML...\n"); fname = "cfdv33a-base-nocertnum.xml"; newname = "cfdv33a-base_new-pluscert.xml"; certfile = "emisor.cer"; n = SAT_InsertCert(newname, fname, certfile, 0); printf("SAT_InsertCert() returns %ld (expecting 0)\n", n); assert(0 == n); // Check noCertificado just inserted // Original should be empty nchars = SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, fname, "NoCertificado", "cfdi:Comprobante"); printf("Old NoCertificado=[%s]\n", attrbuf); nchars = SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, newname, "NoCertificado", "cfdi:Comprobante"); printf("New NoCertificado=[%s]\n", attrbuf); assert(strlen(attrbuf) > 0); printf("\nINSERT CERTIFICATE DETAILS INTO XML AS STRING...\n"); nchars = SAT_InsertCertToString(NULL, 0, fname, certfile, 0); printf("SAT_InsertCertToString() returns %ld\n", nchars); if (nchars >= 0) { newxmlstring = malloc(nchars + 1); nchars = SAT_InsertCertToString(newxmlstring, nchars, fname, certfile, 0); } else { disp_error(nchars); } assert(nchars > 0); // Extract new attribute from XML as string nchars = SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, newxmlstring, "NoCertificado", "cfdi:Comprobante"); printf("New NoCertificado=[%s]\n", attrbuf); assert(nchars > 0); // NEW IN [v8.1] printf("\nSUPPORT FOR CONTABILIDAD V1.3...\n"); fname = "AAA010101AAA201705CT.xml"; printf("FILE: %s\n", fname); SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, fname, "", ""); printf("Doc type is '%s'\n", attrbuf); n = SAT_ValidateXml(fname, 0); printf("SAT_ValidateXml() returns %ld (0 => OK)\n", n); assert(0 == n); n = SAT_VerifySignature(fname, NULL, 0); printf("SAT_VerifySignature() returns %ld (0 => OK)\n", n); assert(0 == n); n = SAT_XmlReceiptVersion(fname, 0); printf("SAT_XmlReceiptVersion() returns %ld (expecting 2013)\n", n); assert(n > 0); // Get default digest algorithm for this document type (a hack!) n = SAT_XmlReceiptVersion(fname, SAT_GEN_DIGALG); printf("Default digest algorithm is SHA-%ld\n", n); // NEW IN [v8.2] printf("\nSAVE KEYFILE WITH NEW PASSWORD...\n"); certfile = "emisor.cer"; keyfile = "emisor.key"; password = "12345678a"; newname = "emisor_new.key"; newpassword = "password123"; n = SAT_NewKeyFile(newname, newpassword, keyfile, password, "", 0); printf("Sat.NewKeyFile() returns %ld (expecting 0)\n", n); assert(0 == n); printf("Created new key file '%s' of length %d bytes with password '%s'.\n", newname, file_length(newname), newpassword); printf("Save again in PEM format...\n"); newname = "emisor_new-key.pem"; newpassword = "password456"; n = SAT_NewKeyFile(newname, newpassword, keyfile, password, "", SAT_FORMAT_PEM); printf("Sat.NewKeyFile() returns %ld (expecting 0)\n", n); assert(0 == n); printf("Created new key file '%s' of length %d bytes with password '%s'.\n", newname, file_length(newname), newpassword); pr_head_tail_file(newname, 103, 109); printf("Check new key still matches old certificate...\n"); n = SAT_CheckKeyAndCert(newname, newpassword, certfile, 0); printf("Sat.NewKeyFile() returns %ld (expecting 0)\n", n); assert(0 == n); printf("\nXPATH EXPRESSIONS FOR XML-GET-ATTRIBUTE...\n"); fname = "A7.xml"; printf("FILE: %s\n", fname); elementName = "/Comprobante"; attributeName = "Version"; nchars = SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, fname, attributeName, elementName); assert(nchars > 0); // NOTE: we display here using the XPath '@' operator, but we don't accept it in an xpath expression. // Put the element path in `szElementName` and the attribute name in `szAttributeName`. printf("'%s/@%s'='%s'\n", elementName, attributeName, attrbuf); elementName = "/Comprobante/Conceptos/Concepto[2]/Impuestos/Traslados/Traslado[1]"; attributeName = "Importe"; nchars = SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, fname, attributeName, elementName); assert(nchars > 0); printf("'%s/@%s'='%s'\n", elementName, attributeName, attrbuf); elementName = "/Comprobante/Conceptos/Concepto[1]/Impuestos/Retenciones/Retencion[2]"; attributeName = "Importe"; nchars = SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, fname, attributeName, elementName); assert(nchars > 0); printf("'%s/@%s'='%s'\n", elementName, attributeName, attrbuf); // Same as above but shorter elementName = "//Conceptos/Concepto[1]//Retencion[2]"; attributeName = "Importe"; nchars = SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, fname, attributeName, elementName); assert(nchars > 0); printf("'%s/@%s'='%s'\n", elementName, attributeName, attrbuf); // Test for existence printf("Check for existence (and fail)...\n"); elementName = "/Comprobante/Conceptos/Concepto[3]"; attributeName = "Importe"; nchars = SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, fname, attributeName, elementName); printf("elemName='%s' attrName='%s' returned=%d\n", elementName, attributeName, nchars); // A missing element/attribute returns a negative error assert(nchars < 0); disp_error(nchars); printf("Invalid path expression...\n"); elementName = "/"; attributeName = "Version"; nchars = SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, fname, attributeName, elementName); printf("elemName='%s' attrName='%s' returned=%d\n", elementName, attributeName, nchars); // A missing element/attribute returns a negative error assert(nchars < 0); disp_error(nchars); printf("\nUSE XPATH TO FIND ALL ATTRIBUTES NAMED 'IMPORTE'...\n"); fname = "A7.xml"; printf("FILE: %s\n", fname); attributeName = "Importe"; // First look at each <Concepto> in the <Conceptos> element. xbase = "//Conceptos/Concepto"; for (i = 1;; i++) { // FOREACH //Conceptos/Concepto[i] element output the value of Importe sprintf(xpath, "%s[%d]", xbase, i); elementName = xpath; n = SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, fname, attributeName, elementName); if (n < 0) break; printf("'%s/@%s'='%s'\n", elementName, attributeName, attrbuf); // 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;; j++) { sprintf(xpath1, "%s//Traslado[%d]", xpath, j); elementName = xpath1; n = SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, fname, attributeName, elementName); if (n < 0) break; printf("'%s/@%s'='%s'\n", elementName, attributeName, attrbuf); } // FOREACH //Conceptos/Concepto[i]//Retencion[j] element output the value of Importe for (j = 1;; j++) { sprintf(xpath1, "%s//Retencion[%d]", xpath, j); elementName = xpath1; n = SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, fname, attributeName, elementName); if (n < 0) break; printf("'%s/@%s'='%s'\n", elementName, attributeName, attrbuf); } } // Now look in the /Comprobante/Impuestos element. xbase = "/Comprobante/Impuestos"; // FOREACH /Comprobante/Impuestos//Retencion[j] element output the value of Importe // Long xpath is /Comprobante/Impuestos/Retenciones/Retencion[j] for (j = 1;; j++) { sprintf(xpath1, "%s//Retencion[%d]", xbase, j); elementName = xpath1; n = SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, fname, attributeName, elementName); if (n < 0) break; printf("'%s/@%s'='%s'\n", elementName, attributeName, attrbuf); } // FOREACH /Comprobante/Impuestos//Traslado[j] element output the value of Importe for (j = 1;; j++) { sprintf(xpath1, "%s//Traslado[%d]", xbase, j); elementName = xpath1; n = SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, fname, attributeName, elementName); if (n < 0) break; printf("'%s/@%s'='%s'\n", elementName, attributeName, attrbuf); } // New in [v9.2] printf("\nREAD IN A FILE TO A VBA UNICODE STRING THEN PASS THE STRING AS XML DOC...\n"); fname = "cfdv33a-nomina12B.xml"; printf("FILE: %s\n", fname); // Use SAT_Asciify to solve issues when reading UTF-8-encoded file into a string nchars = SAT_Asciify(NULL, 0, fname, 0); printf("SAT_Asciify() returns %ld\n", nchars); assert(nchars > 0); xmlstring = malloc(nchars + 1); nchars = SAT_Asciify(xmlstring, nchars, fname, 0); printf("xmlstring is %d bytes long\n", strlen(xmlstring)); printf("\nCHECK XML IS OK USING XML STRING AS INPUT...\n"); n = SAT_ValidateXml(xmlstring, 0); printf("SAT_ValidateXml() returns %ld (expecting 0)\n", n); assert(0 == n); printf("\nGET ATTRIBUTE VALUE USING STRING INPUT...\n"); // Attribute name contains non-ASCII character 'ü', Antigüedad="P3Y2M23D" elementName = "nomina12:Receptor"; attributeName = "Antigüedad"; n = SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, fname, attributeName, elementName); assert(n > 0); printf("'%s/@%s'='%s'\n", elementName, attributeName, attrbuf); // Attribute value contains non-ASCII character 'í', Sindicalizado="Sí" elementName = "nomina12:Receptor"; attributeName = "Sindicalizado"; n = SAT_GetXmlAttribute(attrbuf, sizeof(attrbuf) - 1, fname, attributeName, elementName); assert(n > 0); printf("'%s/@%s'='%s'\n", elementName, attributeName, attrbuf); printf("\nFORM MESSAGE DIGEST OF PIPE STRING USING XML STRING AS INPUT...\n"); ret = SAT_MakeDigestFromXml(digest, sizeof(digest) - 1, xmlstring, 0); printf("SAT_MakeDigestFromXml(string) returns %ld\n", ret); if (ret < 0) disp_error(ret); printf("Digest=%s\n", digest); assert(ret > 0); printf("\nCHECK MESSAGE DIGEST IS THE SAME WHEN USING THE FILE AS INPUT...\n"); printf("FILE: %s\n", fname); ret = SAT_MakeDigestFromXml(digest1, sizeof(digest1) - 1, fname, 0); printf("SAT_MakeDigestFromXml(file) returns %ld\n", ret); if (ret < 0) disp_error(ret); printf("Digest=%s\n", digest1); assert(ret > 0); assert(strcmp(digest, digest1) == 0); // ****************************************************** // FINALLY, DISPLAY CURRENT DLL VERSION AND OTHER DETAILS printf("\n"); SAT_ModuleName(cdate, sizeof(cdate)-1, SAT_GEN_PLATFORM); printf("\nCore DLL Version=%03ld [%s] Lic=%c\n", SAT_Version(), cdate, (char)SAT_LicenceType()); SAT_ModuleName(qrybuf, sizeof(qrybuf)-1, 0); printf("[%s]\n", qrybuf); SAT_CompileTime(cdate, sizeof(cdate)-1); printf("Compiled [%s]\n", cdate); // Change "#if 0" to "#if 1" to activate this #if 0 printf("\nDISPLAY ALL POSSIBLE ERROR MESSAGES:\n"); display_all_errors(); #endif printf("\nALL DONE.\n"); return 0; } void display_all_errors(void) { long i, nchars; char errbuf[256]; for (i = 0; i < 10000; i++) { nchars = SAT_ErrorLookup(errbuf, sizeof(errbuf)-1, i); if (nchars > 0) printf("%ld=>%s\n", i, errbuf); } }