Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Firmar XML (https://www.clubdelphi.com/foros/showthread.php?t=77783)

johan 26-02-2012 01:41:56

Firmar XML
 
Me han pedido poder firmar un fichero xml en formato PKCS#7, alguien tiene un pequeño ejemplo y como tratarlo desde D7 o donde puedo obtener mas información.
Ese fichero lo tendria que enviar a traves de un ws, pero con ese cifrado.

Gracias.

Al González 26-02-2012 03:47:20

Hola johan.

Estando lejos de ser un experto en estas cosas, para la parte de la firma digital te sugiero echar un vistazo a este par de enlaces y valorar las recomendaciones que ahí se dan:

http://www.clubdelphi.com/foros/showthread.php?t=61897

http://www.clubdelphi.com/foros/show...376#post416376

Por si decides usar "COM Interop", dejo aquí el código que escribí cuando necesité hacer algo muy parecido a lo que buscas (también en Delphi 7):

Código:

/* FirmaXML 1.0
  Ensamblaje .NET para permitir el firmado XMLDSig de un documento fiscal mexicano de formato XML.
  DLL compilada con la versión de prueba de Visual Studio. */

using System;
using System.IO;
using System.Xml;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;

namespace FirmaXML
{
  /* Permitimos que esta clase pueda ser exportada como interfaz COM, para ser utilizada en
    entornos no .NET también. */
  [ComVisible (true)]
  [Guid ("4EA3AB36-9DD1-42C5-B57A-D0F89DC4711B")]

  public class FirmaXML
  {
    public string Firmar (string XML, string RFC)
    {
      /* Método que recibe el texto de un documento XML y regresa ese mismo texto pero conteniendo
        una firma digital XMLDSig envolvente.
     
        Parámetro XML: Texto del documento a firmar.
        Parámetro RFC: RFC del contribuyente cuyo certificado se usará para la firma. */

      // Obtenemos el certificado correspondiente al RFC dado
      X509Certificate2 Certificado = BuscarCertificado (RFC);

      // Obtenemos el objeto de llave privada del certificado
      RSACryptoServiceProvider Llave = Certificado.PrivateKey as RSACryptoServiceProvider;

      // Creamos el objeto firmante (Firma) asignándole el texto XML y la llave del certificado
      XmlDocument Documento = new XmlDocument ();
      Documento.LoadXml (XML);
      SignedXml Firma = new SignedXml (Documento);
      Firma.SigningKey = Llave;
     
      /* Creamos el nodo <Reference> con un subnodo <Transforms> conteniendo el elemento
        <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />, y lo
        agregamos al objeto firmante */
      Reference Referencia = new Reference ();
      Referencia.Uri = "";  // Tomar ("digerir") todo el documento al crear la firma
      Referencia.AddTransform (new XmlDsigEnvelopedSignatureTransform ());
      Firma.AddReference (Referencia);

      /* Creamos el nodo <KeyInfo> con el subnodo <X509Data>, poniendo dentro de éste el
        certificado, su número de serie y la entidad emisora del mismo (primero estos dos últimos
        como subnodo <X509IssuerSerial>), y agregando todo al objeto firmante. */
      KeyInfoX509Data NodoX509Data = new KeyInfoX509Data ();
      NodoX509Data.AddIssuerSerial (Certificado.Issuer, Certificado.GetSerialNumberString ());
      NodoX509Data.AddCertificate (Certificado);
      Firma.KeyInfo = new KeyInfo ();
      Firma.KeyInfo.AddClause (NodoX509Data);
     
      // Generamos la firma digital y la agregamos al objeto Documento
      Firma.ComputeSignature ();
      Documento.DocumentElement.AppendChild (Documento.ImportNode (Firma.GetXml (), true));

      // Devolvemos el XML firmado
      return Documento.OuterXml;
    }

    protected static X509Certificate2 BuscarCertificado (string RFC)
    {
      // Método para obtener el certificado que pertenece a un RFC

      // Cargamos la lista de certificados personales instalados en Windows
      X509Store Certificados = new X509Store (StoreName.My, StoreLocation.CurrentUser);
      Certificados.Open (OpenFlags.ReadOnly);

      // Buscamos el certificado del contribuyente
      foreach (X509Certificate2 Resultado in Certificados.Certificates)
        /* El sujeto (propiedad Subject) del certificado puede contener algo como:
          "OU=Unidad 1, SERIALNUMBER=" / AAAA010101HDFRXX01", " (continúa)
          "OID.2.5.4.45=AAA010101AAA / AAAA010101AAA, O=Matriz SA, " (continúa)
          "OID.2.5.4.41=Matriz SA, CN=Matriz SA"
         
          "AAA010101AAA / AAAA010101AAA" son el RFC del contribuyente (persona moral o física) y,
          opcionalmente, el RFC de la persona física que representa a la persona moral
          (posiblemente).  El primero es el que nos interesa y debe ser igual al parámetro RFC para
          dar por encontrado el certificado.
       
          Revisaremos cada par "llave=valor" del sujeto.  NOTA: Puede que convenga robustecer el
          código de este ciclo anidado. */
        foreach (string Dato in Resultado.Subject.Split (','))
        {
          string[] LlaveValor = Dato.Trim().Split ('=');
         
          if ((LlaveValor.Length == 2) && LlaveValor [0].EndsWith ("2.5.4.45") &&
          (LlaveValor [1].Split ('/') [0].Trim () == RFC))
            return Resultado;  // Encontrado
        }

      throw new Exception ("No hay un certificado instalado para el RFC que se indicó.");
    }
  }
}

Lo probé de la siguiente manera en Delphi:
Código Delphi [-]
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, xmldom, XMLIntf, msxmldom, XMLDoc;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Doc: TXMLDocument;
    mm1: TMemo;
    mm2: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

Uses
  { Unidad generada con la opción "Project | Import Type Library..." (para
    obtener acceso a la DLL creada en .NET) }
  FirmaXML_TLB;

procedure TForm1.FormCreate(Sender: TObject);
begin
  // Abrimos el documento XML señalado con la propiedad FileName
  Doc.Active := True;
  
  mm1.Text := Doc.XML.Text;
end;

procedure TForm1.Button1Click(Sender: TObject);
Var
  Firma :Variant;
  Resultado :String;
begin
  // Creamos el objeto firmante
  Firma := CoFirmaXML_.Create;

  { Firmamos el contenido XML con el certificado instalado del
    contribuyente AAA010101AAA (RFC) } 
  Resultado := Firma.Firmar (Doc.XML.Text, 'AAA010101AAA');

  { El resultado lo asignamos nuevamente al documento, pero con
    codificación UTF-8 para que no tenga problemas con los diacríticos
    (acentos, tildes y diéresis), además de darle formato visible
    "estructural" (saltos de línea y sangrado) para facilitar su lectura
    humana. }
  Doc.XML.Text := FormatXMLData (AnsiToUTF8 (Resultado));

  // Guardamos el documento resultante
  Doc.Active := True;
  Doc.SaveToFile ('XMLCancelacionResultado.xml');

  { Mostramos en pantalla el docmento firmado }
  mm2.Text := FormatXMLData (Resultado);
end;

end.

Lo mejor de todo es que funcionó, quedándome con una grata impresión de las bibliotecas nativas de .NET (lástima que su lenguaje puntero sea un derivado de C :().

Ojalá pueda servirte como base a ti o alguien más. :)

Saludos.

Al González.

frankizzio 01-04-2015 23:40:46

Muchas gracias, estuve buscando información y felizmente utilizar una dll de .NET funcionó, pero tengo un inconveniente, a mi me piden firmar en una zona especifica del documento.

Además que utilizo un archivo .pfx para realizar la firma.

Como puedo indicarle que etiqueta es la que debe firmar, además de agregarle prefijos a la firma?

gaunmanuel 02-11-2016 23:32:52

Import Type Library
 
Hola, sabes al importar la DLL a Delphi 7.0, me marco error: Error Type Library/DLL.
que me habrá faltado?

Código:

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace INtradeHTTPLib
{
    [ComVisible(true)]
    [Guid("6f7755dd-227c-4d8f-b38b-2a2e994cdc8e")]
    public class TINtradeHTTP
    {

        public String Consulta(string url, string folio, string rfc)
        {
            using (var client = new WebClient())
            {
                var values = new NameValueCollection();
                values["folio"] = folio;
                values["rfc"] = rfc;
                var response = client.UploadValues(url, values);
                var responseString = Encoding.Default.GetString(response);
                return responseString;

            }
        }
    }
}


Cita:

Empezado por Al González (Mensaje 426172)
Hola johan.

Estando lejos de ser un experto en estas cosas, para la parte de la firma digital te sugiero echar un vistazo a este par de enlaces y valorar las recomendaciones que ahí se dan:

http://www.clubdelphi.com/foros/showthread.php?t=61897

http://www.clubdelphi.com/foros/show...376#post416376

Por si decides usar "COM Interop", dejo aquí el código que escribí cuando necesité hacer algo muy parecido a lo que buscas (también en Delphi 7):

Código:

/* FirmaXML 1.0
  Ensamblaje .NET para permitir el firmado XMLDSig de un documento fiscal mexicano de formato XML.
  DLL compilada con la versión de prueba de Visual Studio. */

using System;
using System.IO;
using System.Xml;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;

namespace FirmaXML
{
  /* Permitimos que esta clase pueda ser exportada como interfaz COM, para ser utilizada en
    entornos no .NET también. */
  [ComVisible (true)]
  [Guid ("4EA3AB36-9DD1-42C5-B57A-D0F89DC4711B")]

  public class FirmaXML
  {
    public string Firmar (string XML, string RFC)
    {
      /* Método que recibe el texto de un documento XML y regresa ese mismo texto pero conteniendo
        una firma digital XMLDSig envolvente.
     
        Parámetro XML: Texto del documento a firmar.
        Parámetro RFC: RFC del contribuyente cuyo certificado se usará para la firma. */

      // Obtenemos el certificado correspondiente al RFC dado
      X509Certificate2 Certificado = BuscarCertificado (RFC);

      // Obtenemos el objeto de llave privada del certificado
      RSACryptoServiceProvider Llave = Certificado.PrivateKey as RSACryptoServiceProvider;

      // Creamos el objeto firmante (Firma) asignándole el texto XML y la llave del certificado
      XmlDocument Documento = new XmlDocument ();
      Documento.LoadXml (XML);
      SignedXml Firma = new SignedXml (Documento);
      Firma.SigningKey = Llave;
     
      /* Creamos el nodo <Reference> con un subnodo <Transforms> conteniendo el elemento
        <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />, y lo
        agregamos al objeto firmante */
      Reference Referencia = new Reference ();
      Referencia.Uri = "";  // Tomar ("digerir") todo el documento al crear la firma
      Referencia.AddTransform (new XmlDsigEnvelopedSignatureTransform ());
      Firma.AddReference (Referencia);

      /* Creamos el nodo <KeyInfo> con el subnodo <X509Data>, poniendo dentro de éste el
        certificado, su número de serie y la entidad emisora del mismo (primero estos dos últimos
        como subnodo <X509IssuerSerial>), y agregando todo al objeto firmante. */
      KeyInfoX509Data NodoX509Data = new KeyInfoX509Data ();
      NodoX509Data.AddIssuerSerial (Certificado.Issuer, Certificado.GetSerialNumberString ());
      NodoX509Data.AddCertificate (Certificado);
      Firma.KeyInfo = new KeyInfo ();
      Firma.KeyInfo.AddClause (NodoX509Data);
     
      // Generamos la firma digital y la agregamos al objeto Documento
      Firma.ComputeSignature ();
      Documento.DocumentElement.AppendChild (Documento.ImportNode (Firma.GetXml (), true));

      // Devolvemos el XML firmado
      return Documento.OuterXml;
    }

    protected static X509Certificate2 BuscarCertificado (string RFC)
    {
      // Método para obtener el certificado que pertenece a un RFC

      // Cargamos la lista de certificados personales instalados en Windows
      X509Store Certificados = new X509Store (StoreName.My, StoreLocation.CurrentUser);
      Certificados.Open (OpenFlags.ReadOnly);

      // Buscamos el certificado del contribuyente
      foreach (X509Certificate2 Resultado in Certificados.Certificates)
        /* El sujeto (propiedad Subject) del certificado puede contener algo como:
          "OU=Unidad 1, SERIALNUMBER=" / AAAA010101HDFRXX01", " (continúa)
          "OID.2.5.4.45=AAA010101AAA / AAAA010101AAA, O=Matriz SA, " (continúa)
          "OID.2.5.4.41=Matriz SA, CN=Matriz SA"
         
          "AAA010101AAA / AAAA010101AAA" son el RFC del contribuyente (persona moral o física) y,
          opcionalmente, el RFC de la persona física que representa a la persona moral
          (posiblemente).  El primero es el que nos interesa y debe ser igual al parámetro RFC para
          dar por encontrado el certificado.
       
          Revisaremos cada par "llave=valor" del sujeto.  NOTA: Puede que convenga robustecer el
          código de este ciclo anidado. */
        foreach (string Dato in Resultado.Subject.Split (','))
        {
          string[] LlaveValor = Dato.Trim().Split ('=');
         
          if ((LlaveValor.Length == 2) && LlaveValor [0].EndsWith ("2.5.4.45") &&
          (LlaveValor [1].Split ('/') [0].Trim () == RFC))
            return Resultado;  // Encontrado
        }

      throw new Exception ("No hay un certificado instalado para el RFC que se indicó.");
    }
  }
}

Lo probé de la siguiente manera en Delphi:
Código Delphi [-]
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, xmldom, XMLIntf, msxmldom, XMLDoc;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Doc: TXMLDocument;
    mm1: TMemo;
    mm2: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

Uses
  { Unidad generada con la opción "Project | Import Type Library..." (para
    obtener acceso a la DLL creada en .NET) }
  FirmaXML_TLB;

procedure TForm1.FormCreate(Sender: TObject);
begin
  // Abrimos el documento XML señalado con la propiedad FileName
  Doc.Active := True;
  
  mm1.Text := Doc.XML.Text;
end;

procedure TForm1.Button1Click(Sender: TObject);
Var
  Firma :Variant;
  Resultado :String;
begin
  // Creamos el objeto firmante
  Firma := CoFirmaXML_.Create;

  { Firmamos el contenido XML con el certificado instalado del
    contribuyente AAA010101AAA (RFC) } 
  Resultado := Firma.Firmar (Doc.XML.Text, 'AAA010101AAA');

  { El resultado lo asignamos nuevamente al documento, pero con
    codificación UTF-8 para que no tenga problemas con los diacríticos
    (acentos, tildes y diéresis), además de darle formato visible
    "estructural" (saltos de línea y sangrado) para facilitar su lectura
    humana. }
  Doc.XML.Text := FormatXMLData (AnsiToUTF8 (Resultado));

  // Guardamos el documento resultante
  Doc.Active := True;
  Doc.SaveToFile ('XMLCancelacionResultado.xml');

  { Mostramos en pantalla el docmento firmado }
  mm2.Text := FormatXMLData (Resultado);
end;

end.

Lo mejor de todo es que funcionó, quedándome con una grata impresión de las bibliotecas nativas de .NET (lástima que su lenguaje puntero sea un derivado de C :().

Ojalá pueda servirte como base a ti o alguien más. :)

Saludos.

Al González.


clauxs88 20-03-2018 13:57:21

Tendrian la DLL para pasarmela??
 
Favor si tienen la DLL para pasarmela e mportarla.


La franja horaria es GMT +2. Ahora son las 13:02:27.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi