Artículo técnico

Cifrar archivos XLSX con AES en Delphi usando HotXLS

Excel expone dos cosas que se llaman "contraseña", y solo una de ellas es cifrado. La contraseña de apertura alimenta un cifrado real: sin ella el archivo no puede leerse en absoluto. Las contraseñas de protección de hoja de cálculo y de libro de trabajo no hacen nada por el estilo. Establecen un indicador que un editor cooperativo acepta respetar, y un libro de trabajo que no lleva más que ese indicador es un zip legible común con los datos en texto claro. Elige la equivocada y enviarás una nómina que parece bloqueada en Excel y se lee en cualquier editor de texto

La prueba toma diez segundos. Renombra un .xlsx protegido a .zip, ábrelo en cualquier herramienta de archivos comprimidos y mira xl/worksheets/sheet1.xml. Si los valores de las celdas están ahí en UTF-8 simple, el archivo no está cifrado, por muchos avisos de contraseña que Excel muestre cuando alguien intenta editar una celda. Esa brecha sobrevive durante años dentro de equipos que asumen que la protección de hoja es confidencialidad, y suele salir a la luz el día en que una revisión de seguridad ejecuta exactamente este renombrado

HotXLS es una biblioteca nativa de hojas de cálculo para Delphi y C++Builder, y mantiene las dos funciones en lados opuestos de esa línea. La protección de hoja de cálculo y de libro de trabajo son restricciones de edición respaldadas por un hash heredado deliberadamente débil. SaveAsEncrypted produce un paquete cifrado con AES que nada que no sea la contraseña abrirá. Las secciones siguientes cubren qué escribe esa llamada, la asimetría en torno a la cual tienes que diseñar (HotXLS escribe archivos cifrados pero no puede volver a leerlos), y en qué difiere la ruta XLS más antigua

Diagrama que contrasta la protección de hoja XLSX en Delphi, que almacena un hash débil y deja los datos de celda legibles en un zip simple, con SaveAsEncrypted de HotXLS, que deriva una clave AES-128 y escribe un contenedor de cifrado OLE
La protección de hoja almacena un hash débil y deja el paquete como un zip legible. SaveAsEncrypted deriva una clave AES-128 y escribe un contenedor OLE que ninguna herramienta de archivos comprimidos puede listar

Por qué la protección de hoja no es cifrado

Los métodos Protect de las hojas y ProtectWorkbook del libro de trabajo almacenan un hash de 4 dígitos hexadecimales de la contraseña. Ese es el algoritmo heredado que tanto OOXML como BIFF heredaron del Excel de los años noventa, y la documentación del formato nunca afirma que haga más que detener ediciones accidentales. El paquete sigue siendo un zip legible ordinario: datos de celda, fórmulas y cadenas compartidas, todo en XML de texto claro. El valor predeterminado lo empeora, no lo mejora. Cada celda comienza con Locked=True, así que llamar a Protect sin desbloquear primero un rango de entrada congela toda la hoja contra la edición mientras deja cada valor a plena vista

Nada de lo cual hace que la protección sea inútil. Guiar a los usuarios hacia rangos editables y estabilizar un diseño para imprimir son trabajos reales, cubiertos en nuestro artículo sobre protección de hojas de cálculo y configuración de página. Pero esos son trabajos de usabilidad. En el instante en que el requisito es la confidencialidad, la única API que lo responde es SaveAsEncrypted

Qué escribe realmente SaveAsEncrypted

La implementación sigue Standard Encryption de ECMA-376, especificado en [MS-OFFCRYPTO] sección 2.3.4. La contraseña pasa por 50 000 iteraciones de SHA-1 para derivar una clave AES-128. Un bloque verificador, cifrado con AES-128 en modo ECB, permite a un consumidor confirmar la contraseña antes de descifrar nada, y luego todo el paquete del libro de trabajo se cifra con AES-128 en modo CBC. Lo que aterriza en disco no es un zip en absoluto. Es un archivo compuesto OLE que contiene los flujos EncryptionInfo, EncryptedPackage y DataSpaces, sin ningún directorio xl/ que una herramienta de archivos comprimidos pueda listar, razón por la cual la prueba del renombrado ahora no encuentra nada legible. Excel 2007 y posteriores lo abren solo con la contraseña, y el LibreOffice actual también lee Standard Encryption

