A digital signature records who signed a document. A trusted timestamp records when, and that time comes from an independent timestamp authority (TSA) rather than the clock on the signer’s computer. This tutorial shows you how to sign a Word document with a timestamped XAdES-T signature in Python, then confirm that the timestamp is in the file.
Key Takeaways
- Set both
SignOptions.xml_dsig_level = XmlDsigLevel.X_AD_ES_TandSignOptions.timestamp_settings. Either one on its own produces a signature without a timestamp, and no error tells you so. - Aspose.Words requests the timestamp during
DigitalSignatureUtil.sign, so that call needs network access to the TSA. - DOCX and DOC files can be timestamped. ODT files cannot.
DigitalSignature.is_validchecks the signature, not the timestamp. Confirm the timestamp separately.
What a Trusted Timestamp Adds to a Signature
Every signature Aspose.Words creates carries a signing time, set by SignOptions.sign_time. That value comes from the signer’s machine, so anyone disputing the document can dispute the time as well.
An XAdES-T signature adds independent evidence. After the document is signed, Aspose.Words sends a hash of the signature value to a TSA. The TSA returns an RFC 3161 token, signed with its own certificate, that binds the hash to a specific time. That token is stored inside the signature. A verifier can then show that the signature existed at that moment, which matters most when the signing certificate later expires or is revoked.
Prerequisites
Before you run the example, make sure you have:
Aspose.Words for Python via .NET 26.9 or later. Install or upgrade from PyPI:
pip install --upgrade "aspose-words>=26.9"A signing certificate in PKCS#12 format (
.pfxor.p12) and its password.A timestamp authority URL. The example uses FreeTSA (
https://freetsa.org/tsr), a free public TSA that is convenient for testing. For production documents, use the TSA your organization or certificate provider recommends, since the people checking your documents must trust that TSA’s certificate.TSA credentials, only if your TSA requires authentication.
Without a license, Aspose.Words runs in evaluation mode with limitations. A temporary license removes them while you test.
Sign a Word Document with a Trusted Timestamp
The following script signs a DOCX file with an XAdES-T signature and embeds a timestamp from the 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}")
How the Code Works
CertificateHolder.createreads the private key and certificate chain from the.pfxfile. A wrong password fails here, before any signing starts.XmlDsigLevel.X_AD_ES_Ttells Aspose.Words to build an XAdES-T signature, which is XAdES-EPES plus a signature timestamp.DigitalSignatureTimestampSettingsholds the TSA URL, an optional user name and password, and an optional timeout. Empty strings are fine for a TSA that accepts anonymous requests. If the TSA responds with an HTTP authentication challenge, Aspose.Words sends the credentials you supplied.DigitalSignatureUtil.signwrites a signed copy toOUTPUT_DOCand leaves the input file unchanged. Sign an unsigned document: if the input already has a signature, the output contains both the existing signature and the new one.
Check That the Timestamp Was Embedded
The DigitalSignature objects that Aspose.Words returns do not expose the timestamp, and is_valid does not check it. In testing, a document whose timestamp token had been deliberately corrupted still reported is_valid as True. To confirm the timestamp, look inside the signature XML stored in the DOCX package:
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")
For a signed DOCX, you should see output similar to this:
Signer: CN=Your Name | valid: True
_xmlsignatures/sig1.xml: timestamp embedded (saved to timestamp-token.der)
To read the time the TSA certified, pass the saved token to OpenSSL:
openssl ts -reply -token_in -in timestamp-token.der -token_out -text
The Time stamp line shows the certified time in GMT, and the TSA line identifies the authority that issued it.
This check reads the DOCX package format. A DOC file stores its signature in a binary container, so the ZIP-based script does not apply to it.
Troubleshooting Signing and Timestamp Errors
| Symptom | Likely cause | What to do |
|---|---|---|
| Signing succeeds, but no timestamp is embedded | Only one of xml_dsig_level = X_AD_ES_T and timestamp_settings was set | Set both before calling sign. With XML_D_SIG or X_AD_ES_EPES, the timestamp settings are ignored. |
RuntimeError mentioning (401) Unauthorized | The TSA requires credentials, or the credentials are wrong | Pass the user name and password issued by your TSA provider. |
RuntimeError mentioning a refused connection or a proxy error | The TSA URL is wrong, or a firewall or proxy blocks the request | Check the URL and confirm that the machine running your script can reach the TSA. |
RuntimeError mentioning The operation has timed out | The TSA did not answer within the timeout | Retry, or pass a longer timeout to DigitalSignatureTimestampSettings. |
| An empty output file remains after an error | sign creates the destination file before the TSA request fails | Delete the destination file before retrying, or write to a temporary path and rename it after a successful call. |
RuntimeError saying timestamping is not supported by this file format | The input is an ODT file | Timestamp DOCX or DOC files, or convert to PDF and use the PDF signing route described below. |
No usable version of libssl was found, or a crash about a missing ICU package, on Linux | The Python package’s bundled .NET runtime needs OpenSSL 1.1 and a supported ICU version | Install OpenSSL 1.1, or install a supported ICU. If your application doesn’t need culture-specific formatting, set DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 for the ICU error. |
Alternative: Timestamp a Signature in PDF Output
If your recipients need a PDF rather than a signed Word file, you don’t need DigitalSignatureUtil. Sign and timestamp the PDF while saving it by setting PdfSaveOptions.digital_signature_details.timestamp_settings to a PdfDigitalSignatureTimestampSettings object. This route has been available for much longer than 26.9. The PdfDigitalSignatureTimestampSettings reference includes a complete example.
Next Steps
To add signature lines, sign with a signature line image, or remove existing signatures, see Work with Digital Signatures in the Aspose.Words for Python documentation.
FAQs
- What does a trusted timestamp add to a digital signature? A timestamp authority (TSA) certifies the time the signature was made. That time comes from an independent third party rather than the signer’s computer clock, and it lets a verifier show the signature existed before the signing certificate expired or was revoked.
- Which Aspose.Words version supports timestamping in DigitalSignatureUtil?
Version 26.9 of Aspose.Words for Python via .NET added
SignOptions.timestamp_settings,XmlDsigLevel.X_AD_ES_T, and theDigitalSignatureTimestampSettingsclass. Earlier versions can only timestamp signatures in PDF output. - Do I need to set both xml_dsig_level and timestamp_settings? Yes. With only one of them set, Aspose.Words still signs the document but does not request or embed a timestamp, and it does not raise an error.
- Which file formats can be timestamped?
DOCX and DOC files signed with
X_AD_ES_Treceive a timestamp. Signing an ODT file with a timestamp raises an error saying the format does not support timestamping. - Does is_valid confirm that the timestamp is valid?
No.
DigitalSignature.is_validchecks the signature itself. To confirm the timestamp, check that the signature XML contains a timestamp token and inspect the token with a tool such as OpenSSL. - What happens if the TSA cannot be reached?
DigitalSignatureUtil.signraises aRuntimeErrorthat describes the network, authentication, or timeout problem. The destination path may be left as an empty file, so delete it before retrying.
