Ir al contenido


Foto

Libreria Para facturacion electronica en Delphi


  • Por favor identifícate para responder
31 respuestas en este tema

#1 IcebergDelphi

IcebergDelphi

    Advanced Member

  • Moderadores
  • PipPipPip
  • 176 mensajes
  • LocationVillaflores, Chiapas, Mexico

Escrito 02 diciembre 2010 - 11:43

Hola amigos les dejo el enlace a la libreria hecha en Delphi liberada gratuitamente por una empresa llamada bambucode.

La librería fue escrita, desde cero y de manera nativa para Delphi,  compatible desde su versión 2007 hasta Delphi XE aprovechando las  ventajas de la programación orientada a objetos que Delphi ofrece,  además de contener ayuda a través de Code Insight.

http://www.bambucode...nica_delphi.php

:cool:
  • 0

#2 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 02 diciembre 2010 - 01:31

Vamos a darle una revisada, muchas gracias por el enlace.

Salud OS
  • 0

#3 poliburro

poliburro

    Advanced Member

  • Administrador
  • 4.945 mensajes
  • LocationMéxico

Escrito 02 diciembre 2010 - 01:51

Gracias por el enlace. :D
  • 0

#4 markdelphi

markdelphi

    Advanced Member

  • Moderadores
  • PipPipPip
  • 97 mensajes
  • LocationMéxico

Escrito 02 diciembre 2010 - 03:20

La recomiendo, fue la que utilice en mi sistemas para la generación de los CFD.
  • 0

#5 fredycc

fredycc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 874 mensajes
  • LocationOaxaca, México

Escrito 02 diciembre 2010 - 05:05

Aportazo IcebergDelphi, muchas gracias. (y)
  • 0

#6 felipergueza

felipergueza

    Advanced Member

  • Miembros
  • PipPipPip
  • 83 mensajes

Escrito 31 enero 2011 - 10:58

La recomiendo, fue la que utilice en mi sistemas para la generación de los CFD.

Tendrán alguna imagen del formato de la factura electrónica.
Actualmente estoy trabajando con un modulo de facturación electrónica de Compaqi pero no me ha acabado de convencer.

  • 0

#7 ramflores

ramflores

    Member

  • Miembros
  • PipPip
  • 42 mensajes
  • LocationMonterrey, Mexico

Escrito 09 agosto 2011 - 07:32

Buen aporte, solo tengo una duda.


Soporte los nuevos esquemas de CDFI y el de codigo de barra CBB?


Gracias de antemano
  • 0

#8 Sergio

Sergio

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.092 mensajes
  • LocationMurcia, España

Escrito 12 agosto 2011 - 09:49

MUY interesante, pero me surge una gran gran pregunta: Para que pais esta pensada la libreria?

UPDATE: Es para Mejico... por cierto ¿como preferis los mexicanos/mejicanos, con la X o la J?

En cada pais se piden cosas diferentes, en españa por ejemplo hay un formato de factura electronica especifico para facturarle a la administracion publica que usa XML, pero en la web del link  no indica nada sobre pais, aunque las siglas que aparecen no me suenan de nada, asi que imagino que de españa no son.

Otro detalle: Vi en unas capturas de pantalla que utiliza como algoritmo de hash MD5 o SHA1... el MD5 esta muerto a nivel de seguridad, no lo useis o vuestra factura sera hackeable realtivamente facil (podran crear una factura falsa, pegarle la firma de la factura original, y que no se note en absoluto). El SHA1 esta moribundo, pero es el mas usado... igual el fin del mundo en 2012 es porque terminan de romper el algoritmo del SHA1 y el dinero electronico se hace facilmente hackeable de un dia para otro!

Aparte de estas cosillas que os pongo, es una gran noticia que exista esto, y si tengo tiempo aportare el codigo necesario para generar el XML español FacturaE, aunque como yo esto ya lo resolvi por otro lado, quizas sea mas practico dejar mi codigo en sus foros por si alguien necesita hacer la adaptacion al facturae.
  • 0

#9 defcon1_es

defcon1_es

    Member

  • Miembros
  • PipPip
  • 19 mensajes
  • LocationEspaña

Escrito 11 septiembre 2013 - 03:01

Hola a tod@s.

Esta semana me ha surgido la necesidad de crear facturas XML en formato FACTURAE (para España)
http://www.facturae....inas/Index.aspx

Sergio, ¿cómo lo resolviste?, ¿Puedes poner tu código?

Muchas gracias.
  • 0

#10 Sergio

Sergio

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.092 mensajes
  • LocationMurcia, España

Escrito 12 septiembre 2013 - 03:10

Esta semana me ha surgido la necesidad de crear facturas XML en formato FACTURAE (para España)
http://www.facturae....inas/Index.aspx

Sergio, ¿cómo lo resolviste?, ¿Puedes poner tu código?


Bueno, tienes tres tareas, así que te pongo tres trozos de código: Crear en XML la cabecera (datos del emisor y receptor, total de las facturas incluidas, etc), luego añadir cada una de las facturas (una o varias) con todos los datos obligatorios correctos y esas cosas (dentro van las líneas de factura), y finalmente tienes el tema de firmarlo como te piden.

Empiezo con código para el XML de cabecera, usamos un objetito simple que nos abre y cierra ramas en el XML, hay muchas clases parecidas y todas son sencillas de usar, pero nos creamos la nuestra porque... pues porque sí.

