Firma tu primer documento en 5 minutos

Recorrido completo desde un proyecto VCL nuevo hasta un sobre XAdES firmado. Funciona en Delphi 7 hasta RAD Studio 13 y en C++Builder.

Delphi 7 – RAD Studio 13
C++ Builder
Perfil VeriFactu

Antes de empezar

Solo dos cosas — sgcSign instalado en el IDE y un certificado PFX con el que firmar.

Lo que necesitas

  • sgcSign instalado en tu IDE — consulta la página de descargas.
  • Un archivo de certificado PFX con su contraseña (sirve cualquier PKCS#12 X.509).
  • Delphi 7 hasta RAD Studio 13, o cualquier versión de C++ Builder. Compatible con Windows de 32 y 64 bits.
  • Sin dependencias externas — sgcSign usa Windows CNG/BCrypt para la criptografía y WinHTTP para las llamadas de red.
make-test-cert.sh
# No PFX yet? Generate a self-signed test cert with OpenSSL:
openssl req -x509 -newkey rsa:2048 \
  -keyout key.pem -out cert.pem \
  -days 365 -nodes \
  -subj "/CN=sgcSign Test"

openssl pkcs12 -export \
  -inkey key.pem -in cert.pem \
  -out test.pfx \
  -password pass:secret

# Now you have test.pfx with password "secret".

Crea un nuevo proyecto

Inicia una aplicación VCL Forms en tu IDE. Coloca dos memos y un botón en el formulario — esa es toda la interfaz.

Diseño del formulario

  • memoXMLTMemo, pega aquí tu XML sin firmar.
  • memoSignedTMemo, aquí aparecerá el XML firmado tras pulsar el botón.
  • btnSignTButton, conectado al manejador OnClick del paso 2.
  • No hacen falta componentes en tiempo de diseño — todo se crea por código.
Unit1.dfm
object Form1: TForm1
  Caption = 'sgcSign Quick Start'
  ClientWidth  = 800
  ClientHeight = 600

  object memoXML: TMemo
    Left = 8
    Top  = 8
    Width  = 784
    Height = 240
  end

  object memoSigned: TMemo
    Left = 8
    Top  = 288
    Width  = 784
    Height = 300
  end

  object btnSign: TButton
    Left = 8
    Top  = 256
    Caption = 'Sign XML'
  end
end

Añade el código de firma

Carga un proveedor PFX, apunta un firmador de documento a él con el perfil VeriFactu y llama a SignXML.

Delphi

  • TsgcPFXKeyProvider importa el .pfx vía Windows CNG — la firma SHA-256 moderna funciona sea cual sea el CSP original.
  • TsgcDocumentSigner elige el nivel XAdES adecuado en función del perfil.
  • spVeriFactu selecciona VeriFactu de la AEAT española — XAdES-EPES, B-B, RSA-SHA256, C14N exclusiva.
  • Cambia la constante por cualquiera de los 21 perfiles por país — consulta Perfiles por país.
Unit1.pas
procedure TForm1.btnSignClick(Sender: TObject);
var
  vKeyProvider: TsgcPFXKeyProvider;
  vSigner: TsgcDocumentSigner;
begin
  vKeyProvider := TsgcPFXKeyProvider.Create(nil);
  try
    vKeyProvider.FileName := 'certificate.pfx';
    vKeyProvider.Password := 'secret';
    vKeyProvider.LoadFromFile;

    vSigner := TsgcDocumentSigner.Create(nil);
    try
      vSigner.KeyProvider := vKeyProvider;
      vSigner.Profile := spVeriFactu;
      memoSigned.Text := vSigner.SignXML(memoXML.Text);
    finally
      vSigner.Free;
    end;
  finally
    vKeyProvider.Free;
  end;
end;

C++ Builder

  • Misma forma, mismas clases, mismos nombres de propiedad — el wrapper de C++ es un port 1:1 de la API de Delphi.
  • El bloque __finally asegura que los proveedores se liberan aunque la firma lance una excepción.
  • Se usa UnicodeString de extremo a extremo — sin preocuparte por conversiones ANSI/UTF-8.
Unit1.cpp
void __fastcall TForm1::btnSignClick(TObject *Sender)
{
  TsgcPFXKeyProvider *vKeyProvider = new TsgcPFXKeyProvider(NULL);
  try {
    vKeyProvider->FileName = "certificate.pfx";
    vKeyProvider->Password = "secret";
    vKeyProvider->LoadFromFile();

    TsgcDocumentSigner *vSigner = new TsgcDocumentSigner(NULL);
    try {
      vSigner->KeyProvider = vKeyProvider;
      vSigner->Profile = spVeriFactu;
      memoSigned->Text = vSigner->SignXML(memoXML->Text);
    } __finally {
      delete vSigner;
    }
  } __finally {
    delete vKeyProvider;
  }
}

Añade las unidades necesarias

Dos unidades en tu cláusula uses — el proveedor PFX y el firmador de documento.

Cláusula uses de Delphi

Cada proveedor de claves vive en su propia unidad para que solo pagues por lo que referencias. sgcSign_DocumentSigner añade la capa de enrutamiento que elige XAdES, PAdES o CAdES según la entrada.

Unit1.pas
uses
  Classes, SysUtils, Forms, StdCtrls, Controls,
  // sgc
  sgcSign_KeyProvider_PFX,
  sgcSign_DocumentSigner;

Includes de C++ Builder

Las mismas unidades, expuestas como cabeceras .hpp. El linker de C++ Builder resuelve automáticamente las bibliotecas estáticas subyacentes.

Unit1.cpp
#include "sgcSign_KeyProvider_PFX.hpp"
#include "sgcSign_DocumentSigner.hpp"

Ejecuta y prueba

Pulsa F9, pega una factura en el memo superior y haz clic en Sign XML. El sobre firmado aparece en el memo inferior.

Compila y ejecuta

Pulsa F9 en el IDE. El formulario se abre con dos memos y un botón. Sin excepciones, sin unidades ausentes.

Pega la entrada

Pega cualquier XML de factura (o cualquier XML bien formado) en memoXML. El firmador lo normaliza internamente a UTF-8.

Verifica el resultado

memoSigned contiene ahora tu XML con un elemento <ds:Signature> enveloped añadido. Guárdalo y pásalo por cualquier verificador XAdES — o usa TsgcSignatureVerifier directamente.

Unicode y la sobrecarga WideString

En Delphi 7, string equivale a AnsiString. Usa las sobrecargas WideString para hacer round-trip de caracteres polacos, cirílicos o griegos sin pérdida.

El problema

En Delphi 7 la sobrecarga string pasa por la ACP del sistema. Los diacríticos de 'Jarosław' solo hacen round-trip cuando la ACP del sistema es CP1250. Si la ACP es CP1252, los caracteres se corrompen en el XML firmado.

Las sobrecargas WideString evitan por completo la ACP — pasan de UTF-16 a UTF-8 mediante WideCharToMultiByte con CP_UTF8. En Delphi 2009+ ambas sobrecargas son equivalentes; UnicodeString ya es UTF-16.

d7-unicode.pas
var
  Doc: IXMLDocument;       // MSXML6
  XML, Signed: WideString; // = MSXML6 DOMString
  Sign: TsgcXAdESSigner;
begin
  Doc.SaveToXML(XML);            // WideString out, no ACP step
  Signed := Sign.SignXML(XML);   // resolves to WideString overload
  // Polish, Cyrillic, Greek round-trip losslessly
end;

UTC por defecto

Todos los sellos de tiempo X.509 / CRL / OCSP / TSA se almacenan como valores TDateTime en UTC, conforme a las especificaciones ASN.1 subyacentes.

Mostrar en hora local

Para mostrarlos al usuario final, usa las propiedades *Local de cada componente (p. ej. vCert.NotAfterLocal) o convierte manualmente con sgcUTCToLocal de la unidad sgcSign_Time.

Esto coincide con RFC 5280 (X.509), RFC 3161 (TSA) y RFC 6960 (OCSP), que especifican UTC. El helper sgcUTCNow devuelve el TDateTime UTC actual para código que necesite comparar sellos de tiempo.

time-zones.pas
uses sgcSign_Time;

// Display certificate validity in local time
Memo1.Lines.Add('Cert expires: ' +
                DateTimeToStr(vCert.NotAfterLocal));
Memo1.Lines.Add('Now (UTC):    ' +
                DateTimeToStr(sgcUTCNow));

// Or convert manually
vLocal := sgcUTCToLocal(vCert.NotAfter);

Hacia dónde seguir desde aquí

XAdES es solo el comienzo. PAdES, CAdES, perfiles, proveedores de claves y el servidor centralizado están a un clic.

Resumen de características

Recorre toda la superficie de la API XAdES, PAdES y CAdES y la infraestructura de sello de tiempo + OCSP que la sostiene.

Leer más →

21 perfiles por país

VeriFactu, FatturaPA, KSeF, FACTUR-X, además de 9 perfiles de contratos laborales de la UE. Cambio de jurisdicción en una línea.

Leer más →

10 proveedores de claves

PFX, PEM, almacén de Windows, PKCS#11, Azure Trusted Signing, AWS KMS, Google KMS, Vault, Certum, CSC v2.

Leer más →

sgcSign Server

Centraliza la firma en toda la build farm. API REST, panel web, GitHub Actions, Azure DevOps, Jenkins y Docker.

Leer más →

¿Listo para producción?

Descarga la prueba, lanza un binario firmado hoy mismo y licencia sgcSign cuando estés satisfecho.