Diagrama de pipeline de una llamada SaveAsEncrypted de HotXLS en Delphi: la contraseña de la bóveda pasa por 50 000 rondas de SHA-1 hasta una clave AES-128, bloque verificador ECB y cifrado CBC del paquete, produciendo un archivo compuesto OLE comprobado con CanReadEncrypted
Una sola llamada a SaveAsEncrypted convierte una contraseña de la bóveda en una clave AES-128 y un archivo compuesto OLE. CanReadEncrypted le da al guardado una compuerta de aceptación verificable por máquina
var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  rc: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Payroll');
    Sheet.Cells[1, 1].Value := 'Employee';
    Sheet.Cells[1, 2].Value := 'Net pay';
    Sheet.Cells[2, 1].Value := 'A. Garcia';
    Sheet.Cells[2, 2].Value := 4815.16;

    rc := Book.SaveAsEncrypted('payroll-2026-06.xlsx', PasswordFromVault);
    if rc <> 1 then
      raise Exception.CreateFmt('Encrypted save failed (rc=%d)', [rc]);
  finally
    Book.Free;
  end;
end;

Trata la variable de contraseña con el mismo cuidado que una cadena de conexión. Obtenla de una bóveda o de un servicio de secretos generados en el último momento, nunca la registres, y nunca la escribas dentro del propio libro de trabajo. La comprobación del código de retorno no es una ceremonia opcional. Un guardado cifrado que falla a mitad de camino tiene que abortar la entrega, porque el único plan alternativo que el código llamador puede ofrecer es una copia sin cifrar, y esa copia es exactamente el incidente que esta función existe para evitar

También hay una prueba de aceptación verificable por máquina que no cuesta casi nada: llama a CanReadEncrypted sobre el archivo que acabas de escribir. Devuelve true solo cuando la salida es realmente un contenedor de cifrado, así que afirmarlo después de cada guardado cifrado detecta la regresión que más importa, una ruta de código que recurrió en silencio a un SaveAs simple, en el momento en que ocurre y no semanas después en la bandeja de entrada de un cliente. La última palabra sigue perteneciendo a una apertura manual en Excel con la contraseña real durante las pruebas de lanzamiento

Solo escritura por diseño: manejar EXlsxEncryptionNotImplemented

Esta es la asimetría que debería dar forma a la arquitectura de tu pipeline: HotXLS cifra al guardar pero no descifra al abrir. OpenEncrypted lanza EXlsxEncryptionNotImplemented cuando se apunta a un paquete realmente cifrado; sobre un libro de trabajo simple, simplemente pasa a un Open normal. El sondeo complementario CanReadEncrypted detecta el contenedor de cifrado OLE a bajo costo, así que el código de recepción puede encaminar esos archivos sin disparar la excepción:

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.CanReadEncrypted(FileName) then
    begin
      // Contenedor cifrado: HotXLS no puede descifrarlo.
      Writeln(FileName + ': needs manual decryption in Excel first');
      Exit;
    end;
    try
      Book.OpenEncrypted(FileName, '');   // los archivos simples pasan a Open
      Writeln(FileName + ': opened, ' + IntToStr(Book.Sheets.Count) + ' sheet(s)');
    except
      on EXlsxEncryptionNotImplemented do
        Writeln(FileName + ': encrypted - routed to manual queue');
    end;
  finally
    Book.Free;
  end;
end;

Esa asimetría tiene una lectura arquitectónica clara: cifra en el borde de entrega, al final. Mantén el original en texto claro dentro de tu límite de confianza, en una base de datos, un almacén de documentos o un recurso compartido con control de acceso, y produce la copia cifrada como el paso final antes de que el archivo salga del sistema. Un pipeline que archiva solo la salida cifrada se ha dejado fuera de sus propios datos, porque ninguna etapa posterior del mismo sistema puede reabrir esos archivos. Cuando un proceso HotXLS posterior necesite el libro de trabajo de nuevo, entrégale el original en texto claro, nunca el artefacto de entrega

Diagrama de arquitectura para servicios Delphi: el original del libro en texto claro permanece dentro de un límite de confianza, SaveAsEncrypted de HotXLS se ejecuta como último paso en el borde de entrega, y una advertencia contra archivar solo la copia cifrada
Cifra en el borde de entrega, al final, a partir de un original en texto claro guardado dentro del límite de confianza. Archivar solo la copia cifrada deja a cada etapa posterior fuera de sus propios datos

Standard Encryption con AES-128 y la línea de cumplimiento de AES-256