Veras algunas variables que no se definen, son parte del objeto que usamos, pero los nombres están claros: "total" es el toal de las facturas, etc.



delphi
  1.   salida:= TXMLGenerador.create;
  2.   QP:= TQueryPack.Create(Self);
  3.   // Leo los datos de la empresa emisora:
  4.   QP.EjecutaSQL('select direccion, CodigoPostal, Poblacion, cif, '+
  5.           '(select p.nombre from Provincia p '+
  6.                 ' where p.provincia=substring(codigopostal from 1 for 2)) '+
  7.           ' from empresa where empresa=1');
  8.  
  9.   salida.TextoLibre('<?xml version="1.0" encoding="ISO-8859-1"?>');
  10.   salida.Abre('fe:Facturae','xmlns:fe="http://www.facturae.es/Facturae/2009/v3.2/Facturae" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"');
  11.   salida.Abre('FileHeader');
  12. salida.Valor('SchemaVersion','3.2');
  13. salida.Valor('Modality','I');
  14. salida.Valor('InvoiceIssuerType','EM');
  15. salida.Abre('Batch');
  16.   salida.Valor('BatchIdentifier', BatchID);
  17.   salida.Valor('InvoicesCount',IntToStr(count));
  18.   salida.Abre('TotalInvoicesAmount');
  19.   salida.Valor('TotalAmount',FloatToSQL(Total));
  20.   salida.Cierra;
  21.   salida.Abre('TotalOutstandingAmount');
  22.   salida.Valor('TotalAmount',FloatToSQL(Total));
  23.   salida.Cierra;
  24.   salida.Abre('TotalExecutableAmount');
  25.   salida.Valor('TotalAmount',FloatToSQL(Total));
  26.   salida.Cierra;
  27.   salida.Valor('InvoiceCurrencyCode','EUR');
  28.   salida.Cierra;
  29.   salida.Cierra;
  30.  
  31.   // Partes
  32.   salida.Abre('Parties');
  33.   // Parte vendedora
  34.   salida.Abre('SellerParty');
  35.   salida.Abre('TaxIdentification');
  36.     salida.Valor('PersonTypeCode','J');
  37.     salida.Valor('ResidenceTypeCode','R');
  38.     salida.Valor('TaxIdentificationNumber',NifEmpresa);
  39.     if not DNIValido(NifEmpresa) then
  40.       result:= result + iif(result='','',', ')+'NIF "'+Trim(NifEmpresa)+'" inválido en la empresa';
  41.   salida.Cierra;
  42.   salida.Abre('LegalEntity');
  43.     salida.Valor('CorporateName',Base.Empresa);
  44.     salida.Abre('AddressInSpain');
  45.     salida.Valor('Address',QP[0].asString);
  46.     salida.Valor('PostCode',QP[1].asString);
  47.     if Length(Trim(QP[1].asString))<>5 then
  48.       result:= result + iif(result='','',', ')+'Código postal de la empresa "'+Trim(QP[1].asString)+'" no es válido';
  49.     salida.Valor('Town',QP[2].asString);
  50.     salida.Valor('Province',QP[4].asString);
  51.     salida.Valor('CountryCode','ESP');
  52.     salida.Cierra;
  53.   salida.Cierra;
  54.   salida.Cierra;
  55.  
  56.   // Parte compradora
  57.  
  58.   QP.EjecutaSQL('select Nif, Nombre, direccion, cpostal, poblacion, '+
  59.         '(select p.nombre from Provincia p '+
  60.               ' where p.provincia=substring(cpostal from 1 for 2)), cliente '+
  61.         'from cliente where Cliente='+IntToStr(Factura(0).Cliente));
  62. salida.Abre('BuyerParty');
  63. salida.Abre('TaxIdentification');
  64. salida.Valor('PersonTypeCode','J');
  65. salida.Valor('ResidenceTypeCode','R');
  66.   salida.Valor('TaxIdentificationNumber',QP[0].asString);
  67.     if not DNIValido(Trim(QP[0].asString)) then
  68.       result:= result + iif(result='','',', ')+'NIF "'+Trim(QP[0].asString)+'" invalido en el cliente nº'+QP[6].asString+' "'+Trim(QP[0].asString)+'"';
  69. salida.Cierra;
  70. salida.Abre('LegalEntity');
  71.   salida.Valor('CorporateName',QP[1].asString);
  72. salida.Abre('AddressInSpain');
  73. salida.Valor('Address',QP[2].asString);
  74. salida.Valor('PostCode',QP[3].asString);
  75.     if Length(Trim(QP[3].asString))<>5 then
  76.       result:= result + iif(result='','',', ')+'Código postal del cliente nº'+QP[6].asString+' "'+Trim(QP[3].asString)+'" no es válido';
  77. salida.Valor('Town',QP[4].asString);
  78. salida.Valor('Province',QP[5].asString);
  79. salida.Valor('CountryCode','ESP');
  80. salida.Cierra;
  81.   salida.Cierra;
  82.   salida.Cierra;
  83.   // Se acaban las partes
  84.   salida.Cierra;
  85.   // Empezamos con las facturas
  86.   salida.Abre('Invoices');
  87.  
  88.   for i:= 0 to count-1 do
  89.     Factura(i).SaveAsXML(salida); //Ver en el siguiente trozo de codigo
  90.  
  91.   // Se acabarón las facturas
  92.   Salida.Cierra;
  93.   //Se acabo el facturae
  94.   Salida.Cierra;
  95.   //A grabarse!
  96.   salida.SaveToStream(Stream);
  97.   if Base.FirmaDigital.Activa then
  98.     Base.FirmaDigital.FirmaStream(TStream(stream), '', tfXML); //Ver parte 3 de codigo
  99.   result:= iif(result='','',result+'.');
  100.   salida.Free;
  101.   QP.Free;



