/* $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);
}
}