Buscar en este blog

lunes, 20 de mayo de 2013

Ejemplo Timbre Electrónico del DTE


Ejemplo Timbre Electrónico del DTE


Para aquellos que aun no pueden generar el timbre electrónico sobre su DTE, les voy a enseñar a realizarlo con las herramientas de C#. Muchos de nosotros cuando iniciamos en este tema recurríamos a clases externas para poder realizar esta técnica. Pues no teníamos el conocimiento ni los ejemplos para hacerlo. Bueno con el paso del tiempo y la mejora en la comprensión de los problemas criptográficos he podido factorizar el código para el Timbre del Documento DTE.


Ejemplo del SII

El SII en su documento Manual de Certificación
Titulo : TIMBRE ELECTRONICO DE EJEMPLO
pagina:  25
Del XML indicado se arma el string que será firmado (Mensaje)

Mensaje=
<DD><RE>97975000-5</RE><TD>33</TD><F>27</F><FE>2003-09-08</FE>
<RR>8414240-9</RR><RSR>JORGE GONZALEZ LTDA</RSR><MNT>502946</M
NT><IT1>Cajon AFECTO</IT1><CAF version="1.0"><DA><RE>97975000-
5</RE><RS>RUT DE PRUEBA</RS><TD>33</TD><RNG><D>1</D><H>200</H>
</RNG><FA>2003-09-04</FA><RSAPK><M>0a4O6Kbx8Qj3K4iWSP4w7KneZYe
J+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==<
/M><E>Aw==</E></RSAPK><IDK>100</IDK></DA><FRMA algoritmo="SHA1
withRSA">g1AQX0sy8NJugX52k2hTJEZAE9Cuul6pqYBdFxj1N17umW7zG/hAa
vCALKByHzdYAfZ3LhGTXCai5zNxOo4lDQ==</FRMA></CAF><TSTED>2003-09
-08T12:28:31</TSTED></DD>



Desde el Código de Autorización de Folios (CAF) el SII entrega la llave privada en
formato PEM con la cual se firmará el Mensaje.

-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBANGuDuim8fEI9yuIlkj+MOyp3mWHifoP6a4oWLSBKJSrd3MpEsZd
czvL0l7t/e0IU5rF+0gRLnU1Mfvtsw1wYWcCAQMCQQCLyV9FxKFLW09yWw7bVCCd
xpRDr7FRX/EexZB4VhsNxm/vtJfDZyYle0Lfy42LlcsXxPm1w6Q6NnjuW+AeBy67
AiEA7iMi5q5xjswqq+49RP55o//jqdZL/pC9rdnUKxsNRMMCIQDhaHdIctErN2hC
IP9knS3+9zra4R+5jSXOvI+3xVhWjQIhAJ7CF0R0S7SIHHKe04NUURf/7RvkMqm1
08k74sdnXi3XAiEAlkWk2vc2HM+a1sCqQxNz/098ketqe7NuidMKeoOQObMCIQCk
FAMS9IcPcMjk7zI2r/4EEW63PSXyN7MFAX7TYe25mw==
-----END RSA PRIVATE KEY-----

El resultado obtenido de la firma RSA-SHA1, utilizado la llave privada sobre el
mensaje es:

pqjXHHQLJmyFPMRvxScN7tYHvIsty0pqL2LLYaG43jMmnfiZfllLA0wb32lP+HBJ
/tf8nziSeorvjlx410ZImw==

El presente ejemplo muestra con claridad los elementos que deben utilizarse para generar el timbre. Ahora traspasaremos esta información al formato C#.