Por cierto, el formato XML para floats es el americano.

Bueno, ahora como añadir cada factura:



delphi
  1. procedure TXMLFacturaE.SaveAsXML(face: TXMLGenerador);
  2. var tipoiva, cuota: double;
  3.     obra, refcli, fecvto: string;
  4.     QP: TQueryPack;
  5.  
  6.   function Fecha(fecha: TDateTime): string;
  7.   var d,m,a: word;
  8.   begin
  9.     DecodeDate(fecha,a,m,d);
  10.     result:= IntToStr(a)+'-'+IntCero(m,2)+'-'+IntCero(d,2);
  11.   end;
  12.  
  13.   function Descripcion: string;
  14.   begin
  15.     if QP[0].asInteger = 1 then
  16.       result:= QP[2].asString
  17.     else
  18.       result:= QP[1].asString+' '+QP[2].asString;
  19.   end;
  20.  
  21. begin
  22.   inherited;
  23.  
  24.   QP:= TQueryPack.Create(Self);
  25.   QP.EjecutaSQL('select f.FacturaTxt, f.factura, f.ejercicio, f.serie'+
  26.         ',f.fecha,i.base, i.total, f.tipoiva, i.IVA '+
  27.         ',f.obra, o.referenciacliente, f.fechavence '+
  28.         ' from factura f '+
  29.         ' left join obra o on o.obra=f.obra '+
  30.         ' left join FacturaImportes('+IDFacturaStr+') i on 1=1 '+
  31.         ' where f.idfactura='+IDFacturaStr);
  32.  
  33.   facE.Abre('Invoice');
  34.   facE.Abre('InvoiceHeader');
  35.     facE.Valor('InvoiceNumber',QP[1].AsString);
  36.     facE.Valor('InvoiceSeriesCode',SerieEjercicio);
  37.     facE.Valor('InvoiceDocumentType','FC');
  38.     facE.Valor('InvoiceClass','OO');
  39.   facE.Cierra;
  40.   facE.Abre('InvoiceIssueData');
  41.     facE.Valor('IssueDate',fecha(QP[4].asDateTime));
  42.     facE.Valor('InvoiceCurrencyCode','EUR');
  43.     facE.Valor('TaxCurrencyCode','EUR');
  44.     facE.Valor('LanguageName','es');
  45.   facE.Cierra;
  46.   // Impuestos
  47.   facE.Abre('TaxesOutputs');
  48.     facE.Abre('Tax');
  49.     facE.Valor('TaxTypeCode','01');
  50.     facE.Valor('TaxRate',format('%1.2f',[QP[7].asDouble]));
  51.     facE.Abre('TaxableBase');
  52.       facE.Valor('TotalAmount',format('%1.2f',[QP[5].asDouble]));
  53.     facE.Cierra;
  54.     facE.Abre('TaxAmount');
  55.       facE.Valor('TotalAmount',format('%1.2f',[QP[8].asDouble]));
  56.     facE.Cierra;
  57.     facE.Cierra;
  58.   facE.Cierra;
  59.   // Totales
  60.   facE.Abre('InvoiceTotals');
  61.     facE.valor('TotalGrossAmount',format('%1.2f',[QP[5].asDouble]));
  62.     facE.valor('TotalGrossAmountBeforeTaxes',format('%1.2f',[QP[5].asDouble]));
  63.     facE.valor('TotalTaxOutputs',format('%1.2f',[QP[8].asDouble]));
  64.     // es obligatorio en f 3.2
  65.     facE.Valor('TotalTaxesWithheld','0.00');
  66.  
  67.     facE.valor('InvoiceTotal',format('%1.2f',[QP[6].asDouble]));
  68.     facE.valor('TotalOutstandingAmount',format('%1.2f',[QP[6].asDouble]));
  69.  
  70.     // es obligatorio en f 3.2
  71.     facE.abre('AmountsWithheld');
  72.     facE.Valor('WithholdingReason','Retencion');
  73.     facE.Valor('WithholdingRate','0.0000');
  74.     facE.Valor('WithholdingAmount','0.00');
  75.     facE.Cierra;
  76.  
  77.     facE.valor('TotalExecutableAmount',format('%1.2f',[QP[6].asDouble]));
  78.   facE.Cierra;
  79.   tipoiva:= QP[7].asDouble;
  80.   obra:=    QP[9].asString;
  81.   refcli:=  QP[10].asString;
  82.   fecvto:=  fecha(QP[11].AsDate);
  83.   // Lineas de factura
  84.   facE.Abre('Items');
  85.     QP.EjecutaSQL('select SinDescripcion,Descripcion,Notas, '+
  86.       ' unidades, precio, cast(unidades*precio as numeric(15,2)),l.ensayo '+
  87.       ' from FacturaLineas('+IDFacturaStr+') l');
  88.     while not QP.Eof do begin
  89.       cuota:= QP[5].AsDouble - Redondeo(QP[5].AsDouble/(1+(tipoiva/100)),2);
  90.       facE.Abre('InvoiceLine');
  91.       facE.valor('IssuerContractReference',Obra);
  92.       if RefCli <> '' then
  93.         facE.valor('ReceiverContractReference',RefCli,20);
  94.       facE.valor('ItemDescription', Descripcion);
  95.       facE.valor('Quantity',format('%1.2f',[QP[3].AsDouble]));
  96.       facE.valor('UnitOfMeasure','01');
  97.       facE.valor('UnitPriceWithoutTax',format('%1.6f',[QP[4].AsDouble]));
  98.       facE.valor('TotalCost',format('%1.6f',[QP[5].AsDouble]));
  99.       facE.valor('GrossAmount',format('%1.6f',[QP[5].AsDouble]));
  100.       facE.Abre('TaxesOutputs');
  101.         facE.Abre('Tax');
  102.         facE.valor('TaxTypeCode','01');
  103.         facE.valor('TaxRate',format('%1.2f',[tipoiva]));
  104.         facE.Abre('TaxableBase');
  105.           facE.valor('TotalAmount',format('%1.2f',[QP[5].AsDouble-cuota]));
  106.         facE.Cierra;
  107.         facE.Abre('TaxAmount');
  108.           facE.valor('TotalAmount',format('%1.2f',[cuota]));
  109.         facE.Cierra;
  110.         facE.Cierra;
  111.       facE.Cierra;
  112.       facE.valor('ArticleCode',trim(QP[6].AsString));
  113.       facE.Cierra;
  114.       QP.Next;
  115.     end;
  116.   facE.Cierra;
  117.   // Condiciones de pago  No es obligatorio
  118.   facE.Abre('PaymentDetails');
  119.     facE.Abre('Installment');
  120.     facE.valor('InstallmentDueDate', fecvto);
  121.     facE.valor('InstallmentAmount',format('%1.2f',[FTotal]));
  122.     facE.valor('PaymentMeans','01');
  123.     facE.Cierra;
  124.   facE.Cierra;
  125.   facE.Cierra;
  126.   QP.Free;
  127. end;



