Una firma digital registra quién firmó un documento. Una marca de tiempo confiable registra cuándo, y ese momento proviene de una autoridad de sellado de tiempo (TSA) independiente en lugar del reloj de la computadora del firmante. Este tutorial le muestra cómo firmar un documento Word con una firma XAdES‑T con marca de tiempo en Python, y luego confirmar que la marca de tiempo está en el archivo.

Conclusiones clave

  • Establezca ambos SignOptions.xml_dsig_level = XmlDsigLevel.X_AD_ES_T y SignOptions.timestamp_settings. Cada uno por sí solo produce una firma sin marca de tiempo, y ningún error lo indica.
  • Aspose.Words solicita la marca de tiempo durante DigitalSignatureUtil.sign, por lo que esa llamada necesita acceso a la red al TSA.
  • DOCX y DOC pueden recibir una marca de tiempo. Los archivos ODT no pueden.
  • DigitalSignature.is_valid verifica la firma, no la marca de tiempo. Confirme la marca de tiempo por separado.

Qué añade una marca de tiempo confiable a una firma

Cada firma que crea Aspose.Words lleva una hora de firma, establecida por SignOptions.sign_time. Ese valor proviene del equipo del firmante, por lo que cualquiera que dispute el documento también puede disputar la hora.

Una firma XAdES‑T agrega evidencia independiente. Después de que el documento se firma, Aspose.Words envía un hash del valor de la firma a una TSA. La TSA devuelve un token RFC 3161, firmado con su propio certificado, que vincula el hash a un momento específico. Ese token se almacena dentro de la firma. Un verificador puede entonces demostrar que la firma existía en ese momento, lo que es más importante cuando el certificado de firma expira o es revocado más adelante.

Requisitos previos

Antes de ejecutar el ejemplo, asegúrate de tener:

