Firma Digital de Documentos PDF con .NET

La firma digital es una tecnología esencial en el mundo actual, especialmente en entornos empresariales donde la autenticación y la integridad de los documentos son cruciales. En este artículo, exploraremos cómo implementar en documentos PDF utilizando .NET y C#.

Antes de empezar con en el código, resulta fundamental tener una comprensión básica de los elementos esenciales necesarios para su implementación. En este artículo, no vamos a profundizar en explicar minuciosamente su funcionamiento ni abordar todos los conceptos vinculados con este proceso, ya que eso extendería considerablemente el contenido. Por el momento, nos enfocaremos exclusivamente en el proceso de firma de documentos PDF

¿Qué es la Firma Digital?

La firma digital es un mecanismo criptográfico que asegura la autenticidad e integridad de un documento electrónico. Al igual que una firma manuscrita, la firma digital proporciona una manera de verificar la identidad del firmante y garantizar que el contenido del documento no ha sido alterado desde su firma. A continuación, revisaremos algunos elementos que son necesarios para este proceso.

Certificado Digital

Un certificado es esencial para la firma digital, actúa como un «Sello» que verifica la identidad del firmante. Los certificados digitales suelen ser emitidos por autoridades de certificación confiables.

Obtención del Certificado Digital

El primer paso es obtener un certificado digital. Este certificado será la pieza fundamental para garantizar la autenticidad y la integridad de los documentos PDF que firmaremos. Existen diversas formas de adquirir un certificado, pero los mas recomendado es obtenerlo a través de una Autoridad de Certificación (CA) reconocida. Esto asegura que nuestro certificado sea confiable para las aplicaciones y evita las advertencias comunes asociadas similares a las ocurridas con los certificados auto-firmado.

Opción 1: Obtención a Través de una Autoridad de Certificación (CA) Reconocida

La mejor opción para obtener un certificado digital es a través de una autoridad de certificación reconocida. Este proceso implica validar la identidad del solicitante, lo que resulta en un certificado confiable y ampliamente aceptado en entornos empresariales y de seguridad.

Opción 2: Certificado Auto-Firmado

Para simplificar el proceso, en nuestro caso, optaremos por obtener un certificado auto-firmado mediante el uso de Internet Information Services (IIS). Es importante tener en cuenta que aunque este método es más sencillo, los certificados auto-firmado pueden generar advertencias en algunas aplicaciones debido a su naturaleza de no ser validados por una autoridad de certificación(CA) reconocida.

Abrimos nuestro Internet Information Services (IIS) y nos dirigimos a «certificados de servidor»

Certificados
Certificados

Luego, en la parte superior derecha seleccionamos que deseamos un certificado auto-firmado.

Crear Certificados Auto-Firmados
Crear Certificados Auto-Firmados

Seleccionamos el nombre que deseamos que tenga nuestro archivo PFX.

Crear Certificado
Crear Certificado

Con esto ya hemos creado nuestro certificado auto-firmado, pero ahora debemos exportarlo para poder usarlo en nuestra aplicación.

Exportar Certificado
Exportar Certificado

Nos va solicitar donde deseamos guardarlo y que contraseña queremos usar.

Exportar Certificado
Exportar Certificado

Este proceso nos va generar un archivo PKCS#12 con la extensión PFX. Aunque este método puede ser adecuado para propósitos de prueba o desarrollo, se recomienda siempre el uso de certificados emitidos por una autoridad de certificación (CA) reconocida en entornos de producción para garantizar la máxima confianza y seguridad.

Firma Digital usando .NET

Requisitos Previos

Este ejemplo vamos a trabajar con una aplicación de consola en .NET 8, adicionalmente debemos instalar el siguiente paquete NuGet iTextSharp, nosotros para este ejemplo se uso la versión 5.5.13.3.

Obtención del Certificado Digital Desde el archivo extensión PFX

Ahora vamos a firma un documento PDF usando nuestro certificado, lo primero que necesitamos hacer es cargar nuestro certificado, El código siguiente tenemos una clase que obtiene el certificado de un archivo de tipo PKCS#12.

namespace Secure;

using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.X509;
using System.IO;
using System.Linq;

public class Certificate
{
    public X509Certificate[] Chain { get; private set; }
    public AsymmetricKeyParameter Key { get; private set; }

    public Certificate(string certificate, string password)
    {
        using var file = File.OpenRead(certificate);
        Pkcs12Store store = new(file, password.ToCharArray());
        string alias = GetCertificateAlias(store);

        Key = store.GetKey(alias).Key;
        Chain = store.GetCertificateChain(alias).Select(x => x.Certificate).ToArray();
    }

    private string GetCertificateAlias(Pkcs12Store store)
    {
        foreach (string currentAlias in store.Aliases)
        {
            if (store.IsKeyEntry(currentAlias))
            {
                return currentAlias;
            }
        }

        throw new InvalidOperationException("No se encontró una entrada de clave en el almacén.");
    }
}

El constructor de la clase recibe la ruta del nuestro certificado y la clave que protege al archivo PKCS#12. En nuestro caso es la misma clave que ingresamos a exportar el certificado desde Internet Information Services (IIS).