Y ahora la firma, yo utilizo la libreria SecureBlackBox, hay otras, pero la verdad, yo no la cambiaría, es barata, fiable, flexible, potente.. y las preguntas te las responden en minutos!

Por cierto, en su día publiqué en sus foros este mismo código, y en las versiones actuales de la librería tienes ya la opción de firmar en formato FacturaE basado en ese post, así que seguramente te puedas ahorrar todo este código, aunque yo lo prefiero así, sé lo que hace y porqué lo hace.

Veras que el código tiene muchos comentarios, es porque la información oficial está llena de errores y huecos, y solo mirando los ejemplos por dentro y probando con los validadores -que tampoco erán muy fiables cuando los probé- consegui que todo funcionase.



delphi
  1.   case TipoFirma of
  2.     tfXML: //Fichero XML para FacturaE (con politica de firmado V3.1)
  3.     begin
  4.       // ************************
  5.       // ** Firmar fichero XML **
  6.       // ************************
  7.       //
  8.       //Usamos FacturaE politica de firma V3.1:
  9.       //http://www.facturae.es/politica_de_firma_formato_facturae/politica_de_firma_formato_facturae_v3_1.pdf
  10.       //
  11.       //Creo objetos involucrados en la firma...
  12.       XML_Doc:=    ElXMLDOMDocument.create;            //Documento XML a ser firmado
  13.       XML_Refs:=    TElXMLReferenceList.create;          //Lista de nodos a ser firmados
  14.       XML_RefDocu:= TElXMLReference.create;              //Nodo que representa al XML completo
  15.       XML_RefCert:= TElXMLReference.create;              //Nodo del certificado utilizado
  16.       XML_Signer:=  TElXMLSigner.Create(nil);            //Objeto firmador en XML...
  17.       XML_XAdES:=  TElXAdESSigner.Create(nil);          //Subjeto firmador con info adicional XAdES
  18.       XML_KeyData:= TElXMLKeyInfoX509Data.create(false); //Certificado a utilizar...
  19.       try
  20.         //Leo el fichero XML
  21.         //==================
  22.         // Por defecto en UTF-8 y normalizando los finales de linea (si llegan
  23.         // CR+LF entonces la firma no se validará luego pues los digest no
  24.         // deberían incluir estos CR+LF).
  25.         XML_Doc.LoadFromStream(MS, '', true);
  26.         if not XML_Doc.Loaded then
  27.           raise Exception.create('Firma XML: No se pudo cargar el documento XML.');
  28.         //Configuro el objeto firmador de XML
  29.         //===================================
  30.         // Se han de firmar la lista de nodos referenciados en XML_Refs
  31.         XML_Signer.References:= XML_Refs;
  32.         // Quiero firma electronica, no claves secretas (MAC)
  33.         XML_Signer.SignatureMethodType:= xmtSig;
  34.         // Se firma en formato "enveloped"
  35.         XML_Signer.SignatureType:= xstEnveloped;
  36.         // Se canoniza -ver ejemplos de la web facturae- el XML
  37.         XML_Signer.CanonicalizationMethod:= xcmCanon;
  38.         // La firma con el metodo mas estandar posible
  39.         XML_Signer.SignatureMethod:= xsmRSA_SHA1;
  40.         // La parte publica del certificado se debe incluir
  41.         XML_Signer.IncludeKey:= true;
  42.         // Necesito incrustar info adicional "XAdES" (XML Advanced Electronic Signatures )...
  43.         XML_XAdES:= TElXAdESSigner.Create(nil);
  44.         XML_Signer.XAdESProcessor:= XML_XAdES;
  45.         //Configuro la parte XAdES del objeto firmador
  46.         //============================================
  47.         //
  48.         // Se pide usar XAdES V1.2.2 o superior pero ha de ser "compatible" (?)
  49.         // Algunos ejemplos usando V1.3.2 validan, asi que lo uso.
  50.         XML_Signer.XAdESProcessor.XAdESVersion:= XAdES_v1_3_2;
  51.         //SignerRole (en calidad de qué firmamos): supplier, customer o third_party
  52.         //  Firmamos facturas emitidas por nosotros (supplier) en este caso.
  53.         //  Si recibimos una factura firmada por el supplier, la podemos volver
  54.         //  a firmar como que la damos por recibida usando "customer".
  55.         //NOTA: El validador de [url=http://www.facturae.es]www.facturae.es[/url] no comprueba este dato (2-2010)
  56.         XML_Signer.XAdESProcessor.Included := [xipSignerRole];
  57.         XML_Signer.XAdESProcessor.SignerRole.ClaimedRoles.AddText(
  58.           XML_Signer.XAdESProcessor.XAdESVersion, XML_Doc, 'supplier');
  59.         // La politica de firmado es la definida en el PDF V3.1 sobre firmado Facturae
  60.         XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.Identifier:= '[url=http://www.facturae.es/politica_de_firma_formato_facturae/politica_de_firma_formato_facturae_v3_1.pdf';]http://www.facturae.es/politica_de_firma_formato_facturae/politica_de_firma_formato_facturae_v3_1.pdf';[/url]
  61.         //NOTA: Si vas a la web, este PDF esta en otro link diferente (WTF):
  62.         //'[url=http://www.facturae.es/es-ES/Documentacion/Politicas/Politicas/Versi%C3%B3n%203_1/Politica_Firma_formato_facturae_v3_1.pdf';]http://www.facturae.es/es-ES/Documentacion/Politicas/Politicas/Versi%C3%B3n%203_1/Politica_Firma_formato_facturae_v3_1.pdf';[/url]
  63.         XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.Description:= 'Política de firma electrónica para facturación electrónica con formato Facturae';
  64.         //Ahora el hash del propio PDF en SHA1 (en la web esta debajo del PDF)...
  65.         XML_Signer.XAdESProcessor.PolicyId.SigPolicyHash.DigestMethod:= '[url=http://www.w3.org/2000/09/xmldsig#sha1';]http://www.w3.org/2000/09/xmldsig#sha1';[/url]
  66.         //El digest en SHA-1 de la web (en formato hex), esta MAL, no corresponde
  67.         //con ese PDF, y ademas, en su ejemplo de la V3.0 el digest es correcto
  68.         //y NO se corresponde con el digest que dan!
  69.         // '613c46e7bac7df5b266e6be0349b5fe8bb4944e2' ESTA MAL EN LA WEB
  70.         //Uso el digest SHA1 correcto generado a partir del PDF:
  71.         SetLength(XML_Buf,0); //Evito un warning tonto
  72.         if not HexStr2ByteArray('3A18B197ABA90FA6AFF0DEE912F0C006110BEA13', XML_Buf) then
  73.           raise Exception.Create('Firma XML: Error convirtiendo digest de política de firmado a Base64.');
  74.         {NOTA: Sustituido por  HexStr2ByteArray()
  75.         XML_Digest:= LowerCase('3A18B197ABA90FA6AFF0DEE912F0C006110BEA13');
  76.         SetLength(XML_Buf, Length(XML_Digest) div 2);
  77.         //En delphi 7 HexToBin va bien, en Delphis mas moderno, no vale porque
  78.         //WideChar y pChar se tratan de forma diferente, asi que se hace a mano.
  79.     //HexToBin(PChar(XML_Digest), PChar(XML_Buf), Length(XML_Digest) div 2);
  80.         for j:= 0 to Length(XML_Buf)-1 do begin
  81.           k1:= SBMath.HexToDecDigit(XML_Digest[j*2 + 1]);
  82.           k2:= SBMath.HexToDecDigit(XML_Digest[j*2 + 2]);
  83.           if (k1<0) or (k2<0) then
  84.             raise Exception.Create('Firma XML: Error convirtiendo digest de política de firmado a Base64.');
  85.           XML_Buf[j]:= k1 shl 4 + k2;
  86.         end;}
  87.         XML_Signer.XAdESProcessor.PolicyId.SigPolicyHash.DigestValue:= XML_Buf;
  88.         //Anoto la fecha y hora del firmado (del PC, no es legalmente valida)
  89.         XML_Signer.XAdESProcessor.SigningTime := UtcNow;
  90.         //Pre-Genero los nodos de firmado necesarios
  91.         //==========================================
  92.         // Esta es la parte mas "confusa": He de firmar partes del XML que no
  93.         // existen hasta que este firmado ¿Como? El objeto Signer, junto con el
  94.         // prepocesador XAdES, crean esos nuevos nodos para que podamos
  95.         // referenciarlos -decirle al firmador que son unas referecnias a ser
  96.         // firmadas- y al final, al firmar, unira el XML original con estos
  97.         // nuevos nodos -formato de firma "enveloped"- y se graba el XML final.
  98.         //
  99.         // Primero genero la zona de informacion XAdES:
  100.         XML_Signer.XAdESProcessor.Generate;
  101.         // Ahora, al objeto firmador "generico" le pido lo mismo, con lo que
  102.         // tambien se crea el nodo "KeyInfo" con el certificado a utilizar.
  103.         XML_Signer.UpdateReferencesDigest;
  104.         //Cargo certificado para firmar
  105.         //=============================
  106.         // Cargo el certificado a usarse en la firma en el objeto firmador
  107.         // NOTA: Se incluira la parte publica del certificado en base64.
  108.         // De los certificados que me pases, elijo el primero con parte privada,
  109.         // ya que no quiero incluir el certificado raiz del emisor (no se
  110.         // menciona en la politica de firmado y no aparece en los ejemplos):
  111.         XML_KeyData.IncludeKeyValue:= true;
  112.         for i:= 0 to Cajon.Count-1 do begin
  113.           if Cajon.Certificates[i].PrivateKeyExists then begin
  114.             XML_KeyData.certificate:= Cajon.Certificates[i];
  115.             break;
  116.           end;
  117.         end;
  118.         //Compruebo que ha quedado algún certificado válido...
  119.         if not Assigned(XML_KeyData.certificate) then
  120.           raise Exception.create('FirmaXML: No se cargó un certificado válido para firmar, no contiene clave privada.');
  121.         XML_Signer.KeyData:= XML_KeyData;
  122.         //Añado a la lista de nodos a firmar
  123.         //==================================
  124.         //NOTA: El metodo SHA1 no es el mas seguro, pero el SHA256 no se usa mucho
  125.         //y puede dar problemas (WinXP SP2 no lo admite) por eso el DNIe tampoco
  126.         //lo usa aun, asi que mejor dejo el valor por defecto.
  127.         //
  128.         // NODO 1: Se ha de firmar el documento original XML completo...
  129.         //
  130.         XML_RefDocu.DigestMethod:= xdmSHA1; //El mas estandard/compatible de todos
  131.         XML_RefDocu.URINode:= XML_Doc.DocumentElement; //El XML completo
  132.         XML_RefDocu.URI:= ''; //No hay nombre especifico para este nodo
  133.         // En los ejemplos aparece la transformacion "Enveloped-Signature"
  134.         // aunque en el PDF de la firma V3.1 no lo menciona.
  135.         XML_RefDocu.TransformChain.Add(TElXMLEnvelopedSignatureTransform.Create);
  136.         XML_Refs.Add(XML_RefDocu); //Lo sumo a las cosas a ser firmadas
  137.         // Actualizo digest con este nuevo nodo.
  138.         XML_Signer.UpdateReferencesDigest;
  139.         // NODO 2: Se han de firmar las propiedades de firmado (XAdES)....
  140.         //
  141.         // El nodo SignedProperties es parte de la informacion XAdES que se
  142.         // creo en XML_Signer.XAdESProcessor.Generate y se firma siempre en
  143.         // el standard XAdES. Dejo el codigo abajo como referencia solo, pero
  144.         // no funcionaría si se activa porque ya esta siendo firmado una vez.
  145.         //XML_Signer.XAdESProcessor.QualifyingProperties.SignedProperties.ID:= 'SignedPropertiesID';
  146.         //XML_RefProp.DigestMethod:= xdmSHA1;
  147.         //XML_RefProp.URI:= '#SignedPropertiesID';
  148.         //XML_Refs.Add(XML_RefProp);
  149.         //XML_Signer.UpdateReferencesDigest;
  150.         //
  151.         // NODO 3: Se ha de firmar el propio certificado utilizado
  152.         //
  153.         // El nodo KeyInfo ha de ir identificado por 'Certificate1' -segun los
  154.         // ejemplos, el PDF no da ningun nombre concreto-
  155.         // si se firma dos veces un mismo XML no chocan aunque el
  156.         // nombre coincida por estar dentro de diferentes nodos "Signature".
  157.         XML_RefCert.URI:= '#Certificate1';
  158.         XML_RefCert.DigestMethod:= xdmSHA1;
  159.         XML_Refs.Add(XML_RefCert); //Lo sumo a las cosas a ser firmadas
  160.         //Como ya tengo todos los nodos, ahora genero la firma y consigo que
  161.         //exista el nodo KeyInfo, de forma que pueda darle el nombre que le toca
  162.         //antes de unir el XML original con el nuevo nodo de la firma.
  163.         XML_Signer.Sign;
  164.         XML_Signer.Signature.KeyInfo.ID:= 'Certificate1';
  165.         //Añado la firma al XML
  166.         //=====================
  167.         XML_Nodo:= ElXMLDOMNode(XML_Doc.DocumentElement);
  168.         XML_Signer.Save(XML_Nodo);
  169.         //Actualizo el stream MS con el XML final firmado.
  170.         MS.Position:= 0;
  171.         XML_Doc.SaveToStream(MS);
  172.         result:= true;
  173.         //Guardo XML firmado a fichero
  174.         //============================
  175.         //  Out_Stream:= TFileStream.create(SAveDialog1.FileName, fmCreate);
  176.         //  XML_Doc.SaveToStream(Out_Stream);
  177.         //  FreeAndNil(Out_Stream);
  178.       finally
  179.         //NOTA: Los XML_Ref se destruyen solos junto al XML_Refs
  180.         XML_Doc.free;
  181.         XML_Refs.free;
  182.         XML_Signer.free;
  183.         XML_XAdES.free;
  184.         XML_KeyData.Free;
  185.       end;
  186.     end; //XML