pip install --upgrade "aspose-words>=26.9"
  • Un certificado de firma en formato PKCS#12 (.pfx o .p12) y su contraseña.
  • Una URL de autoridad de sellado de tiempo. El ejemplo usa FreeTSA (https://freetsa.org/tsr), una TSA pública gratuita que es conveniente para pruebas. Para documentos de producción, use la TSA que su organización o proveedor de certificados recomiende, ya que las personas que verifican sus documentos deben confiar en el certificado de esa TSA.
  • Credenciales de TSA, solo si su TSA requiere autenticación.

Sin una licencia, Aspose.Words se ejecuta en modo de evaluación con limitaciones. Una licencia temporal las elimina mientras pruebas.

Firmar un documento Word con una marca de tiempo confiable

El siguiente script firma un archivo DOCX con una firma XAdES‑T e inserta una marca de tiempo del TSA.

import datetime
import aspose.words as aw

# Replace these values with your own files and credentials.
INPUT_DOC = "contract.docx"
OUTPUT_DOC = "contract-signed.docx"
CERT_FILE = "signing-cert.pfx"
CERT_PASSWORD = "your-pfx-password"

TSA_URL = "https://freetsa.org/tsr"
TSA_USER = ""       # Fill in only if your TSA requires authentication.
TSA_PASSWORD = ""

# Load the signing certificate from a PKCS#12 file.
cert_holder = aw.digitalsignatures.CertificateHolder.create(
    file_name=CERT_FILE, password=CERT_PASSWORD)

# Request an XAdES-T signature and point it at a timestamp authority.
sign_options = aw.digitalsignatures.SignOptions()
sign_options.xml_dsig_level = aw.digitalsignatures.XmlDsigLevel.X_AD_ES_T
sign_options.timestamp_settings = aw.digitalsignatures.DigitalSignatureTimestampSettings(
    server_url=TSA_URL,
    user_name=TSA_USER,
    password=TSA_PASSWORD,
    timeout=datetime.timedelta(seconds=60),  # Default is 100 seconds.
)

# Sign the document. Aspose.Words contacts the TSA during this call.
aw.digitalsignatures.DigitalSignatureUtil.sign(
    src_file_name=INPUT_DOC,
    dst_file_name=OUTPUT_DOC,
    cert_holder=cert_holder,
    sign_options=sign_options,
)
print(f"Signed with a trusted timestamp: {OUTPUT_DOC}")

Cómo funciona el código

  • CertificateHolder.create lee la clave privada y la cadena de certificados del archivo .pfx. Una contraseña incorrecta falla aquí, antes de que comience cualquier firma.
  • XmlDsigLevel.X_AD_ES_T indica a Aspose.Words que genere una firma XAdES‑T, que es XAdES‑EPES más una marca de tiempo de la firma.
  • DigitalSignatureTimestampSettings contiene la URL del TSA, un nombre de usuario y contraseña opcionales, y un tiempo de espera opcional. Las cadenas vacías están bien para un TSA que acepta solicitudes anónimas. Si el TSA responde con un desafío de autenticación HTTP, Aspose.Words envía las credenciales que proporcionó.
  • DigitalSignatureUtil.sign escribe una copia firmada en OUTPUT_DOC y deja el archivo de entrada sin cambios. Firma un documento sin firmar: si la entrada ya tiene una firma, la salida contiene tanto la firma existente como la nueva.

Verifique que la marca de tiempo se haya incrustado

Los objetos DigitalSignature que devuelve Aspose.Words no exponen la marca de tiempo, y is_valid no la verifica. En pruebas, un documento cuyo token de marca de tiempo había sido deliberadamente corrompido todavía reportaba is_valid como True. Para confirmar la marca de tiempo, examine el XML de la firma almacenado en el paquete DOCX:

import base64
import re
import zipfile
import aspose.words as aw

SIGNED_DOC = "contract-signed.docx"

# 1. Check the signature itself.
for sig in aw.digitalsignatures.DigitalSignatureUtil.load_signatures(SIGNED_DOC):
    print(f"Signer: {sig.subject_name} | valid: {sig.is_valid}")

# 2. Check that a timestamp token was embedded, and save it for inspection.
with zipfile.ZipFile(SIGNED_DOC) as package:
    for part in package.namelist():
        if part.startswith("_xmlsignatures/sig") and part.endswith(".xml"):
            xml = package.read(part).decode("utf-8")
            match = re.search(r"<(?:\w+:)?EncapsulatedTimeStamp[^>]*>([^<]+)<", xml)
            if match:
                with open("timestamp-token.der", "wb") as f:
                    f.write(base64.b64decode(match.group(1)))
                print(f"{part}: timestamp embedded (saved to timestamp-token.der)")
            else:
                print(f"{part}: no timestamp found")

Para un DOCX firmado, deberías ver una salida similar a esta:

Signer: CN=Your Name | valid: True
_xmlsignatures/sig1.xml: timestamp embedded (saved to timestamp-token.der)

Para leer la hora certificada por la TSA, pase el token guardado a OpenSSL:

openssl ts -reply -token_in -in timestamp-token.der -token_out -text

La línea Time stamp muestra la hora certificada en GMT, y la línea TSA identifica la autoridad que la emitió.

Esta comprobación lee el formato de paquete DOCX. Un archivo DOC almacena su firma en un contenedor binario, por lo que el script basado en ZIP no se aplica a él.

Solución de problemas de firma y errores de marca de tiempo

SíntomaCausa probableQué hacer
La firma se realiza con éxito, pero no se incrusta ninguna marca de tiempoSólo uno de xml_dsig_level = X_AD_ES_T y timestamp_settings se configuróConfigura ambos antes de llamar a sign. Con XML_D_SIG o X_AD_ES_EPES, la configuración de marca de tiempo se ignora.
RuntimeError que menciona (401) UnauthorizedEl TSA requiere credenciales, o las credenciales son incorrectasProporcione el nombre de usuario y la contraseña emitidos por su proveedor de TSA.
RuntimeError que menciona una conexión rechazada o un error de proxyLa URL del TSA es incorrecta, o un firewall o proxy bloquea la solicitudVerifique la URL y confirme que la máquina que ejecuta su script puede alcanzar el TSA.
RuntimeError que menciona The operation has timed outEl TSA no respondió dentro del tiempo de esperaReintente, o pase un timeout más largo a DigitalSignatureTimestampSettings.
Un archivo de salida vacío permanece después de un errorsign crea el archivo de destino antes de que falle la solicitud al TSAElimine el archivo de destino antes de reintentar, o escriba en una ruta temporal y renómbrelo después de una llamada exitosa.
RuntimeError que indica que la marca de tiempo no es compatible con este formato de archivoLa entrada es un archivo ODTMarque de tiempo archivos DOCX o DOC, o conviértalos a PDF y use la ruta de firma PDF descrita a continuación.
No usable version of libssl was found, o un bloqueo por un paquete ICU faltante, en LinuxEl runtime .NET incluido en el paquete Python necesita OpenSSL 1.1 y una versión de ICU compatibleInstale OpenSSL 1.1, o instale una ICU compatible. Si su aplicación no necesita formato específico de cultura, establezca DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 para el error de ICU.

Alternativa: Marca de tiempo a una firma en la salida PDF

Si sus destinatarios necesitan un PDF en lugar de un archivo Word firmado, no necesita DigitalSignatureUtil. Firma y marca de tiempo el PDF al guardarlo estableciendo PdfSaveOptions.digital_signature_details.timestamp_settings a un objeto PdfDigitalSignatureTimestampSettings. Esta vía ha estado disponible mucho más tiempo que la versión 26.9. La PdfDigitalSignatureTimestampSettings reference incluye un ejemplo completo.

Próximos pasos

Para agregar líneas de firma, firmar con una imagen de línea de firma o eliminar firmas existentes, consulte Trabajar con firmas digitales en la documentación de Aspose.Words for Python.

FAQs

  1. ¿Qué agrega una marca de tiempo confiable a una firma digital?
    Una autoridad de marca de tiempo (TSA) certifica la hora en que se realizó la firma. Esa hora proviene de un tercero independiente en lugar del reloj del equipo del firmante, y permite al verificador demostrar que la firma existía antes de que el certificado de firma expirara o fuera revocado.

  2. ¿Qué versión de Aspose.Words admite la marcación de tiempo en DigitalSignatureUtil?
    La versión 26.9 de Aspose.Words for Python via .NET añadió SignOptions.timestamp_settings, XmlDsigLevel.X_AD_ES_T y la clase DigitalSignatureTimestampSettings. Las versiones anteriores solo pueden agregar marcas de tiempo a firmas en la salida PDF.

  3. ¿Necesito establecer tanto xml_dsig_level como timestamp_settings?
    Sí. Si solo se establece uno de ellos, Aspose.Words aún firma el documento pero no solicita ni incrusta una marca de tiempo, y no genera un error.

  4. ¿Qué formatos de archivo pueden recibir una marca de tiempo?
    Los archivos DOCX y DOC firmados con X_AD_ES_T reciben una marca de tiempo. Firmar un archivo ODT con una marca de tiempo genera un error que indica que el formato no admite la marcación de tiempo.

  5. ¿is_valid confirma que la marca de tiempo es válida?
    No. DigitalSignature.is_valid verifica la firma en sí. Para confirmar la marca de tiempo, compruebe que el XML de la firma contenga un token de marca de tiempo y examine el token con una herramienta como OpenSSL.

  6. ¿Qué ocurre si no se puede contactar al TSA?
    DigitalSignatureUtil.sign genera un RuntimeError que describe el problema de red, autenticación o tiempo de espera. La ruta de destino puede quedar como un archivo vacío, por lo que debe eliminarse antes de volver a intentarlo.

Obtenga una Licencia Gratuita y Soporte