Электронная подпись фиксирует, кто подписал документ. Доверенный временной штамп фиксирует когда, и это время берётся от независимого органа временных меток (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, подписанный своим собственным сертификатом, который связывает хеш с конкретным временем. Этот токен сохраняется внутри подписи. Затем проверяющий может показать, что подпись существовала в тот момент, что особенно важно, когда сертификат подписи позже истекает или отзывается.

Требования

Прежде чем запустить пример, убедитесь, что у вас есть:

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) UnauthorizedTSA требует учетные данные, либо указанные учетные данные неверныПередайте имя пользователя и пароль, выданные вашим поставщиком TSA.
RuntimeError mentioning a refused connection or a proxy errorURL TSA неверен, либо брандмауэр или прокси блокируют запросПроверьте URL и убедитесь, что машина, на которой выполняется ваш скрипт, может достичь TSA.
RuntimeError mentioning The operation has timed outTSA не ответил в течение установленного тайм‑аутаПовторите попытку или передайте более длительный 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

  1. Что добавляет доверенный временной штамп к цифровой подписи?
    Временная метка (TSA) подтверждает время создания подписи. Это время предоставляется независимой третьей стороной, а не системными часами подписанта, и позволяет проверяющему доказать, что подпись существовала до истечения срока действия или отзыва сертификата подписи.

  2. Какая версия Aspose.Words поддерживает временную метку в DigitalSignatureUtil?
    Версия 26.9 Aspose.Words for Python via .NET добавила SignOptions.timestamp_settings, XmlDsigLevel.X_AD_ES_T и класс DigitalSignatureTimestampSettings. Более ранние версии могут ставить временную метку только в подписи PDF‑файлов.

  3. Нужно ли устанавливать и xml_dsig_level, и timestamp_settings?
    Да. Если установить только один из параметров, Aspose.Words всё равно подпишет документ, но не запросит и не внедрит временную метку, и ошибка не будет сгенерирована.

  4. Какие форматы файлов можно подписать временной меткой?
    Файлы DOCX и DOC, подписанные с X_AD_ES_T, получают временную метку. Попытка подписать файл ODT с временной меткой приводит к ошибке, указывающей, что данный формат не поддерживает временные метки.

  5. Подтверждает ли is_valid, что временная метка действительна?
    Нет. DigitalSignature.is_valid проверяет только подпись. Чтобы подтвердить временную метку, необходимо убедиться, что XML подписи содержит токен временной метки, и проанализировать этот токен с помощью инструмента, например OpenSSL.

  6. Что происходит, если TSA недоступен?
    DigitalSignatureUtil.sign генерирует RuntimeError, описывающий проблему сети, аутентификации или тайм‑аута. Путь назначения может остаться пустым файлом, поэтому его следует удалить перед повторной попыткой.

Получите бесплатную лицензию и поддержку