Bueno, un tocho como ves, me costos meses dejarlo funcionando -eso sí, aprendí de XML y firmas que no veas- así que me debes X cervezas, con X >> 10
  • 1

#11 defcon1_es

defcon1_es

    Member

  • Miembros
  • PipPip
  • 19 mensajes
  • LocationEspaña

Escrito 12 septiembre 2013 - 04:56

:wink:

Excelente post.

Yo peleándome con el xmlmapper de Delphi, con un XMLTransformProvider y 20 clientdataset, por los muchos campos TDataSetField que crea la transformación ...

Y lo que haces es construir tu el xml, sólo con las ramas que necesitas, no con todas (faltan <ThirdParty> y <FactoringAssignmentData>, por ejemplo)

(b) Te invitaré a los botellines  de cerveza que hagan falta cuando vengas a cuenca  (b)
  • 0

#12 apicito

apicito

    Advanced Member

  • Miembros
  • PipPipPip
  • 104 mensajes

Escrito 12 septiembre 2013 - 06:37

Y ahora la firma, yo utilizo la libreria SecureBlackBox, hay otras, pero la verdad, yo no la cambiaría, es barata, fiable, flexible, potente.. y las preguntas te las responden en minutos!

Dos preguntas sobre esta librería:
¿Permite firmar con certificados de la FNMT?
¿Permite incluir sellado de tiempo?.
Un saludo.
  • 0

