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_TySignOptions.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_validverifica 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:
- Aspose.Words for Python via .NET 26.9 o posterior. Instale o actualice desde PyPI:
pip install --upgrade "aspose-words>=26.9"
- Un certificado de firma en formato PKCS#12 (
.pfxo.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.createlee 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_Tindica a Aspose.Words que genere una firma XAdES‑T, que es XAdES‑EPES más una marca de tiempo de la firma.DigitalSignatureTimestampSettingscontiene 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.signescribe una copia firmada enOUTPUT_DOCy 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íntoma | Causa probable | Qué hacer |
|---|---|---|
| La firma se realiza con éxito, pero no se incrusta ninguna marca de tiempo | Só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) Unauthorized | El TSA requiere credenciales, o las credenciales son incorrectas | Proporcione 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 proxy | La URL del TSA es incorrecta, o un firewall o proxy bloquea la solicitud | Verifique la URL y confirme que la máquina que ejecuta su script puede alcanzar el TSA. |
RuntimeError que menciona The operation has timed out | El TSA no respondió dentro del tiempo de espera | Reintente, o pase un timeout más largo a DigitalSignatureTimestampSettings. |
| Un archivo de salida vacío permanece después de un error | sign crea el archivo de destino antes de que falle la solicitud al TSA | Elimine 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 archivo | La entrada es un archivo ODT | Marque 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 Linux | El runtime .NET incluido en el paquete Python necesita OpenSSL 1.1 y una versión de ICU compatible | Instale 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
¿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.¿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_Ty la claseDigitalSignatureTimestampSettings. Las versiones anteriores solo pueden agregar marcas de tiempo a firmas en la salida PDF.¿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.¿Qué formatos de archivo pueden recibir una marca de tiempo?
Los archivos DOCX y DOC firmados conX_AD_ES_Treciben 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.¿
is_validconfirma que la marca de tiempo es válida?
No.DigitalSignature.is_validverifica 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.¿Qué ocurre si no se puede contactar al TSA?
DigitalSignatureUtil.signgenera unRuntimeErrorque 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.
