Электронная подпись фиксирует, кто подписал документ. Доверенный временной штамп фиксирует когда, и это время берётся от независимого органа временных меток (TSA), а не от часов компьютера подписывающего. В этом руководстве показано, как подписать документ Word подписью XAdES‑T с временной меткой в Python, а затем подтвердить, что временная метка присутствует в файле.
Ключевые выводы
- Установите обе
SignOptions.xml_dsig_level = XmlDsigLevel.X_AD_ES_TиSignOptions.timestamp_settings. Любой из них по отдельности приводит к подписи без отметки времени, и никакая ошибка вам об этом не сообщает. - Aspose.Words запрашивает отметку времени во время
DigitalSignatureUtil.sign, поэтому этот вызов требует сетевого доступа к TSA. - DOCX и DOC файлы можно подписать отметкой времени. Файлы ODT — нет.
DigitalSignature.is_validпроверяет подпись, а не отметку времени. Подтвердите отметку времени отдельно.
Что добавляет доверенная метка времени к подписи
Каждая подпись, создаваемая Aspose.Words, содержит время подписи, задаваемое SignOptions.sign_time. Это значение берётся с машины подписанта, поэтому любой, кто оспаривает документ, может оспорить и время.
Подпись XAdES‑T добавляет независимое доказательство. После подписания документа Aspose.Words отправляет хеш значения подписи в TSA. TSA возвращает токен RFC 3161, подписанный своим собственным сертификатом, который связывает хеш с конкретным временем. Этот токен сохраняется внутри подписи. Затем проверяющий может показать, что подпись существовала в тот момент, что особенно важно, когда сертификат подписи позже истекает или отзывается.
Требования
Прежде чем запустить пример, убедитесь, что у вас есть:
- Aspose.Words for Python via .NET 26.9 или новее. Установите или обновите из PyPI:
pip install --upgrade "aspose-words>=26.9"
- Сертификат подписи в формате PKCS#12 (
.pfxили.p12) и его пароль. - URL службы отметки времени. В примере используется FreeTSA (
https://freetsa.org/tsr), бесплатный публичный TSA, удобный для тестирования. Для производственных документов используйте TSA, рекомендованный вашей организацией или поставщиком сертификатов, поскольку лица, проверяющие ваши документы, должны доверять сертификату этого TSA. - Учетные данные TSA, только если ваш TSA требует аутентификации.
Без лицензии Aspose.Words работает в режиме оценки с ограничениями. Временная лицензия удаляет их, пока вы тестируете.
Подпишите документ Word с доверенным временным штампом
Следующий скрипт подписывает файл DOCX подписью XAdES‑T и встраивает временной штамп от 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}")
Как работает код
CertificateHolder.createсчитывает закрытый ключ и цепочку сертификатов из файла.pfx. Неправильный пароль приводит к ошибке здесь, до начала подписи.XmlDsigLevel.X_AD_ES_Tуказывает Aspose.Words построить подпись XAdES‑T, которая представляет собой XAdES‑EPES плюс метку времени подписи.DigitalSignatureTimestampSettingsсодержит URL TSA, необязательное имя пользователя и пароль, а также необязательный тайм‑аут. Пустые строки допустимы для TSA, принимающего анонимные запросы. Если TSA отвечает HTTP‑запросом на аутентификацию, Aspose.Words отправляет предоставленные вами учетные данные.DigitalSignatureUtil.signзаписывает подписанную копию вOUTPUT_DOCи оставляет исходный файл без изменений. Подпишите неподписанный документ: если входной файл уже содержит подпись, в выходном файле будут как существующая подпись, так и новая.
Проверьте, что метка времени была встроена
Объекты DigitalSignature, которые возвращает Aspose.Words, не раскрывают метку времени, и is_valid её не проверяет. При тестировании документ, у которого токен метки времени был преднамеренно повреждён, всё равно сообщал is_valid как True. Чтобы подтвердить метку времени, посмотрите внутрь XML‑подписи, хранящейся в пакете 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")
Для подписанного DOCX вы должны увидеть вывод, похожий на следующий:
Signer: CN=Your Name | valid: True
_xmlsignatures/sig1.xml: timestamp embedded (saved to timestamp-token.der)
Чтобы прочитать время, подтверждённое TSA, передайте сохранённый токен в OpenSSL:
openssl ts -reply -token_in -in timestamp-token.der -token_out -text
Строка Time stamp показывает сертифицированное время в GMT, а строка TSA указывает организацию, выдавшую его.
Эта проверка читает формат пакета DOCX. Файл DOC хранит свою подпись в бинарном контейнере, поэтому скрипт, основанный на ZIP, к нему не применяется.
Устранение проблем с подписью и отметкой времени
| Симптом | Вероятная причина | Что делать |
|---|---|---|
| Подпись выполнена успешно, но отметка времени не внедрена | Было установлено только одно из xml_dsig_level = X_AD_ES_T и timestamp_settings | Установите оба параметра перед вызовом sign. При использовании XML_D_SIG или X_AD_ES_EPES параметры отметки времени игнорируются. |
RuntimeError mentioning (401) Unauthorized | TSA требует учетные данные, либо указанные учетные данные неверны | Передайте имя пользователя и пароль, выданные вашим поставщиком TSA. |
RuntimeError mentioning a refused connection or a proxy error | URL TSA неверен, либо брандмауэр или прокси блокируют запрос | Проверьте URL и убедитесь, что машина, на которой выполняется ваш скрипт, может достичь TSA. |
RuntimeError mentioning The operation has timed out | TSA не ответил в течение установленного тайм‑аута | Повторите попытку или передайте более длительный timeout в DigitalSignatureTimestampSettings. |
| После ошибки остаётся пустой файл вывода | sign создаёт файл назначения до того, как запрос к TSA завершится ошибкой | Удалите файл назначения перед повторной попыткой или запишите в временный путь и переименуйте его после успешного вызова. |
RuntimeError saying timestamping is not supported by this file format | Входной файл имеет формат ODT | Отмечайте время в файлах DOCX или DOC, либо преобразуйте в PDF и используйте описанный ниже путь подписи PDF. |
No usable version of libssl was found, or a crash about a missing ICU package, on Linux | Встроенная в Python‑пакет среда .NET требует OpenSSL 1.1 и поддерживаемую версию ICU | Установите OpenSSL 1.1 или поддерживаемый ICU. Если вашему приложению не требуется форматирование, зависящее от культуры, задайте DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 для устранения ошибки ICU. |
Альтернатива: Добавление метки времени к подписи в PDF‑выводе
Если вашим получателям нужен PDF, а не подписанный файл Word, вам не нужен DigitalSignatureUtil. Подпишите и добавьте метку времени к PDF при сохранении, установив PdfSaveOptions.digital_signature_details.timestamp_settings в объект PdfDigitalSignatureTimestampSettings. Этот способ доступен уже гораздо дольше, чем 26.9. Ссылка PdfDigitalSignatureTimestampSettings reference содержит полный пример.
Следующие шаги
Чтобы добавить строки подписи, подписать с помощью изображения строки подписи или удалить существующие подписи, см. Работа с цифровыми подписями в документации Aspose.Words for Python.
FAQs
Что добавляет доверенный временной штамп к цифровой подписи?
Временная метка (TSA) подтверждает время создания подписи. Это время предоставляется независимой третьей стороной, а не системными часами подписанта, и позволяет проверяющему доказать, что подпись существовала до истечения срока действия или отзыва сертификата подписи.Какая версия Aspose.Words поддерживает временную метку в DigitalSignatureUtil?
Версия 26.9 Aspose.Words for Python via .NET добавилаSignOptions.timestamp_settings,XmlDsigLevel.X_AD_ES_Tи классDigitalSignatureTimestampSettings. Более ранние версии могут ставить временную метку только в подписи PDF‑файлов.Нужно ли устанавливать и xml_dsig_level, и timestamp_settings?
Да. Если установить только один из параметров, Aspose.Words всё равно подпишет документ, но не запросит и не внедрит временную метку, и ошибка не будет сгенерирована.Какие форматы файлов можно подписать временной меткой?
Файлы DOCX и DOC, подписанные сX_AD_ES_T, получают временную метку. Попытка подписать файл ODT с временной меткой приводит к ошибке, указывающей, что данный формат не поддерживает временные метки.Подтверждает ли is_valid, что временная метка действительна?
Нет.DigitalSignature.is_validпроверяет только подпись. Чтобы подтвердить временную метку, необходимо убедиться, что XML подписи содержит токен временной метки, и проанализировать этот токен с помощью инструмента, например OpenSSL.Что происходит, если TSA недоступен?
DigitalSignatureUtil.signгенерируетRuntimeError, описывающий проблему сети, аутентификации или тайм‑аута. Путь назначения может остаться пустым файлом, поэтому его следует удалить перед повторной попыткой.