El cifrado de archivos de Office viene en dos generaciones. Standard Encryption, el que escribe HotXLS, usa AES-128 con derivación de clave SHA-1. Agile Encryption llegó después y pasa a AES-256 con SHA-512 y un contenedor de clave distinto, descrito en XML. Ambos se abren de forma transparente en Excel, y AES-128 sigue siendo computacionalmente sólido para proteger un archivo en tránsito hacia un cliente

La diferencia deja de ser académica el día en que un cuestionario de seguridad pide "cifrado AES-256 de archivos en reposo". Standard Encryption no cumple esa línea, sin importar cuán fuerte sea la contraseña, y ningún parámetro de SaveAsEncrypted cambia el algoritmo que emite. Así que declara el perfil con precisión en tu documentación de seguridad: AES-128, Standard Encryption de ECMA-376, derivación de clave SHA-1 con 50 000 iteraciones. Una afirmación que sobrevive a una revisión vale más que una optimista que se derrumba bajo una auditoría

La ruta XLS heredada: RC4 de salida, RC4 y XOR de entrada

La fachada BIFF tiene la forma opuesta. Su cifrado es más antiguo y más débil, pero el viaje de ida y vuelta es completo: lo que escribe, también puede leerlo de vuelta. Establecer EncryptionPassword antes de SaveAs produce un .xls cifrado con RC4 a través del mecanismo FilePass de BIFF, y Open con un parámetro de contraseña lee los tres esquemas heredados, RC4, RC4 CryptoAPI y la antigua ofuscación XOR:

var
  Writer, Reader: IXLSWorkbook;   // referencias de interfaz: sin Free manual
begin
  Writer := TXLSWorkbook.Create;
  Writer.Sheets.Add.Cells.Item[1, 1].Value := 'Confidential';
  Writer.EncryptionPassword := 'S3cret!';
  Writer.SaveAs('confidential.xls');

  Reader := TXLSWorkbook.Create;
  if Reader.Open('confidential.xls', 'S3cret!') > 0 then
    Writeln(Reader.Sheets[1].Cells.Item[1, 1].Value);  // Las entradas tienen base uno
end;

RC4 es criptografía obsoleta y nunca debería proteger datos que importen hoy; su único valor restante es la interoperabilidad con sistemas que todavía intercambian .xls. El lado de lectura, en cambio, se gana su lugar en el trabajo de migración. Un archivo heredado protegido con contraseña se abre con Open(FileName, Password), pasa al modelo OOXML y se vuelve a asegurar a través de la ruta AES, una actualización unidireccional que se ejecuta sin Excel en ningún punto del ciclo. Para entregas cifradas de alto volumen, las notas de rendimiento del lado de guardado de nuestro artículo sobre escrituras en streaming para trabajos por lotes en servidor se aplican a la fase de construcción de contenido que ocurre antes del cifrado

El cifrado y la protección no son rivales

Un punto más que vale la pena zanjar, porque surge en el momento en que alguien lee la advertencia al inicio de esta página como "la protección no vale nada". No es así. El cifrado y la protección responden preguntas distintas, y se apilan limpiamente. El cifrado decide quién puede abrir el archivo; la protección decide qué puede cambiar un lector que ya está dentro. Una entrega de nómina puede razonablemente hacer ambas cosas: cifrar el paquete para que solo quien tenga la contraseña lo vea, y luego bloquear las celdas de fórmula para que el destinatario pueda filtrar y ordenar pero no reescribir en silencio los cálculos. El error nunca es agregar protección. El error es dejar que su presencia sustituya al cifrado cuando el requisito era la confidencialidad

El lado de la custodia no tiene red de seguridad, y eso es por diseño. La derivación de clave de 50 000 iteraciones existe para hacer costosas las adivinanzas, y nada dentro del archivo deposita el secreto en garantía. Una contraseña perdida son datos perdidos. Genera, entrega y almacena estas contraseñas con la misma disciplina que aplicas a las credenciales de base de datos, y el cifrado cumple su parte

El cifrado real de archivos es una sola llamada en HotXLS. La disciplina vive en todo lo que rodea a la llamada: la custodia de la contraseña, el límite de solo escritura que impide a HotXLS reabrir su propia salida, y una afirmación de algoritmo que puedas defender en una auditoría. SaveAsEncrypted y el viaje de ida y vuelta heredado se incluyen con el componente HotXLS para Delphi, ejecutándose de forma nativa en procesos Delphi y C++Builder sin automatización de Excel en ningún punto de la ruta