Artículo técnico

Cifrado de PDF AES-256 en Delphi: Configuración de HotPDF y problemas comunes

Una opción (flag) de permiso en PDF no es un candado. Es una solicitud que el archivo hace a lo que sea que lo abra, y un visor es libre de ignorarla. Ese único hecho decide cómo debe razonar acerca de todas las demás opciones en esta página. La verdadera confidencialidad proviene de un solo lugar: cifrado AES-256 con una clave basada en una contraseña que el lector no tiene. Todo lo demás, las casillas de verificación de "no imprimir" y "no copiar", es una política que el software compatible acepta respetar y el software hostil no. Mezcle esas dos capas y entregará algo que se siente seguro en una demostración pero que tiene fugas en la práctica

HotPDF es un componente PDF VCL nativo para Delphi y C++Builder, y expone el modelo de protección ISO 32000 a través de un pequeño conjunto de propiedades. Las propiedades son fáciles de configurar. La parte difícil es saber cuál le otorga protección criptográfica y cuál le otorga una sugerencia amable, y obtener el orden de asignación correcto para que el cifrado que solicitó sea en realidad el cifrado que obtiene

Lo que realmente prometen las dos contraseñas

El cifrado PDF define dos credenciales con diferentes trabajos, y combinarlas es el error de diseño más común en el código de salida protegida. La contraseña de usuario (user password) controla el descifrado. Sin ella, o sin la contraseña del propietario, un lector compatible no puede reconstruir la clave del archivo y el contenido permanece ilegible criptográficamente. La contraseña del propietario (owner password) controla la configuración de permisos: a un lector que recibe la contraseña del propietario se le concede acceso total sin importar lo que digan las opciones de restricción

Los bits de permisos se asientan en un terreno más débil. Impresión, extracción de contenido, llenado de formularios: cada una es una opción (flag) que un visor lee y decide respetar (ISO 32000-2 §7.6.4). El cifrado protege los bytes. Las opciones de permisos solo instruyen al software compatible, y lo instruyen a posteriori. Cualquiera que abra el documento con la contraseña de usuario ya tiene el contenido descifrado en la memoria, por lo que "no copiar" y "no imprimir" significan algo para un visor con buen comportamiento y nada para uno decidido. Construya el modelo de amenazas en torno a esa línea. La confidencialidad reside en la contraseña de usuario. Los permisos determinan lo que ofrecen los visores convencionales, y eso es todo lo que hacen

Orden de configuración: todo antes de BeginDoc

HotPDF construye el diccionario de cifrado y deriva la clave del archivo en el momento en que se ejecuta BeginDoc. Lo que sea que contengan las propiedades de protección en ese instante es lo que obtiene el documento, y cambiarlas después no cambia nada. La propiedad que más importa aquí es CryptKeyLength, que elige el esquema de los valores de THPDFKeyType: k40, k128, aes128 y aes256. Si la asigna después de BeginDoc, no obtendrá ninguna excepción, ninguna advertencia, solo un archivo que silenciosamente mantuvo lo que tenía al principio. Ese tipo de divergencia silenciosa es el peor tipo: pasa todas las pruebas locales y aparece meses después como un hallazgo de cumplimiento (compliance) en el escritorio de un cliente

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'statement.pdf';
    Pdf.ActivateProtection := True;
    Pdf.CryptKeyLength := aes256;        // must be set before BeginDoc
    Pdf.UserPassword := 'open-secret';
    Pdf.OwnerPassword := 'admin-secret';
    Pdf.UseAES256R6 := False;            // R=5: widest viewer support
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Arial', [], 11);
    Pdf.CurrentPage.TextOut(50, 720, 0, 'Account statement, June 2026');
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Las contraseñas están en UTF-8 y tienen un límite de 127 bytes, que es el límite de ISO 32000-2 para los esquemas AES-256. Si su política de contraseñas le entrega secretos más largos, realice el truncamiento usted mismo, de su lado, donde controle exactamente dónde recae el corte. Déjelo al azar y es posible que la biblioteca y algún visor futuro no estén de acuerdo sobre el límite, lo que produce un archivo que se abre para usted y rechaza la misma contraseña en otro lugar

Revisión 5 o revisión 6: un valor booleano, dos ecosistemas

UseAES256R6 elige entre los dos handshakes AES-256, y la elección tiene más consecuencias de lo que sugiere su tipo booleano. Déjelo en False y HotPDF escribe la revisión 5, el esquema AES-256 que llegó como una extensión a PDF 1.7 y que pueden abrir unos quince años de visores. Establézcalo en True y obtendrá la revisión 6, la derivación de clave reforzada estandarizada en ISO 32000-2 para PDF 2.0, que cierra una debilidad conocida en la forma en que la revisión 5 verifica la contraseña

Así que la revisión 6 es criptográficamente la mejor opción. También es la que rompe cosas. Un archivo de revisión 6 necesita un visor creado para PDF 1.7 Nivel de Extensión 3 o PDF 2.0, y gran parte del software implementado no es ninguno de los dos: archivos de gestión de registros, renderizadores integrados en otros productos, herramientas de línea de negocio que nadie ha tocado en años. Esos rechazarán el archivo directamente, y lo harán en la computadora del cliente, nunca en la suya. Por lo tanto, la opción predeterminada práctica es la revisión 5. Elija la revisión 6 solo cuando una política de seguridad mencione a ISO 32000-2 por revisión, y cuando haya confirmado realmente que cada consumidor puede leerlo. De cualquier manera, anote cuál eligió y por qué, porque la próxima persona que toque este código se lo preguntará

