Example | Avoid XML-DSIG whitespace hassles | Source code to do this | Procedure | Passing key and certificate data as a string | How can we verify the signature? | Downloads | Required libraries | Contact
We show here how to create a CancelaCfdi XML document (Esquema cancelación CFDI versión 4.0) as per Actualización Factura electrónica - Reforma Fiscal 2022, digitally signed using XML-DSIG.
Here is an example, reformatted for display purposes (see output as a "flattened" one-line string).
<?xml version="1.0" encoding="utf-8"?> <CancelaCFD xmlns="http://cancelacfd.sat.gob.mx"> <Cancelacion xsi:schemaLocation="http://cancelacfd.sat.gob.mx CancelaCfdi.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Fecha="2022-01-29T15:45:12" RfcEmisor="AAA010101AAA"> <Folios> <Folio UUID="E3BA5811-FD07-4B9C-84B4-B146D7560575" Motivo="01" FolioSustitucion="20D816A0-BA64-4DF4-BE87-AA55E62F992F" /> </Folios> <Folios> <Folio UUID="29880559-5057-4871-9572-A222FD10BD97" Motivo="02" /> </Folios> <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> <SignedInfo> <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" /> <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /> <Reference URI=""> <Transforms> <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /> <DigestValue>YzhhA9+z+QYh9CTaYZ3CBegpdNg=</DigestValue> </Reference> </SignedInfo> <SignatureValue> QhrwPqmk0mbva9fPEcubk877qqXYrE0jFg0JROWFKK74mZHfpPp5MVBbL3CPf4z3LeSC4HZbN2ZRfBJ13Rstu47BVK4A7R+W4GsOC8q+Xg2Et4f80p HUSlKTt03uzorhvelVQbFGAgK0VQBldj80oSLH7UFs7VJCSLlb+CVjpQpBq93PFkU40/Aywf/LhoTT3Cy//FUkjxywyfMGJAmPxb45oewJGNgADH4m GBREUIp0WJtNBCYUxBEEt1J42hk4VkIT0Zid7kxIKoE9wB77uMZiBiu1Ry8iTBuJ7Ni9DYnHPeptw0/RgH2cBls0+i24XsIL5xzZYWMXZ7I91DsYMg== </SignatureValue> <KeyInfo> <!-- /CUT/ --> </KeyInfo> </Signature> </Cancelacion> </CancelaCFD>
There are issues with XML-DSIG in dealing with the white space between elements (indentations, tabs vs spaces, trailing white space, etc) which may get changed during transmission and invalidate the signature.
We avoid these hassles by removing it all. We "flatten" the input document before signing. That is, we create the signed output XML document in one long line with no white space between elements. This makes subsequent signature validations less prone to error, but makes the display less friendly.
<?xml version="1.0" encoding="utf-8"?><CancelaCFD xmlns="http://cancelacfd.sat.gob.mx"><Cancelacion xsi:schemaLocation="http://cancelacfd.sat.gob.mx CancelaCfdi.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Fecha="2022-01-29T15:45:12" RfcEmisor="AAA010101AAA"><Folios><Folio UUID="E3BA5811-FD07-4B9C-84B4-B146D7560575" Motivo="01" FolioSustitucion="20D816A0-BA64-4DF4-BE87-AA55E62F992F" /></Folios><!-- /CUT/ --></Cancelacion></CancelaCFD>
Note that if you add back any whitespace by, say, pretty-printing, the signature will be invalid.
C# code: SignEnvelopedSignature.cs. See Downloads below for the full set of source code, base XML template and test files; and Required Libraries.
cancelacion-2022-base.xml
with your own data in the elements shaded green.
You can have up to 10,000 <Folios>
elements. The <Signature> element should be copied exactly as is.
<?xml version="1.0" encoding="UTF-8"?> <CancelaCFD xmlns="http://cancelacfd.sat.gob.mx"> <Cancelacion xsi:schemaLocation="http://cancelacfd.sat.gob.mx CancelaCfdi.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Fecha="@!TIMESTAMP!@" RfcEmisor="AA010101AAA"> <Folios> <Folio UUID="E3BA5811-FD07-4B9C-84B4-B146D7560575" Motivo="01" FolioSustitucion="20D816A0-BA64-4DF4-BE87-AA55E62F992F"/> </Folios> <Folios> <Folio UUID="29880559-5057-4871-9572-A222FD10BD97" Motivo="02"/> </Folios> <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> <SignedInfo> <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> <Reference URI=""> <Transforms> <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <DigestValue>@!DIGVAL!@</DigestValue> </Reference> </SignedInfo> <SignatureValue>@!SIGVAL!@</SignatureValue> <KeyInfo> <KeyValue>@!RSAKEYVALUE!@</KeyValue> <X509Data> <X509IssuerSerial> <X509IssuerName>@!ISSUERNAME!@</X509IssuerName> <X509SerialNumber>@!SERIALNUMBER!@</X509SerialNumber> </X509IssuerSerial> <X509Certificate>@!CERTIFICATE!@</X509Certificate> </X509Data> </KeyInfo> </Signature> </Cancelacion> </CancelaCFD>Any white space between elements will be removed before processing, so any indentation is not important. The placeholders in the document in the form @!...!@ will be replaced by valid values. The "@!TIMESTAMP!@" placeholder will be replaced by the local system clock time in the correct ISO time format. If you want to hardcode a time there yourself, just overwrite it in the base file. The placeholders "@!DIGVAL!@" and "@!SIGVAL!@" are mandatory.
static string SignDoc(string outputxmlFile, string baseXmlFile, string certFile, string keyFile, string password, bool noFlatten = false)
The input parameters for SignEnvelopedSignature.SignDoc are:
If the program runs successfully, the output file specified in
- outputxmlFile
- Name of signed output file to be created.
- baseXmlFile
- Path to base XML document.
- certFile
- Filename of X.509 certificate file, or certificate-as-a-string.
- keyFile
- Filename of matching private key (or PEM string).
- password
- Password for private key file (
""
if unencrypted).- noFlatten
- Set as
true
to leave XML in original format (default=flatten to a single line).
outputxmlFile
will be created.
There are more notes on usage in the source code itself.
signedDoc = SignDoc("cancelacion-2022-signed.xml", "cancelacion-2022-base.xml", "emisor2021.cer", "emisor.key", password:"12345678a", noFlatten:false);
The final document (in one line) (zipped form 3 kB). Here is a reformatted pretty-print version, which displays nicely but will not validate because of its extra whitespace characters (hey, don't blame us, we didn't design XML-DSIG!).
keyFile
and certFile
parameters,
you can pass the data directly as a string. See
KeyCertsAsStrings.cs.Note that the certificate and key in this code are different from the latest test examples in the download.
Obviously you can verify this by submitting to SAT. If you have problems, please let us know with the error message you're getting. Please send us an example of your signed XML file that fails (ask us first, we'll get back to you on how to send it).
As an independent alternative, you can verify the signature using the Online XML Digital Signature Verifer. This will verify that the XML-DSIG signature is correct, but is not necessarily proof that the document satisfies all the SAT requirements. However, if it fails, something definitely needs to be fixed.
2022-03-20: See
Troubleshooting problems on the 'Online XML Digital Signature Verifier' site
cancelacion-2022-signed.xml
in a text editor (that's the one-line version, not the pretty-printed one).
The result:
SignEnvelopedSignature.cs.src | SignEnvelopedSignature.cs | SignEnvelopedSignature.csproj | +---Properties | AssemblyInfo.cs | \---Work CancelaCfdi.xsd cancelacion-2022-base.xml cancelacion-2022-signed-pretty.xml cancelacion-2022-signed.xml emisor.key emisor2021.cer
When ordering CryptoSys PKI Pro on the DI Management Services Store, check the "Add to Order" box for SC14N.
![]()
For more information or to comment on this page, please send us a message.
This page first published 1 October 2012. Last updated 15 November 2022.