//////////////////////////////////////////////////////////////////////
//// BY: Marcelo Rojas R.
//// Dt: 16-05-2013
//// El ejercicio actual representa un ejemplo del SII
//// Donde se suministra el valor del nodo TED, es decir 
//// su contenido y posteriormente se calcula el timbre
//////////////////////////////////////////////////////////////////////
public static void PruebaTimbreDD()
{

   ////
   //// Contenido del nodo TED del ejemplo. 
   //// Este es el formato que debe tener los datos
   //// 
   string DD = string.Empty;
   DD += "<DD><RE>97975000-5</RE><TD>33</TD><F>27</F><FE>2003-09-08</FE>";
   DD += "<RR>8414240-9</RR><RSR>JORGE GONZALEZ LTDA</RSR><MNT>502946</M";
   DD += "NT><IT1>Cajon AFECTO</IT1><CAF version=\"1.0\"><DA><RE>97975000-";
   DD += "5</RE><RS>RUT DE PRUEBA</RS><TD>33</TD><RNG><D>1</D><H>200</H>";
   DD += "</RNG><FA>2003-09-04</FA><RSAPK><M>0a4O6Kbx8Qj3K4iWSP4w7KneZYe";
   DD += "J+g/prihYtIEolKt3cykSxl1zO8vSXu397QhTmsX7SBEudTUx++2zDXBhZw==<";
   DD += "/M><E>Aw==</E></RSAPK><IDK>100</IDK></DA><FRMA algoritmo=\"SHA1";
   DD += "withRSA\">g1AQX0sy8NJugX52k2hTJEZAE9Cuul6pqYBdFxj1N17umW7zG/hAa";
   DD += "vCALKByHzdYAfZ3LhGTXCai5zNxOo4lDQ==</FRMA></CAF><TSTED>2003-09";
   DD += "-08T12:28:31</TSTED></DD>";    

   ////
   //// Representa la clave privada rescatada desde el CAF que envía el SII
   //// para la prueba propuesta por ellos.
   ////
   string pk = string.Empty;
   pk += "MIIBOwIBAAJBANGuDuim8fEI9yuIlkj+MOyp3mWHifoP6a4oWLSBKJSrd3MpEsZd";
   pk += "czvL0l7t/e0IU5rF+0gRLnU1Mfvtsw1wYWcCAQMCQQCLyV9FxKFLW09yWw7bVCCd";
   pk += "xpRDr7FRX/EexZB4VhsNxm/vtJfDZyYle0Lfy42LlcsXxPm1w6Q6NnjuW+AeBy67";
   pk += "AiEA7iMi5q5xjswqq+49RP55o//jqdZL/pC9rdnUKxsNRMMCIQDhaHdIctErN2hC";
   pk += "IP9knS3+9zra4R+5jSXOvI+3xVhWjQIhAJ7CF0R0S7SIHHKe04NUURf/7RvkMqm1";
   pk += "08k74sdnXi3XAiEAlkWk2vc2HM+a1sCqQxNz/098ketqe7NuidMKeoOQObMCIQCk";
   pk += "FAMS9IcPcMjk7zI2r/4EEW63PSXyN7MFAX7TYe25mw==";
   

   //// 
   //// Este es el resultado que el SII indica debe obtenerse despues de crear
   //// el timbre sobre los datos expuestos.
   ////
   const string HTIMBRE = "pqjXHHQLJmyFPMRvxScN7tYHvIsty0pqL2LLYaG43jMmnfiZfllLA0wb32lP+HBJ/tf8nziSeorvjlx410ZImw==";

   
   //// //////////////////////////////////////////////////////////////////
   //// Generar timbre sobre los datos del tag DD utilizando la clave 
   //// privada suministrada por el SII en el archivo CAF
   //// //////////////////////////////////////////////////////////////////

   ////
   //// Calcule el hash de los datos a firmar DD
   //// transformando la cadena DD a arreglo de bytes, luego con
   //// el objeto 'SHA1CryptoServiceProvider' creamos el Hash del
   //// arreglo de bytes que representa los datos del DD
   ASCIIEncoding ByteConverter = new ASCIIEncoding();
   byte[] bytesStrDD = ByteConverter.GetBytes(DD);
   byte[] HashValue = new SHA1CryptoServiceProvider().ComputeHash(bytesStrDD);

   ////
   //// Cree el objeto Rsa para poder firmar el hashValue creado
   //// en el punto anterior. La clase FuncionesComunes.crearRsaDesdePEM()
   //// Transforma la llave rivada del CAF en formato PEM a el objeto
   //// Rsa necesario para la firma.
   RSACryptoServiceProvider rsa = FuncionesComunes.crearRsaDesdePEM(pk);

   ////
   //// Firme el HashValue ( arreglo de bytes representativo de DD )
   //// utilizando el formato de firma SHA1, lo cual regresará un nuevo 
   //// arreglo de bytes.
   byte[] bytesSing = rsa.SignHash(HashValue, "SHA1");

   ////
   //// Recupere la representación en base 64 de la firma, es decir de
   //// el arreglo de bytes 
   string FRMT1 = Convert.ToBase64String(bytesSing);

   ////
   //// Comprobación del timbre generado por nuestra rutina contra el
   //// valor 
   if (HTIMBRE.Equals(FRMT1))
   {
      Console.WriteLine("Comprobacion OK");
   }
   else
   {
      Console.WriteLine("Comprobacion NOK");
   }

}