Los tipos de clave más antiguos merecen una oración para que sepa omitirlos. THPDFKeyType todavía incluye k40, k128 y aes128, pero existen para reproducir archivos históricos, no para proteger otros nuevos. RC4 de 40 bits cae ante hardware básico, y los esquemas de 128 bits son anteriores a las revisiones AES-256 que esperará cualquier revisión de seguridad actual. Para un documento que está creando en 2026, la verdadera pregunta es solo la revisión 5 frente a la revisión 6; si se encuentra recurriendo a los tipos heredados en un diseño nuevo, algo ha salido mal en el proceso previo

Opciones de permisos sin una contraseña de apertura

A menudo, el requisito es el opuesto al secreto. Cualquiera debería poder leer el documento, pero la impresión o la extracción debe ser limitada. Usted expresa eso con una contraseña de usuario vacía y una contraseña de propietario que no esté vacía, que PDF llama modo de contraseña de apertura (open-password mode), y enumera las operaciones que desea permitir en ProtectOptions

Pdf.ActivateProtection := True;
Pdf.CryptKeyLength := aes256;
Pdf.UserPassword := '';                      // anyone can open the file
Pdf.OwnerPassword := 'rotate-me-quarterly';  // guards the permission set
Pdf.ProtectOptions := [prPrint, prPrint12bit, prExtractContent];
Pdf.BeginDoc;
// ... page content ...
Pdf.EndDoc;

El conjunto THPDFProtectOptions se mapea a los bits de permiso ISO: prPrint y prPrint12bit para impresión de alta resolución, prInformationCopy para copia y extracción general, prExtractContent para extracción de tecnología de asistencia (accesibilidad), además de prModifyStructure, prEditAnnotations, prFillAnnotations y prAssemble. Dos de ellos merecen una advertencia. Deje prExtractContent activado en casi todos los perfiles que cree. Es el bit que un lector de pantalla necesita para llegar al texto, y borrarlo convierte silenciosamente una decisión de derechos en un defecto de accesibilidad con el que se topa alguien con una discapacidad y que usted nunca ve. La otra trampa es prPrint por sí solo, sin prPrint12bit: varios visores responden degradando la calidad de impresión, y sus usuarios lo informarán como un error de renderizado en lugar de la configuración de permisos que realmente es

La verificación toma cinco minutos y pertenece a su lista de verificación de lanzamiento. Abra una muestra de cada perfil en Acrobat, abra Propiedades del documento y lea la pestaña Seguridad, que explica el algoritmo ("AES de 256 bits") y enumera las operaciones permitidas una por una. Luego, abra el mismo archivo en el visor más antiguo que sus clientes realmente ejecutan, no el más nuevo en su máquina. Esa segunda apertura es el seguro económico en contra de que un archivo de revisión 6 pase por el desarrollo sin problemas y muera en un cliente que nunca se actualizó

Eliminación de la protección de archivos existentes

El descifrado ejecuta el mismo modelo de propiedades a la inversa. Cargue el documento con una credencial válida, desactive la protección y guarde el resultado sin ella

var
  Pdf: THotPDF;
  PageCount: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    PageCount := Pdf.LoadFromFile('encrypted.pdf', 'open-secret');
    if PageCount > 0 then
    begin
      Pdf.ActivateProtection := False;   // drop encryption on save
      Pdf.SaveLoadedDocument('plain.pdf');
    end;
  finally
    Pdf.Free;
  end;
end;

Esa ruta analiza (parses) todo el documento en la memoria, lo cual está bien para archivos comunes y es un desperdicio para los enormes. Cuando la entrada asciende a cientos de megabytes, DecryptFile es la opción más económica: descifra durante una copia a nivel de archivo, tomando una ruta directa de reescritura AES-256 que omite la creación de todo el árbol de objetos siempre que la entrada lo permita. Es parte de la API de archivo directo que se cubre en el artículo complementario sobre el procesamiento de archivos PDF grandes desde Delphi

Restricciones que interactúan con el cifrado

Vale la pena conocer dos límites antes de diseñar en torno al cifrado en lugar de después. El primero es la conformidad (conformance) de archivos a largo plazo. ISO 19005 prohíbe el cifrado en PDF/A, por lo que cualquier flujo de trabajo que cifra un documento y también afirma la conformidad con PDF/A es contradictorio por diseño; HotPDF no le permitirá tener ambos en un solo archivo. Cuando realmente necesita ambos, la respuesta son dos artefactos: una copia cifrada para su distribución y una copia sin cifrar separada para el archivo

El segundo límite es más severo. El cifrado PDF no tiene depósito de garantía (escrow) y no tiene recuperación. Pierda la contraseña de usuario en un archivo R5 o R6 y sus opciones son usar fuerza bruta o darse por vencido. Por lo tanto, trate los secretos de propietarios y usuarios de la forma en que trata cualquier credencial de producción. Genérelos, guárdelos en una bóveda, rótelos en un horario. La única cosa que nunca se debe hacer es incrustarlos en el código (hard-code) como constantes en una unidad, donde pasan directamente al control de versiones y se asientan en la copia de trabajo de cada desarrollador para siempre

Un último reflejo que vale la pena construir. Cambiar la protección de un archivo que usted no creó utiliza la misma maquinaria que el descifrado, no es una función separada: cárguelo con su contraseña a través de LoadFromFile, edite ProtectOptions o las contraseñas in situ y escríbalo de nuevo con SaveLoadedDocument. Si puede descifrar un archivo, puede volver a configurar sus permisos, y el código se ve casi idéntico al ejemplo anterior

Las propiedades de protección que se muestran aquí forman parte del Componente HotPDF estándar para Delphi y C++Builder; la página del producto contiene la referencia completa de cifrado, incluida la enumeración completa de permisos