#13 TiammatMX

TiammatMX

    Advanced Member

  • Miembros
  • PipPipPip
  • 1.750 mensajes
  • LocationUniverso Curvo\Vía Láctea\Sistema Solar\Planeta Tierra\América\México\Ciudad de México\Xochimilco\San Gregorio Atlapulco\Home

Escrito 12 septiembre 2013 - 02:27

... por cierto ¿como preferis los mexicanos/mejicanos, con la X o la J?...


Es indiferente, realmente..., pero por convención de la ONU, el nombre del país es México, con 'x'...
  • 0

#14 poliburro

poliburro

    Advanced Member

  • Administrador
  • 4.945 mensajes
  • LocationMéxico

Escrito 12 septiembre 2013 - 02:30

... por cierto ¿como preferis los mexicanos/mejicanos, con la X o la J?...


Es indiferente, realmente..., pero por convención de la ONU, el nombre del país es México, con 'x'...


:) Si me das a elegir amigo, yo prefiero Mexicanos :) 
  • 0

#15 defcon1_es

defcon1_es

    Member

  • Miembros
  • PipPip
  • 19 mensajes
  • LocationEspaña

Escrito 13 septiembre 2013 - 03:42

Hola Sergio.

He estado adaptando y probando tu código y el Servicio de validación de facturas electrónicas de la AEAT ya me valida la estructura y el formato contable !!