Descargue aqui la clase que contiene el metodo de FuncionesComunes.crearRsaDesdePEM(pk) la cual permite transformar una clave privada en formato PEM a formato RSA.


Bueno espero que este codigo los ayude, especialmente a aquellos que recien comienzan con este tema.



46 comentarios:

  1. Muy buen blog, no me vas a creer que hace una semana atrás estuve viendo este tema, lo logré con una clase parecida a la que hiciste para tomar la clave privada PEM y generar el RSA.

    Estaría agradecido si tienes info sobre el envío automático de DTE a través de Socket.

    ResponderEliminar
  2. Tengo una clase que permite la autenticación automatica contra el SII publicada en el blog, dentro de estos días publicaré la fomr de realizar el envio de un libro y un SetDte al SII.

    Gracias por tu comentario

    ResponderEliminar
  3. Se encuentra disponible el codigo de envio de documentos al SII

    ResponderEliminar
  4. Estimado, primero que todo muchas gracias por este blog que me ha ayudado bastante. Quería consultarle sobre la "constante":

    const string HTIMBRE

    Es algún dato dentro del XML de la CAF???

    ResponderEliminar
    Respuestas
    1. Ricardo, esta constante solo representa el valor de resultado del ejercicio del SII expuesto en el archivo pdf de descarga mencionado al comienzo de este manual. La rutina que se muestra aqui es solo para indicar que utilizando este ejemplo se puede llegar al resultado del SII. Así que solo utilizalo para probar tus rutinas.

      Estiamdo, espero haber contestado a tu inquietud. cualquier duda me escribe.

      Eliminar
  5. Muy bueno el blog, me ha permitido resolver mis dudas sobre este tema y otros que vendran. gracias por compartir esta info.

    ResponderEliminar
  6. Que bueno que el material te haya sido de ayuda, la idea es que el conocimiento se comparta y no sea exclusivo de algunos.

    Gracias por su comentario.

    Atte.
    Marcelo Rojas Rojas

    ResponderEliminar
  7. Hola Marcelo,

    no te haces una idea de como me esta ayudando tu Blog. Muchas gracias por compartir tu conocimiento!

    Muchos saludos,
    Sebastian

    ResponderEliminar
  8. Hola Marcelo,
    Veo que estas bien al dia con este tema. Te agradeceria que me comentes si el tipo de PDF417 que debemos imprimir (ya lo he impreso en otrso documentos) puede distribuirse en menos columnas horizontales a fin de poder imprimirlo en un papel angosto de 5,7 cms. Vale decir, que el timpre salga un poco mas "largo" para acomodar los datos que no imprimimos a lo ancho. En algun lugar lei que la cantidad de data codificada en cada linea horizontal del pdf no podia variar para cumplir con lo que exige el SII. Ojala me puedas dar tu opinion al respecto. Gracias! Oliver

    ResponderEliminar
    Respuestas
    1. Estimado,

      La verdad es que este es un sueño de muchos emisores, puesto que al momento de emitir sus documentos generalmente el pdf417 queda feo en la presentación. Sin perjuicio de esto, según lo que yo sé, es que el pdf417 tiene un tamaño minimo y maximo definido por el SII. Esto para asegurar que los receptores electrónicos puedan leer con sus pistolas el famoso codigo impreso.

      Dejame buscar información acerca de esto, pero tengo la impresión que los tamaños no podras jugar mucho con ellos.


      Atte,
      Marcelo Rojas Rojas
      Analista Desarrollador

      Eliminar
    2. Lo mismo sospecho yo Marcelo, pero quiero estar seguro. Buzeando por el sitio del SII no he podido dar con algun documento (resolucion) que ponga reglas de ese estilo y la gente de la mesa telefonica sabe aun menos ;D. Si encuentras algo por ahi te estaria muy agradecido.
      Oliver

      Eliminar
    3. El tamaño minimo de impresion del PDF417 es 5x2 cm

      Eliminar
  9. Ya...duda despejada. Si es posible adaptar la impresion al tamaño del papel reduciendo las columnas de datos del timbre. En vez de 18 se podrian usar 12, teniendo como efecto alargar un poco el timbre cuidando de que la resolucion permita una lectura efectiva.

    OVT

    ResponderEliminar
    Respuestas
    1. Que bueno que puedas entonces adaptar el tamaño. Eso ayudará amuchos emisores a mejorar sus diseños. se agradece mucho la información Oliver.

      Atte,
      Marcelo Rojas Rojas

      Eliminar
  10. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  11. Estimado:
    Muchas gracias por la info. Me sirvió mucho ...



    ResponderEliminar
  12. Marcelo

    valoro enormemente tu aporte.. felicidades por el blog realmente no pensé que en Chile quedaran personas desinteresadas que dediquen su tiempo a los demás.

    te cuento que llevaba mucho tiempo en solo tratar de entender la forma en que funciona el tema de Facturación Electrónica para nuestro país, había tenido contacto cor personas como tu de México y España pero no era lo mismo en términos técnicos.

    te pasaste con este aporte lo comienzo a leer y me es de gran ayuda, gracias.

    éxito para tu vida.

    ResponderEliminar
  13. Estimado... es posible que me des el valor del digest que se va a firmar, es decir, el contenido de la variable HashValue.

    ResponderEliminar
  14. Marcelo, te felicito por tu generosidad para compartir el conocimiento. Saludos,

    ResponderEliminar
  15. Primero que cualquier cosa quiero darte las gracias por tus aportes. Pero en esta parte del envío es donde quede colgado

    ResponderEliminar
  16. Estimado, de verdad muy agradecido por su aporte (te pasaste viejo). El tema es que puede obtener la firma del DTE, pero en mi caso he tenido la posibilidad de comprobar las firmas con algunos DTEs generados desde otra aplicación que ya funciona y he podido conseguir la misma firma pero modificando el código, deje de usar ASSCIIEncoding:

    Dim bytesDatos As Byte() = Encoding.GetEncoding("iso-8859-1").GetBytes(DD)

    Con esto he obtenido la misma firma.
    Saludos

    ResponderEliminar
    Respuestas
    1. Yo también tuve que usar Encoding.GetEncoding("iso-8859-1").GetBytes(DD)

      Una forma de hacer "caer" el código original (con ASCIIEncoding) es cambiar esto:

      DD = ..... "Cajon AFECTO" .....

      por esto

      DD = ..... "Cajón AFECTO" .....

      Si uno hace ese cambio, Convert.ToBase64String(bytesSing) devuelve un FRMT incorrecto.

      Esto lo comprobé con una librería que funciona (LibreDTE), en ella se ocupa este código PHP para generar el valor del tag :

      openssl_sign($DD, $timbre, $Folios->getPrivateKey(), OPENSSL_ALGO_SHA1)

      $TED->getElementsByTagName('FRMT')->item(0)->nodeValue = base64_encode($timbre);

      Eliminar
  17. Gracias por tu ayuda en todo este tema de Factura Electrónica. Ahora estoy entrampado en generar el barcode del timbre. Espero tu código para ver si funciona ItextSharp.

    Saludos.

    Sergio Casaletti

    ResponderEliminar
  18. Disculpe, podría porfavor subir la función crearRsaDesdePEM
    La necesito urgente, de verdad me es muy importante. Gracias por este excelente blog!

    ResponderEliminar
  19. Una Pregunta.

    Ya genere un archivo con PHP que timbra el documento.

    ¿Con este string debo generar una imagen de codigo de barras??



    ResponderEliminar
    Respuestas
    1. Hola, como timbraste el documento en php¿..con alguna funcion o libreria?
      agradeceria me pudieras ayudar

      Eliminar
  20. Alguien sabe como crear un CAF para pruebas?
    Saludos,

    ResponderEliminar
  21. Marcelo....para el timbre del ted debo comparar la firma del caf con lo que me da al usar tu rutina de firma y ambas deben ser iguales??
    y si tomo la firma del caf y la inserto directo en el ted de cada documento correspondientes...es valido???

    ResponderEliminar
  22. Hola marcelo!, te agradeceria si me pudieras dar un ejemplo de como agregas el nodo caf al documento, esto de la factura ya me tiene con dolor de cabeza, gracias

    ResponderEliminar
  23. debes trabajar el caf como xdocument y utilizar la propiedad AddAfterSelf(SoloCaf) donde "soloCaf" es el nodo caf.

    ResponderEliminar
  24. Hola amigos, alguien me podria ayudar a hacerlo en php?

    ResponderEliminar
  25. ¿Como obtengo el texto para la generacion del pdf417 en c#?

    ResponderEliminar
  26. ¿Como genero las muestras impresas de papel contiguo? yo estoy generando la factura en la impresora termica, no la mando imprimir desde un archivo, la dibujo linea a linea incluyendo el pdf417

    ResponderEliminar
  27. Muy bueno el blog, el timbre electronico me resulto a la primera estoy con la ultima parte del documeto XML y me gustaria saber que es digestvalue, signaturevalue, modulus, exponent, X509Certificate???????????????

    ResponderEliminar
  28. Marcelo, en el ejemplo de la firma del timbre electrónico, colocas el valor esperado...como obtengo ese valor esperado? para validar si la firma esta correcta?
    gracias por tu ayuda...

    ResponderEliminar
  29. Hola, he tratado de realizar otra de las validaciones que se deben hacer y es validar el CAF antes de empezar a usarlo alguien tiene alguna funcion para verificar la valides del CAF. He tratado de encontrar la forma de validar con el modulo, exponente y public key pero no encuentro ejemplos que me lleven a buen puerto. ¿Alguien que me pueda ayudar?

    ResponderEliminar
  30. Gracias Marcelo por tu blog, muy útil.
    Podrías por favor proveer la función: funcionesComunes.crearRsaDesdePEM(pk)
    Atte
    Juan Soto

    ResponderEliminar
  31. Estimado, buenas tardes. Primeramente saludarte y agradecerte por tu apoyo en lo que respecta al código de generación de timbre electrónico. Quería comentarte que he realizado dicho código con un Folio de FACTURA y esta todo OK.

    Sin embargo, cuando quiero realizar el timbre electrónico con un Folio de FACTURA EXENTA, me sale error en esta linea: RSA.ImportParameters(RSAparams);

    Algun motivo en especial?

    Saludos.

    ResponderEliminar
    Respuestas
    1. Estimado,

      Es algo bien raro, pero me ha pasado. Te recomiendo que bajes nuevamente el archivo caf y pruebes nuevamente. resulta que aveces el caf no esta correcto y da errores, pero si lo vuelves a bajar sale bien.

      Prueba eso y me cuentas

      Atte.
      Marcelo Rojas Rojas
      997800285

      Eliminar
    2. Efectivamente Marcelo. Como era un proyecto que se retomo, dicho archivo de Folio para exentas tenia la fecha del 2015. Generé un nuevo archivo de Folio de esta fecha y pudo realizarse el timbraje electrónico.

      Muchas gracias. Éxistos!

      Eliminar
  32. Estimado marcelo, sabes que aparece un error de "acceso denegado" cuando ejecuta la setencia RSA.ImportParameters(RSAparamas) en la funcion DecodeRSAPrivateKey

    ResponderEliminar
  33. Holaaaa, espero n osea muy tarde. Me ha ayudado mucho la informacion, pero yo estoy trabajando en nodejs, tengo problema cuando en el caf la razon social tiene acentos o anies. en C#, no hay problema? haz probado timprando un caf con razon social con acentos, anies o apostrofes?

    ResponderEliminar