Tema: Firmar XML
Ver Mensaje Individual
  #2  
Antiguo 26-02-2012
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.604
Reputación: 29
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
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.
Responder Con Cita