pero tengo un par de dudas:

1) ¿qué hace exactamente la funcion HexStr2ByteArray?

2) la variable XML_Buf, ¿es de tipo TByteArray = array[0..32767] of Byte;?


Por cierto, las líneas:
" XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.Identifier:= 'http://www.facturae....urae_v3_1.pdf'; "

y

" XML_Signer.XAdESProcessor.PolicyId.SigPolicyHash.DigestMethod:= 'http://www.w3.org/20.../xmldsig#sha1';"

no compilan tal cual están, no se si es un error debido al copy&paste....
  • 0

#16 Sergio

Sergio

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.092 mensajes
  • LocationMurcia, España

Escrito 13 septiembre 2013 - 04:22

Y ahora la firma, yo utilizo la libreria SecureBlackBox, hay otras, pero la verdad, yo no la cambiaría, es barata, fiable, flexible, potente.. y las preguntas te las responden en minutos!

Dos preguntas sobre esta librería:
¿Permite firmar con certificados de la FNMT?
¿Permite incluir sellado de tiempo?.
Un saludo.


Si, permite de todo, puedes firmar con certificados "fichero" como el de FNMT, con cualquier certificado cargado en tu windows (incluyendo tarjetas criptograficas, como el eDNI), permite aplicar time stamp (evidentemente tienes que configurar un servidor de tiempo gratuito o tener uno "pagado", creo que la marina española tiene uno gratuito pero no lo he investigado).
  • 0

#17 Sergio

Sergio

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.092 mensajes
  • LocationMurcia, España

Escrito 13 septiembre 2013 - 04:25

:wink:

Excelente post.

Yo peleándome con el xmlmapper de Delphi, con un XMLTransformProvider y 20 clientdataset, por los muchos campos TDataSetField que crea la transformación ...

