Un indicador de permiso no es un mecanismo de seguridad. El bit que dice «no copiar» reside en el mismo diccionario /Encrypt que la criptografía, lo que le da una apariencia de aplicación que no tiene, y en cuanto trata ambos como una misma cosa su auditoría empieza a producir respuestas erróneas. La única pregunta que merece hacerse a un PDF no es «¿está cifrado?». Es más concreta y más difícil: qué algoritmo, qué revisión del controlador de seguridad, cuál de las dos contraseñas se estableció, qué bits de permisos se declaran y qué partes del archivo toca realmente el cifrado. Un archivo puede estar formalmente cifrado y en la práctica abierto. Puede negarse a ser leído y dejar sus metadatos en texto plano. Puede restringir la impresión mediante un indicador que cualquier visor es libre de ignorar. Auditar un PDF significa resolver todas esas cuestiones por separado, y PDF Library for Delphi, el motor PDF de losLab para Delphi y C++Builder, expone cada una de ellas tanto mediante una API plana de identificadores enteros como mediante una capa de clases tipada
Qué registra realmente el diccionario /Encrypt
ISO 32000-1 §7.6 define la seguridad del documento mediante unas pocas entradas de diccionario, y PDF Library for Delphi las refleja una a una en el registro TPDFEncryption. La versión del filtro V y la revisión R seleccionan la familia de algoritmos. Length contiene el tamaño de la clave. Los bits de permisos están en P, las cadenas de validación de las contraseñas de propietario y usuario en O y U (con OE y UE añadidos para AES-256), un indicador EncryptMetadata las acompaña y otros tres campos nombran los filtros criptográficos aplicados respectivamente a cadenas, flujos y archivos incrustados
El valor de este registro es que no interpreta nada por usted. Devuelve el diccionario sin procesar y le deja extraer las conclusiones, que es exactamente lo que necesita una auditoría. El caso de texto plano dentro de contenido cifrado aparece en StringFilterIdentity y StreamFilterIdentity: cuando cualquiera de ellos es verdadero, los datos correspondientes atraviesan intactos el filtro Identity, sin importar qué informe el estado de cifrado del documento. Un escáner que se detiene en «hay un diccionario /Encrypt» declarará protegido un archivo cuyas cadenas y flujos están al descubierto. El mismo matiz rige los metadatos. Cuando EncryptMetadata es falso, el paquete XMP queda legible para cualquier indexador aunque el contenido de las páginas no lo esté, algo importante en cuanto sus reglas de enrutamiento se basen en un campo de título o autor
Una breve comprobación de seguridad con la API plana
Para la mayoría de las canalizaciones, cuatro llamadas planas responden las preguntas cotidianas. LoadFromFile devuelve 1 si tiene éxito y, una vez abierto el documento, los inspectores de cifrado informan sobre su estado descifrado:
var
PDF: TPDFlib;
begin
PDF := TPDFlib.Create;
try
if PDF.LoadFromFile('contract.pdf', UserPassword) <> 1 then
raise Exception.Create('Open failed: wrong password or damaged file');
Writeln('status : ', PDF.EncryptionStatus); // decrypted / encrypted / unknown
Writeln('algorithm : ', PDF.EncryptionAlgorithm); // RC4 vs AES family
Writeln('strength : ', PDF.EncryptionStrength); // clase de longitud de clave
Writeln('owner pw? : ', PDF.CheckPassword(CandidatePassword));
finally
PDF.Free;
end;
end;
CheckPassword importa más de lo que sugiere su firma de una línea. PDF define dos contraseñas con facultades desiguales. La contraseña de usuario es necesaria para abrir el archivo. La contraseña de propietario concede plenos derechos y anula todos los bits de permisos. Los bytes en disco son idénticos en ambos casos, pero una sesión abierta con la contraseña de propietario puede hacer cosas que la sesión con contraseña de usuario no puede; por eso una auditoría que no registre qué credencial se presentó está registrando solo la mitad de la verdad. La capa de clases permite consultar esa distinción. TPDFDocument.HasUserPassword y HasOwnerPassword indican qué exige el archivo, mientras que IsUserPassword y IsOwnerPassword indican qué contraseña abrió realmente la sesión actual. Registre ese hecho. Nunca registre los valores de las contraseñas
La escala Strength, donde «AES-256» significa dos cosas
Las funciones planas Encrypt y EncryptFile aceptan un entero Strength con cinco valores significativos: 0 para RC4 de 40 bits, 1 para RC4 de 128 bits, 2 para AES de 128 bits legible desde Acrobat 7, 3 para AES de 256 bits introducido con Acrobat 9 y 4 para AES de 256 bits requerido por Acrobat X y posteriores
La parte interesante es que 3 y 4 se etiquetan ambos como AES-256 y no son el mismo esquema. Strength 3 corresponde a la revisión 5 del controlador de seguridad, un diseño provisional que Acrobat 9 distribuyó y que ISO nunca adoptó. Strength 4 corresponde a la revisión 6, cuya función de derivación de claves se reforzó y estandarizó en ISO 32000-2. Para un documento que cree hoy no hay razón para elegir 3 en lugar de 4. Para una auditoría, la diferencia es decisiva: una política que diga «AES-256 según ISO 32000-2» solo se cumple con R6, y un archivo R5 que se denomina AES-256 incumple esa política aunque supere una comprobación ingenua de resistencia. La capa de clases los mantiene separados por nombre, esAES256Bit para R5 frente a esAES256BitAcroX para R6, y la propiedad EncryptionAcroX responde a la cuestión de la revisión con un único booleano
Bits de permisos y su letra pequeña sobre la longitud de clave
EncodePermissions empaqueta ocho indicadores en el entero que esperan Encrypt y EncryptFile. Imprimir, copiar, cambiar y añadir notas forman el conjunto básico; rellenar campos, copiar para accesibilidad, ensamblar e imprimir a máxima calidad forman el conjunto ampliado. La letra pequeña, que la propia demostración de cifrado de la biblioteca expone claramente, es que los cuatro ampliados solo tienen efecto con una resistencia de 128 bits o superior. El indicador de impresión a máxima calidad se somete a la misma regla: bórrelo para forzar una impresión de baja resolución y un documento de 40 bits le ignorará, porque esa degradación también requiere cifrado de 128 bits o superior. Codifique una política de «solo impresión de baja resolución» en un archivo de 40 bits y todos los visores imprimirán de todos modos a máxima calidad
La pregunta más profunda es quién aplica cualquiera de esos bits, y la respuesta es nadie en quien pueda confiar. Los permisos son instrucciones para lectores conformes, no restricciones criptográficas. La clave de descifrado es idéntica tanto si se permite copiar como si se deniega, de modo que un conjunto de permisos restrictivo solo mantiene honestos a los visores honestos. Un lector que decide ignorar los bits no enfrenta ningún obstáculo criptográfico. Si la obligación es impedir la extracción en lugar de desaconsejarla, el archivo necesita una contraseña de usuario y el flujo de trabajo necesita controles de proceso a su alrededor; un informe de auditoría debe indicar bajo cuál de esos dos regímenes se encuentra realmente cada archivo, en vez de tratar un indicador de permiso como un candado
Establecer una política y demostrar que se aplicó
Aplicar cifrado a archivos existentes no exige cargarlos en el árbol de objetos. EncryptFile procesa la entrada a la salida en una sola llamada, y el bucle de auditoría vuelve a abrir el resultado para confirmar qué se escribió en el disco. La demostración de cifrado distribuida sigue la misma secuencia de escribir y después volver a leer:
var
PDF: TPDFlib;
R: Integer;
begin
PDF := TPDFlib.Create;
try
R := PDF.EncryptFile('in.pdf', 'out.pdf', 'owner-secret', 'user-secret', 4,
PDF.EncodePermissions(1, 0, 0, 0, // print allowed; copy/change/notes denied
0, 0, 0, 1)); // conjunto extendido: solo impresión a calidad completa
if (R = 1) and (PDF.LoadFromFile('out.pdf', 'user-secret') = 1) then
begin
Writeln('algorithm = ', PDF.EncryptionAlgorithm);
Writeln('strength = ', PDF.EncryptionStrength);
Writeln('owner pw accepted: ', PDF.CheckPassword('owner-secret'));
end;
finally
PDF.Free;
end;
end;
Los equipos que trabajan en la capa de documento disponen de la misma operación con conjuntos tipados en lugar de empaquetado de bits, algo que supera una revisión de código con mucha menos necesidad de entrecerrar los ojos:
if not Doc.Encrypt('owner-secret', 'user-secret', esAES256BitAcroX,
[ppCanPrint], [ppCanPrintFull]) then
raise Exception.Create('Encryption failed');
En ambos casos, el paso de volver a leer no es una ceremonia opcional. Detecta los errores de despliegue que de otro modo aparecerían meses después en la máquina de un cliente: una compilación antigua de la biblioteca que degrada silenciosamente la resistencia solicitada, una ruta de salida que nunca se escribió porque el directorio era de solo lectura, un entero de permisos cuyos argumentos se pasaron en el orden equivocado. Los tres superan una prueba de humo local y fallan en producción; reabrir la salida convierte cada uno en una excepción que ve durante la ejecución que creó el archivo. GetEncryptionFingerprint devuelve un valor compacto que puede almacenar con el registro del trabajo, de modo que una comparación posterior puede indicar si dos salidas comparten la misma configuración de cifrado sin reabrir ninguna de ellas
Falsos positivos de auditoría que conviene programar
Algunos patrones empujan de forma fiable a los escáneres de seguridad a una conclusión errónea, y cada uno surge de reducir una pregunta de varias partes a una respuesta de sí o no. El filtro criptográfico Identity es el ejemplo más claro. Hay un diccionario /Encrypt, el archivo se declara cifrado y, sin embargo, las cadenas y los flujos atraviesan sin cambios el filtro Identity, de modo que el contenido real está en texto plano. Leer StringFilterIdentity y StreamFilterIdentity antes de declarar protegido nada es la solución
La separación de los metadatos es más sutil. EncryptMetadata puede diferir del resto del documento en ambos sentidos, dejando un archivo cifrado con un paquete XMP legible o, con menor frecuencia, lo contrario. «El archivo está cifrado» no dice nada sobre si sus metadatos lo están, lo que importa en cuanto un indexador o una regla de enrutamiento intenta usar el título. Los archivos incrustados añaden un tercer eje: PDF permite un filtro criptográfico específico solo para adjuntos, de modo que los adjuntos pueden ser la única parte cifrada de un documento abierto o la única parte en texto plano de uno cifrado. Capture las tres asignaciones de filtro como campos separados para cadenas, flujos y archivos incrustados, y ninguna de estas trampas podrá sorprenderle. Almacene un único booleano y la llamada errónea será solo cuestión de tiempo
Eliminar el cifrado y elegirlo para archivos nuevos
Una auditoría suele acabar con la decisión de eliminar la protección, y la mecánica no es el obstáculo en ese caso. DecryptFile(InputFileName, OutputFileName, Password) escribe una copia descifrada sin una carga completa, y Decrypt sobre el documento cargado hace lo mismo en memoria una vez que el archivo ya está abierto. Ambos requieren una contraseña válida; ninguno evita la criptografía. El verdadero umbral es de política, no de código, así que sus reglas de admisión deben declarar claramente cuándo se permite la eliminación y registrar la clase de contraseña que la autorizó, porque el paso técnico por sí mismo no deja rastro
La elección para una salida nueva es más limitada de lo que sugieren los cinco valores Strength. Use Strength 4, AES-256 revisión 6, salvo que deba abrir archivos en visores anteriores a Acrobat X. Strength 2, AES-128, es el mínimo pragmático para una flota de visores antiguos que no puede actualizarse. Las opciones RC4 de 0 y 1 existen para que pueda leer y auditar archivos históricos, no para producir nada nuevo con ellas; recurrir a ellas en un diseño de 2026 indica que algún requisito previo está obsoleto
El estado de cifrado alimenta directamente las decisiones de firma, ya que un banco de trabajo que valida y firma documentos necesita la misma disciplina de volver a leer en la que se apoya esta auditoría. Ese terreno se trata en el artículo sobre el banco de trabajo de cumplimiento y firma. Cuando un lote aplica EncryptFile a miles de documentos grandes, la guía de acceso directo para PDF grandes muestra cómo mantener estable el uso de memoria durante la ejecución. La referencia completa de la API de cifrado está en la página de producto de PDF Library for Delphi