Debemos recordar que el archivo PKCS#12 no solo tiene el certificado, también tiene otros archivos como por ejemplo la llave publica, aunque normalmente solo trae una, puede almacenar varias, por eso se usa la clave y un alias para poder obtener el que necesitamos.

Firmar el Documento PDF

A diferencia del proceso de obtener certificado digital, vamos a ir paso a paso explicando cada linea de código que usamos para firmar el documento. Vamos crear un método llamado SignPdf que va recibir la ruta del archivo a firmar, la ruta para el archivo firmado, la ruta del certificado y la contraseña.

void SignPdf(string sourcePathPdf, string targetPathPdf, string certificatePath, string password)
{
}

Lo primero que vamos hacer es obtener el certificado usando la clase que creamos en la sección anterior.

 Certificate certificate= new(certificatePath, password);

Ahora debemos cargar el documento PDF, para eso usamos librería iTextSharp.

 using var reader = new PdfReader(sourcePathPdf);
 using var writer = new FileStream(targetPathPdf, FileMode.Create, FileAccess.Write);

Cuando firmamos el documento PDF no es necesario que sea visible la firma, eso queda registrado en las propiedades del PDF, pero si podemos generar una especie de sello visible en el documento, para esto debemos configurar como y donde queremos que se visualice.

  int nsejeX = 400;
  int nsejeY = 30;
            
  using var stamper = PdfStamper.CreateSignature(reader, writer, '\0', null, true);
  stamper.SignatureAppearance.SetVisibleSignature( new Rectangle(nsejeX, nsejeY, nsejeX + 150, nsejeY + 50), 1, "Signature");

Estamos configurando para que aparezca en la hoja 1 del documento PDF, ademas estamos diciendo que aparezca en la parte inferior derecha (Para eso son esos cálculos), en forma rectangular, ademas en nombre del campo que va tener la firma dentro de las propiedades de l documento va ser «Signature».

Podemos especificar varias configuraciones adicionales si lo deseamos, como por ejemplo el «Certification Level».

var signature = stamper.SignatureAppearance;
signature.CertificationLevel = PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED;

Ahora necesitamos configurar toda la información que vamos usar para la firma, incluyendo el algoritmo de HASH a usar.

 var signatureKey = new PrivateKeySignature(certificate.Key, DigestAlgorithms.SHA256);
 var signatureChain = certificate.Chain;
 var standard = CryptoStandard.CADES;

Finalmente firmamos el documento PDF.

MakeSignature.SignDetached(signature, signatureKey, signatureChain, null, null, null, 0, standard);

Con todo lo anterior ya tenemos un proceso completo de firma. El código completo para la clase Signature seria el siguiente:

namespace Secure;

using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.security;

public class Signature
{
    public void SignPdf(string sourcePathPdf, string targetPathPdf, string certificatePath, string password)
    {
        Certificate certificado = new(certificatePath, password);

        int nsejeX = 400;
        int nsejeY = 30;

        using var reader = new PdfReader(sourcePathPdf);
        using var writer = new FileStream(targetPathPdf, FileMode.Create, FileAccess.Write);
        using var stamper = PdfStamper.CreateSignature(reader, writer, '\0', null, true);
        stamper.SignatureAppearance.SetVisibleSignature(new Rectangle(nsejeX, nsejeY, nsejeX + 150, nsejeY + 50), 1, "Signature");

        var signature = stamper.SignatureAppearance;
        signature.CertificationLevel = PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED;

        var signatureKey = new PrivateKeySignature(certificado.Key, DigestAlgorithms.SHA256);
        var signatureChain = certificado.Chain;
        var standard = CryptoStandard.CADES;

        MakeSignature.SignDetached(signature, signatureKey, signatureChain, null, null, null, 0, standard);
    }
}

Ahora desde nuestro Program.cs podemos llamar nuestro método de firma.

Signature signature = new();
signature.SignPdf(sourcePathPdf, targetPathPdf, certificatePath, password);

El resultado seria algo similar a lo siguiente:

PDF Firmado
PDF Firmado

Ya tenemos un PDF firmado, donde la firma es visible en el lugar que le indicamos, y si entramos a la opciones de firmas del lector de PDF (En este caso Adobe Acrobat) nos dirá que el documento esta firmado con un certificado y nos mostrara informacion de la firma y podremos comprobar si el documento es del autor que creemos y sin fue modificado posteriormente, es posible que si usaste un certificado auto-firmado y no lo has instalado localmente, entonces puede aparecerte algunas advertencias.

Conclusión

La implementación de la firma digital en documentos PDF mediante .NET y C# es un proceso para garantizar la autenticidad e integridad en entornos electrónicos. Desde la obtención cuidadosa de certificados digitales hasta la personalización de la apariencia de las firmas, hemos explorado pasos cruciales para desarrollar aplicaciones seguras. La comprensión de la cadena de certificación y la elección de prácticas seguras son fundamentales. Al adoptar estas prácticas, no solo fortalecemos la seguridad de nuestros documentos electrónicos, adicionalmente promovemos la confianza en las transacciones digitales, destacando su importancia en el desarrollo de aplicaciones con NET.

Te invito a ver mis otros artículos.