Ir al contenido


Foto

Libreria Para facturacion electronica en Delphi


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

#21 FerCastro

FerCastro

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 636 mensajes
  • LocationCiudad de México

Escrito 13 septiembre 2013 - 07:49

[quote author=TiammatMX link=topic=4320.msg79817#msg79817 date=1379017679
Es indiferente, realmente..., pero por convención de la ONU, el nombre del país es México, con 'x'...
[/quote]

En serio es indiferente?? Jelipe ó Felipe?


:wink:

- Por cierto, excelente herramienta y como agua en el desierto. Estoy comenzando a programar CFDI.


  • 0

#22 defcon1_es

defcon1_es

    Member

  • Miembros
  • PipPip
  • 19 mensajes
  • LocationEspaña

Escrito 13 septiembre 2013 - 08:30

Muchísimas gracias, Sergio.

sobre lo del TByteArray = array[0..32767] of Byte; (es la definicion que viene en Sysutils.pas),
como no tengo los fuentes de esos componentes, buscaba una alternativa  :angel:

El lunes le diré a mi jefe que los compre, que si no, no hay factura electronica  :dmad:

Un saludo y buen finde.
  • 0

#23 defcon1_es

defcon1_es

    Member

  • Miembros
  • PipPip
  • 19 mensajes
  • LocationEspaña

Escrito 16 septiembre 2013 - 03:47

Un millón de Gracias, Sergio !!

Archivos adjuntos


  • 0

#24 Sergio

Sergio

    Advanced Member

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

Escrito 16 septiembre 2013 - 05:55

Un millón de Gracias, Sergio !!


De nada chaval, recuerda lo de las birras si pasas por Murcia algún día!  (b)
  • 0

#25 ManuelCornejo

ManuelCornejo

    Newbie

  • Miembros
  • Pip
  • 1 mensajes

Escrito 19 febrero 2014 - 05:22

Hola Sergio,

Muchas gracias por publicar tu código.  Me ha sido de gran ayuda!!

Un saludo.


  • 0

#26 Sergio

Sergio

    Advanced Member

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

Escrito 19 febrero 2014 - 02:19

Hola Sergio,

Muchas gracias por publicar tu código.  Me ha sido de gran ayuda!!

Un saludo.


De nada, me alegrar evitar el calvario que me tocó para entender el lio del FacturaE, que por cierto para los que no seaís españolitos: no se ha llegado a utilizar realmente, tras varios años siendo "obligatorio" para facturar a la administración aún no me he cruzado ni con una verdadera factura en ese formato ni con ningún cliente preguntándome sobre el tema (y muchos trabajan para la administración), por lo que es un fracaso total.

En fín, como diría el genial humorista Gila: "Me habeis matado un hijo, pero y lo que nos hemos reido?" (aconsejo mirarlo por YouTube, habla de la guerra normalmente y es muy personal su humos, por cierto, Gila fué fusilado en la guerra civil española, por los años 30, pero no murió, así que él puede bromear con la guerra todo lo que quiera, se lo ganó).
  • 0

#27 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.446 mensajes
  • LocationMéxico

Escrito 19 febrero 2014 - 04:32


Hola Sergio,

Muchas gracias por publicar tu código.  Me ha sido de gran ayuda!!

Un saludo.


De nada, me alegrar evitar el calvario que me tocó para entender el lio del FacturaE, que por cierto para los que no seaís españolitos: no se ha llegado a utilizar realmente, tras varios años siendo "obligatorio" para facturar a la administración aún no me he cruzado ni con una verdadera factura en ese formato ni con ningún cliente preguntándome sobre el tema (y muchos trabajan para la administración), por lo que es un fracaso total.

En fín, como diría el genial humorista Gila: "Me habeis matado un hijo, pero y lo que nos hemos reido?" (aconsejo mirarlo por YouTube, habla de la guerra normalmente y es muy personal su humos, por cierto, Gila fué fusilado en la guerra civil española, por los años 30, pero no murió, así que él puede bromear con la guerra todo lo que quiera, se lo ganó).


Contrariamente acá en México a entrado con todo éste año, sólo faltamos los que facturamos por honorarios y que entrará en vigor en Abril de éste año. Lo bueno es que hay un mercado creciente de sistemas de facturación :)

Saludos
  • 0

#28 Sergio

Sergio

    Advanced Member

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

Escrito 20 febrero 2014 - 04:55

Contrariamente acá en México a entrado con todo éste año, sólo faltamos los que facturamos por honorarios y que entrará en vigor en Abril de éste año. Lo bueno es que hay un mercado creciente de sistemas de facturación :)

Saludos


El otro día leí que en suramerica ahora se nos llama "euracas" a nosotros  :D lo tenemos bien merecido por este tipo de cosas!
  • 0

#29 francastro

francastro

    Newbie

  • Miembros
  • Pip
  • 1 mensajes

Escrito 04 marzo 2015 - 08:17

Retomo este post, pues no me aclaro con el código de sergio, si fueras tan amable de dar una pequeña explicación: XMLGenerator es la unidad de Berend de boer?, supongo que no pues no existen las referencias abre,cierra, valor, entonces el objjeto que usas "Salida" que es?.
Saludos a todos.
Gracias por anticipado por vuestras explicaciones.
  • 0

