This page is a complete rewrite of the original page written in 2009.
It takes a fresh look at using CryptoSys PKI Pro to sign EnvioDTE and EnvioBOLETA documents
using the standards for electronic invoices set by the
Servicio de Impuestos Internos (SII) of Chile ("Internal Revenue Service").
2020-11-11: Added code for VBA/VB6 users.
2021-03-03: New Version 1.2 of source code.
We have new tools available since we first published our old page on the subject in 2009 (original 2009 page kept for historical reference). In particular, we can do all the canonicalization and cryptographic operations in memory and avoid using intermediate canonicalized files like we had to do before. These new tools include SC14N and xmlsq. See New tools since 2009.
This page includes the full working code, written in both C# and VBA, for a program to sign either of these documents. There is also a program to verify an existing signed document, and code to extract your X.509 certificate from the PFX/P12 file provided by your e-certificate issuer.
We emphasise that we have no connection whatsoever with the Chilean Servicio de Impuestos Internos, and this site is in no way endorsed by them.
We look at two XML document specifications here: EnvioDTE and EnvioBOLETA (see xsd references). For signing purposes, these can be considered the same. Their structure is almost identical, just a few different nodes and different allowable TipoDTE values. We'll refer to these generically as an Envio document. The overall structure is as follows.
<EnvioDTE> or <EnvioBOLETA> <SetDTE ID="..."> <Caratula> (<DTE>)+ <Signature> </EnvioDTE> <DTE> <Documento ID="..."> <Signature> </DTE> <Documento> ... <TED version="1.0"> <DD> <FRMT> </TED> ... </Documento>
The outer Envio document contains a set of DTE documents, each of which is individually signed using XML-DSIG. Inside each DTE document is a special FRMT signature described below. The complete outer Envio document once assembled is then signed using XML-DSIG.
There are three parts to sign, which must be done in the correct order, working outwards.
The function takes as input a set of one or more template DTE documents prepared by the user plus an outer template for the EnvioDTE
or EnvioBOLETA
document.
Inside these templates are placeholders to be completed by the signing program. These are in the form @!...!@.
The program also requires the user's private signing key file, its password, the user's X.509 certificate, and CAF private key.
The input parameters for MakeEnvio.ProcessEnvioDoc are:
If the program runs successfully, the output file specified in
- outputxmlFile
- Name of output file to create.
- certFile
- Signer's X.509 certificate file (.cer). Extracted from the PFX file as explained below.
- keyFile
- Signer's private key file (.pfx). The original PFX file issued by your E-Cert provider, or your private signing key in any other supported form.
- password
- Password for private key file (
""
if unencrypted).- cafKeyFile
- CAF private key file. Extracted from the Autorizacion file as described below.
- outerTemplate
- XML template for outer Envio document. See Outer template.
- dtedocs
- List of DTE XML documents to be signed (comma-separated list of filenames). See DTE templates.
outputxmlFile
will be created. This should be in the correct format to be submitted to the SII.
As an extra check, use the VerifyDoc.VerifySignedEnvio function.
Example:
string envioDoc = MakeEnvio.ProcessEnvioDoc("output-enviodte.xml", // Output XML file to create "user.cer", // User's signing certificate "user.pfx", // User's private key file "password", // Password for private key file "caf33.key", // Private key for CAF "template-enviodte.xml", // Template for outer document, completed as necessary // (params) variable-length list of base DTE documents to be signed... "dte-33-1.xml", "dte-33-2.xml");All the test files used here are available in the downloads below.
White space is important! Do not reformat or pretty-print your document after signing - it will invalidate the signatures. Note also that the signatures will be different each time you run the program even on the same base documents, because the timestamp changes each time.
Please refer to the comments in the code modules MakeEnvio.cs and basMakeEnvio.bas. Here are some of the main points.
Encoding.GetBytes
method to do this.Prepare an outer template XML document for your Envio document and separate individual DTE template documents, as shown below.
You fill in the specific details marked in green. The placeholders shown as @!...!@ will be completed by the program.
<?xml version="1.0" encoding="ISO-8859-1"?> <EnvioDTE xmlns="http://www.sii.cl/SiiDte" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sii.cl/SiiDte EnvioDTE_v10.xsd" version="1.0"> <SetDTE ID="SetDoc"> <Caratula version="1.0"> <RutEmisor>11111111-1</RutEmisor> <RutEnvia>11111111-1</RutEnvia> <RutReceptor>60803000-K</RutReceptor> <FchResol>2020-07-31</FchResol> <NroResol>0</NroResol> <TmstFirmaEnv>@!TIMESTAMP!@</TmstFirmaEnv> <SubTotDTE> <TpoDTE>33</TpoDTE> <NroDTE>@!NUM-DTE!@</NroDTE> </SubTotDTE> </Caratula> <DTE>@!SET-OF-DTE!@</DTE> </SetDTE> <Signature>@!SIGNATURE!@</Signature> </EnvioDTE>
Prepare a set of separate DTE template files for processing similar to the following. The CAF element marked in blue should be copied verbatim from your Autorizacion file.
<DTE version="1.0"> <Documento ID="Ejemplo_F72T33"> <Encabezado> <IdDoc> <TipoDTE>33</TipoDTE> <Folio>72</Folio> <FchEmis>@!FECHA!@</FchEmis> </IdDoc> <Emisor> <RUTEmisor>11111111-1</RUTEmisor> <RznSocEmisor>EJEMPLO S.A.</RznSocEmisor> </Emisor> <Receptor> <RUTRecep>12345678-2</RUTRecep> <RznSocRecep>Empresa A&B el año 1999 Limitada</RznSocRecep> </Receptor> <Totales> <MntTotal>100000</MntTotal> <TasaIVA>19</TasaIVA> <IVA>19000</IVA> <MntTotal>119000</MntTotal> </Totales> </Encabezado> <Detalle> <NroLinDet>1</NroLinDet> <CdgItem> <TpoCodigo>INT1</TpoCodigo> <VlrCodigo>011</VlrCodigo> </CdgItem> <NmbItem>Parlantes Multimedia 180W.</NmbItem> <DscItem/> <QtyItem>20</QtyItem> <PrcItem>4500</PrcItem> <MontoItem>90000</MontoItem> </Detalle> <Detalle> ... </Detalle> <TED version="1.0"> <DD> <RE>11111111-1</RE> <TD>33</TD> <F>72</F> <FE>@!FECHA!@</FE> <RR>12345678-2</RR> <RSR>Empresa A&B el año 1999 Limitada</RSR> <MNT>119000</MNT> <IT1>Parlantes Multimedia 180W.</IT1> <CAF version="1.0"> <DA> <RE>11111111-1</RE> <RS>EJEMPLO S.A.</RS> <TD>33</TD> <RNG><D>1</D><H>200</H></RNG> <FA>2020-05-31</FA> <RSAPK><M>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</M><E>Aw==</E></RSAPK> <IDK>100</IDK> </DA> <FRMA algoritmo="SHA1withRSA">s8jKW4d5E9NR/gaVkllgzJGwAQmtn35v0ZIQEO99pFITXLOmmX/M0TL2uLQZL/b4FYLj2uWZhG3AZ8TOjGs0WQ==</FRMA> </CAF> <TSTED>@!TIMESTAMP!@</TSTED> </DD> <FRMT algoritmo="SHA1withRSA">@!FRMT-SIG!@</FRMT> </TED> <TmstFirma>@!TIMESTAMP!@</TmstFirma> </Documento> <Signature>@!SIGNATURE!@</Signature> </DTE>
The Documento ID must be unique for each DTE document in the set.
The example above contains a node with a couple of gotchas.
The recipient's business name (RznSocRecep/RSR) is "Empresa A&B el año 1999 Limitada"
.
This contains an ampersand symbol (&) which needs to be escaped in all your XML text files as A&B
.
The company name also contains the non-ASCII character (ñ) which we need to treat carefully because of encoding issues.
The Signature template is hard-coded in the code. This is the same for both the inner DTE and outer Envio templates. If any of the algorithms change, you will need to change this template and the default algorithms in the source code.
<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="#@!DOCID!@"> <Transforms> <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <DigestValue>@!DIGVAL!@</DigestValue> </Reference> </SignedInfo> <SignatureValue>@!SIGVAL!@</SignatureValue> <KeyInfo> <KeyValue> <RSAKeyValue><Modulus>@!RSA-MOD!@</Modulus><Exponent>@!RSA-EXP!@</Exponent></RSAKeyValue> </KeyValue> <X509Data> <X509Certificate>@!CERTIFICATE!@</X509Certificate> </X509Data> </KeyInfo> </Signature>
If you wish, you can replace the placeholders
@!CERTIFICATE!@
, @!RSA-MOD!@
and @!RSA-EXP!@
with your specific base64 values.
Use the VerifyDoc.VerifySignedEnvio function in the C# code module VerifyDoc.cs.
string s = VerifyDoc.VerifySignedEnvio("F60T33-ejemplo.xml"); Console.WriteLine(s); Debug.Assert("OK" == s, "VerifyDoc failed");
You should have been provided with one or more AUTORIZACION files similar to the following, probably with a filename like FOLIOSSIIXXX.XML.
1 <AUTORIZACION> 2 <CAF version="1.0"> 3 <DA> 4 <RE>11111111-1</RE> 5 <RS>EJEMPLO S.A.</RS> 6 <TD>33</TD> 7 <RNG><D>1</D><H>200</H></RNG> 8 <FA>2020-05-31</FA> 9 <RSAPK><M>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</M><E>Aw==</E></RSAPK> 10 <IDK>100</IDK> 11 </DA> 12 <FRMA algoritmo="SHA1withRSA">s8jKW4d5E9NR/gaVkllgzJGwAQmtn35v0ZIQEO99pFITXLOmmX/M0TL2uLQZL/b4FYLj2uWZhG3AZ8TOjGs0WQ==</FRMA> 13 </CAF> 14 <RSASK> 15 -----BEGIN RSA PRIVATE KEY----- 16 MIIBOwIBAAJBANGuDuim8fEI9yuIlkj+MOyp3mWHifoP6a4oWLSBKJSrd3MpEsZd 17 czvL0l7t/e0IU5rF+0gRLnU1Mfvtsw1wYWcCAQMCQQCLyV9FxKFLW09yWw7bVCCd 18 xpRDr7FRX/EexZB4VhsNxm/vtJfDZyYle0Lfy42LlcsXxPm1w6Q6NnjuW+AeBy67 19 AiEA7iMi5q5xjswqq+49RP55o//jqdZL/pC9rdnUKxsNRMMCIQDhaHdIctErN2hC 20 IP9knS3+9zra4R+5jSXOvI+3xVhWjQIhAJ7CF0R0S7SIHHKe04NUURf/7RvkMqm1 21 08k74sdnXi3XAiEAlkWk2vc2HM+a1sCqQxNz/098ketqe7NuidMKeoOQObMCIQCk 22 FAMS9IcPcMjk7zI2r/4EEW63PSXyN7MFAX7TYe25mw== 23 -----END RSA PRIVATE KEY----- 24 </RSASK> 25 <RSAPUBK> 26 -----BEGIN PUBLIC KEY----- 27 MFowDQYJKoZIhvcNAQEBBQADSQAwRgJBANGuDuim8fEI9yuIlkj+MOyp3mWHifoP 28 6a4oWLSBKJSrd3MpEsZdczvL0l7t/e0IU5rF+0gRLnU1Mfvtsw1wYWcCAQM= 29 -----END PUBLIC KEY----- 30 </RSAPUBK> 31 </AUTORIZACION>
caf33.key
.
This is the cafKeyFile
argument to be passed to the program.
The first line should begin with -----BEGIN RSA PRIVATE KEY-----
with no leading spaces.
This is an unencrypted private key used to create the FRMT signature element.
Keep it secret.
The program needs your signing X.509 certificate as a separate file (the certFile
argument). This is included in your PFX file.
To extract it, use the
GetCertsFromPfx.GetCerts function in the C# code module GetCertsFromPfx.cs.
VBA/VB6 users can use the
GetCertsFromPFX function in the module basGetCertsFromPFX.bas.
GetCertsFromPfx.GetCerts("user.pfx", "password");
This function will extract all the X.509 certificates in your PFX file (there may be more than one). You want your individual .cer file, the one issued to you. Double-click on each CER file to see the details in Windows Certificate Manager.
The FRMT signature is computed in a different way to the XML-DSIG signatures. It's actually easier to do. You extract the <DD> element and "flatten" it. That is, remove all white space between the elements to form a one-line string. The FRMT signature is computed over this string using SHA1withRSA with your 512-bit "caf" private RSA key.
<DD><RE>11111111-1</RE><TD>33</TD><F>72</F><FE>2020-09-27</FE><RR>12345678-2</RR><RSR>Empresa A&B el año 1999 Limitada</RSR><MNT>119000</MNT><IT1>Parlantes Multimedia 180W.</IT1><CAF version="1.0"><DA><RE>11111111-1</RE><RS>EJEMPLO S.A.</RS><TD>33</TD><RNG><D>1</D><H>200</H></RNG><FA>2020-05-31</FA><RSAPK><M>0a4O6Kbx8Qj3K4iWSP4w7KneZYeJ+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==</M><E>Aw==</E></RSAPK><IDK>100</IDK></DA><FRMA algoritmo="SHA1withRSA">s8jKW4d5E9NR/gaVkllgzJGwAQmtn35v0ZIQEO99pFITXLOmmX/M0TL2uLQZL/b4FYLj2uWZhG3AZ8TOjGs0WQ==</FRMA></CAF><TSTED>2020-09-27T18:37:31</TSTED></DD>
To summarize the rules:
&
),
the less than (<
) and greater than (>
) symbols, and the quote and apostrophe
(a.k.a. double and single quotes: "
'
)
must be escaped in the form
&
<
>
"
and '
respectively.
Note this is different from the XML-DSIG c14n rules.
// Use regex to extract the <DD> element. Match match = Regex.Match(xmlStr, @"(<DD.*?</DD>)", RegexOptions.Singleline); if (!match.Success) { // Handle error... } ddelem = match.Value; // Now flatten it - this is the input for the FRMT signature ddelem = Regex.Replace(ddelem, @">\s+<", @"><"); // Convert to bytes in Latin-1 encoding byte[] b = System.Text.Encoding.GetEncoding("iso-8859-1").GetBytes(ddelem); // Compute FRMT signature in base64 over the flattened, Latin-1-encoded <DD> element string frmtSig = Pki.Sig.SignData(b, sbCafKey.ToString(), "", Pki.SigAlgorithm.Rsa_Sha1);
Observant readers will notice that we can form a signature in two ways. We can use
Sig.SignData
and pass the data to be signed directly as a byte array (as we have done above).
Alternatively, we can compute the digest value of the data to be signed and pass this value to
Sig.SignDigest
(we do this in the source code).
Both ways work.
Sometimes it's handy when debugging to check the digest value.
These are the latest relevant XSD files we know of as at 1 October 2020: chilesii-xsd.zip (31 kB), with their SHA-1 digest values.
2017/12/12 18:18:10 GMT 233408 bytes DTE_v10.xsd 74460a0126cd2061521cc3c4d7ae8faabe573442 2020/09/03 09:02:38 GMT 53809 bytes EnvioBOLETA_v11.xsd 9e59658c1c5c1a14700c474cb720240272e24693 2011/05/27 14:38:06 GMT 4738 bytes EnvioDTE_v10.xsd 5de95b2d1d263e0e3c14491c89bdf196ea4077d3 2017/12/14 15:38:52 GMT 31096 bytes SiiTypes_v10.xsd b69b473e6bc609634e692731d3d1f5762a7c7141 2017/07/02 01:18:22 GMT 10293 bytes xmldsignature_v10.xsd 6e61b4fb62b292090b796b17b25e8215bca8e187
Version 1.2: Fixed MakeEnvio and VerifyDoc to cope with '
and "
entities in FRMT <DD> elements.
The original code used Xpath in xmlsq to extract and flatten the DD element. Unfortunately
Xpath converts XML entities '
and "
to literal characters, '
and "
(which is normal behaviour for XML processors).
So in the rare event that the DD element had text content with a quote or apostrophe in it, for example <RS>BERNADETTE O'CONNOR</RS>
,
this change would invalidate the signature.
The solution is to use a regex instead, as in the code above.
New tools now available:
To contact us or comment on this page, please send us a message.
This page last updated 15 November 2022