Y lo que haces es construir tu el xml, sólo con las ramas que necesitas, no con todas (faltan <ThirdParty> y <FactoringAssignmentData>, por ejemplo)

(b) Te invitaré a los botellines  de cerveza que hagan falta cuando vengas a cuenca  (b)


XML = Fichero de texto, no hay que olvidarlo!
  • 0

#18 Sergio

Sergio

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.092 mensajes
  • LocationMurcia, España

Escrito 13 septiembre 2013 - 04:51

... por cierto ¿como preferis los mexicanos/mejicanos, con la X o la J?...


Es indiferente, realmente..., pero por convención de la ONU, el nombre del país es México, con 'x'...


Resulta muy dificil hablar de este tema "escribiendo", porque pronunciamos diferente, lo estuve comentando con una amiga mexicana en directo, y la cosa es así:

Cuando se puso el nombre de "Mexico", en el español de esa época se leía como lo leeis vosotros ahora, pero en España el sonido fue cambiando, y el antiguo sonido "x" equivale a la "j" nuestra actual.

Así que si un español "de ahora" lee la palabra "mexico" os sonaría rarísimo (la x sonaría como en "extraordinario") pero si lee "mejico" os sonaría perfecto, como si vosotros leyeseis "mexico".

Es igual que con "Don Quixote de la Mancha", ese es el nombre original, pero con el tiempo y para que siguiese sonando igual en España, se ha cambiado a "Quijote".
  • 0

#19 Sergio

Sergio

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.092 mensajes
  • LocationMurcia, España

Escrito 13 septiembre 2013 - 06:12

He estado adaptando y probando tu código y el Servicio de validación de facturas electrónicas de la AEAT ya me valida la estructura y el formato contable !!


Eso ya es para celebrarlo!

1) ¿qué hace exactamente la funcion HexStr2ByteArray?


Convierte un texto con una valor hexadecimal (como '3A18B197ABA90FA6AFF0DEE912F0C006110BEA13') en un array de bytes.

Tienes un código similar comentado justo debajo, en D7 es una línea, pero en versiones posteriores no vale así, por eso me enviaron código "todo terreno" los de Eldos que luego saque a una función.



delphi
  1. function HexStr2ByteArray(digest: string; var Buf: ByteArray): boolean;
  2. var j, k1, k2: integer;
  3. begin
  4.   try
  5.     //Longitud ha de ser par (van en parejas)...
  6.     if Length(Digest) mod 2 <> 0 then
  7.       raise Exception.create('Hex2ByteArray: El valor exadecimal no contiene un número par de dígitos.');
  8.     //Hexadecimal ha de estar en minusculas...
  9.     Digest:= LowerCase(Digest);
  10.     //Fijo tamaño del array of bytes final...
  11.     SetLength(Buf, Length(Digest) div 2);
  12.     //En delphi 7 HexToBin va bien, en Delphis mas moderno, no vale porque
  13.     //WideChar y pChar se tratan de forma diferente, asi que se hace a mano.
  14.     //HexToBin(PChar(XML_Digest), PChar(XML_Buf), Length(XML_Digest) div 2);
  15.     for j:= 0 to Length(Buf)-1 do begin
  16.       k1:= SBMath.HexToDecDigit(Digest[j*2 + 1]);
  17.       k2:= SBMath.HexToDecDigit(Digest[j*2 + 2]);
  18.       if (k1<0) or (k2<0) then
  19.         raise Exception.Create('Firma XML: Error convirtiendo digest de política de firmado a Base64.');
  20.       Buf[j]:= k1 shl 4 + k2;
  21.     end;
  22.     result:= true;
  23.   except
  24.     result:= false;
  25.   end;
  26. end;




2) la variable XML_Buf, ¿es de tipo TByteArray = array[0..32767] of Byte;?


Es ByteArray (sin la T en mi código), ese tipo se define en SBUtil o SBType (según la versión) y debe ser algo similar a lo que pones, aunque no lo he mirado por dentro (opté por usar los .dcu en lugar de las .pas en la librería, no recuerdo porqué!).

Por cierto, las líneas:
" XML_Signer.XAdESProcessor.PolicyId.SigPolicyId.Identifier:= 'http://www.facturae....urae_v3_1.pdf'; "

" XML_Signer.XAdESProcessor.PolicyId.SigPolicyHash.DigestMethod:= 'http://www.w3.org/20.../xmldsig#sha1';"

no compilan tal cual están, no se si es un error debido al copy&paste....


Es porque el editor del foro le ha añadido "[ u r l =" (sin los espacios) al principio de cada url y "[ / u r l ]" al final, quitaselo (el segundo añadido no es posible en pascal, está detras del ";") y prueba.

Un saludo y suerte con el firmado!

  • 1

#20 poliburro

poliburro

    Advanced Member

  • Administrador
  • 4.945 mensajes
  • LocationMéxico

Escrito 13 septiembre 2013 - 07:18


Es igual que con "Don Quixote de la Mancha", ese es el nombre original, pero con el tiempo y para que siguiese sonando igual en España, se ha cambiado a "Quijote".


Mira que cosa tan interesante nos has enseñado hoy. Toda la vida pensé que era "Don quijote"...

Como puede verse por cierto aquí:

http://books.google....quixote&f=false


Por cierto, veo que la V era usada como U en quellos años...


  • 0




IP.Board spam blocked by CleanTalk.