#30 ramherfer

ramherfer

    Newbie

  • Miembros
  • Pip
  • 1 mensajes

Escrito 21 mayo 2021 - 05:02

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

 

 

Por favor, ¿podrías incluir el objeto Abre y Cierra? las instrucciones que incluyes son una verdadera maravilla, pero "no soy capaz" de realizar esas operaciones con éxito.

Gracias.


  • 0

#31 defcon1_es

defcon1_es

    Member

  • Miembros
  • PipPip
  • 19 mensajes
  • LocationEspaña

Escrito 18 agosto 2021 - 01:49

Por favor, ¿podrías incluir el objeto Abre y Cierra? las instrucciones que incluyes son una verdadera maravilla, pero "no soy capaz" de realizar esas operaciones con éxito.
Gracias.

 
Mira si esta clase te puede servir, tiene los metodos AbreNodo, CierraNodo y ValorNodo (abre el nodo, añade el valor y lo cierra):
 
 
 

delphi
  1. unit ClaseXMLGenerador;
  2.  
  3. interface
  4.  
  5. uses Classes, SysUtils, StrUtils;//, AnsiStrings;
  6.  
  7. type
  8.   TXMLGenerador = class(TComponent)
  9.   private
  10.     { private declarations }
  11.     FListaTexto: TStringList;
  12.     function  GetStrings:string;
  13.     procedure SetStrings(Valor:string);
  14.   protected
  15.     { protected declarations }
  16.  
  17.   public
  18.     { public declarations }
  19.     constructor Create(AOwner:TComponent); override;
  20.     destructor Destroy; override;
  21.  
  22.     procedure TextoLibre(const Texto:String);
  23.     procedure AbreNodo(const Nodo:String);
  24.     procedure CierraNodo(const Nodo:String);
  25.     procedure ValorNodo(const Nodo, Valor:String);
  26.     procedure GuardarEnFichero(const Fichero:String);
  27.     procedure GuardarEnStream(var VariableStream: TStream);
  28.   published
  29.     property Texto: string read GetStrings write SetStrings;
  30.   end;
  31.  
  32.   function QuitarSaltosLinea(Strs: TStrings; CharReplace:String=''):String;
  33.  
  34.  
  35. implementation
  36.  
  37. function QuitarSaltosLinea(Strs: TStrings; CharReplace:String=''):String;
  38. var Str1, Str2:string;
  39. begin
  40.    Str1 := StrUtils.AnsiReplaceStr(Strs.Text, #10, CharReplace);
  41.    Str2 := StrUtils.AnsiReplaceStr(Str1, #13, CharReplace);
  42.    Result := StrUtils.AnsiReplaceStr(Str2, '&', CharReplace);
  43. end;
  44.  
  45. { tXMLGenerador }
  46.  
  47. procedure tXMLGenerador.AbreNodo(const Nodo: String);
  48. begin
  49.   FListaTexto.Add('<'+Nodo+'>');
  50. end;
  51.  
  52. procedure tXMLGenerador.CierraNodo(const Nodo: String);
  53. begin
  54.   FListaTexto.Add('</'+Nodo+'>');
  55. end;
  56.  
  57. constructor tXMLGenerador.Create(AOwner:TComponent);
  58. begin
  59.   inherited Create(AOwner);
  60.   FListaTexto := TStringList.Create;
  61. end;
  62.  
  63. destructor tXMLGenerador.Destroy;
  64. begin
  65.   FListaTexto.Free;
  66.   inherited;
  67. end;
  68.  
  69. function TXMLGenerador.GetStrings: string;
  70. begin
  71.   Result := FListaTexto.Text;
  72. end;
  73.  
  74. procedure tXMLGenerador.GuardarEnFichero(const Fichero: String);
  75. var AuxStr:string;
  76. begin
  77.   AuxStr := QuitarSaltosLinea(FListaTexto,'');
  78.   FListaTexto.Text := AuxStr;
  79.   FListaTexto.SaveToFile(Fichero);
  80. end;
  81.  
  82. procedure TXMLGenerador.GuardarEnStream(var VariableStream: TStream);
  83. var AuxStr:string;
  84. begin
  85.   AuxStr := QuitarSaltosLinea(FListaTexto,'');
  86.   FListaTexto.Text := AuxStr;
  87.   FListaTexto.SaveToStream(VariableStream);
  88. end;
  89.  
  90. procedure TXMLGenerador.SetStrings(Valor: string);
  91. begin
  92.   FListaTexto.Text := Valor;
  93. end;
  94.  
  95. procedure tXMLGenerador.TextoLibre(const Texto: String);
  96. begin
  97.   FListaTexto.Add(Texto);
  98. end;
  99.  
  100. procedure tXMLGenerador.ValorNodo(const Nodo, Valor: String);
  101. begin
  102.   AbreNodo(Nodo);
  103.   TextoLibre(Valor);
  104.   CierraNodo(Nodo);
  105. end;
  106.  
  107. end.


  • 0

#32 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.446 mensajes
  • LocationMéxico

Escrito 18 agosto 2021 - 07:29

Muchas gracias por el aporte amigo defcon1_es. (y)

 

Saludos


  • 0




IP.Board spam blocked by CleanTalk.