Technical Article

Долгосрочные подписи PDF в Delphi: PAdES B-LT и B-LTA

Чтобы подписанный файл PDF оставался валидным спустя годы, после истечения срока действия сертификата подписанта и смены корневых сертификатов УЦ, компонент PDFium Component может создавать долгосрочные подписи PAdES в среде Windows: добавлять штамп времени RFC 3161 для фиксации времени подписания, внедрять контейнер данных проверки Document Security Store (DSS) и накладывать архивный штамп DocTimeStamp на весь файл. Эти шаги соответствуют базовым уровням PAdES B-T, B-LT и B-LTA, и PDFium Component позволяет достичь их в рамках единого вызова. Данная функция поддерживается только в Windows и доступна при проверке PadesCryptoAvailable

Это третья статья цикла. Первая, подписание PDF-файлов с уровнем PAdES B-B в PDFium VCL, описывает базовое подписание; вторая, анализ цифровых подписей PDF и уровней PAdES, посвящена чтению и проверке структуры подписей. В этой статье мы подробно разберем вопросы долгосрочного архивирования: создание подписей, которые должны оставаться валидными на протяжении многих лет, а не недель

Почему корректная подпись PDF со временем перестает проверяться?

Базовая подпись отвечает только на один вопрос: изменялись ли байты документа после подписания. Сама по себе она не доказывает точное время совершения операции, и именно это приводит к ошибкам проверки в будущем. В структуре CMS содержится атрибут времени подписания, но подписант записывает это значение на основе системных часов своего компьютера, поэтому валидатор не может полностью ему доверять. Когда срок действия сертификата истекает или УЦ его отзывает, строгий валидатор больше не может отличить подпись, сделанную во время действия сертификата, от подписи, созданной с помощью просроченного ключа. Корректная на момент создания подпись перестает проверяться не по вине разработчика

Долгосрочное архивирование решает эту проблему двумя способами. Во-первых, доверенная служба штампов времени (TSA) подтверждает точное время криптографическими средствами, исключая зависимость от часов подписанта. Во-вторых, все сертификаты, статусы OCSP и списки отзывов CRL, необходимые для проверки цепочки доверия, внедряются непосредственно внутрь самого файла PDF, поэтому проверка не зависит от доступности серверов в будущем. Стандарт PAdES (ETSI EN 319 142-1) определяет эти требования как базовые уровни, и PDFium Component формирует все необходимые для них структуры

Что такое подписи PDF со структурами LTV или архивными штампами?

PAdES определяет четыре базовых уровня, которые строятся последовательно и представлены в PDFium Component перечислением TPadesLevel (plB_B, plB_T, plB_LT, plB_LTA). Уровень B-B — простая подпись. Уровень B-T добавляет доверенный штамп времени поверх значения подписи. Уровень B-LT (long-term) внедряет встроенные данные проверки сертификатов — это именно то, что обычно называют аббревиатурой LTV. Уровень B-LTA (long-term with archive timestamp) накладывает дополнительный архивный штамп времени на весь файл целиком, гарантируя неизменность всех данных проверки с момента архивирования

На практике это выглядит как лестница. Уровень B-T доказывает когда. Уровень B-LT гарантирует с помощью чего подпись может быть проверена в будущем. Уровень B-LTA защищает весь контейнер от изменений с момента фиксации. Именно этот уровень продлевают в будущем, накладывая новый штамп DocTimeStamp до истечения срока действия предыдущего, продлевая архив на неопределенный срок. Для договоров и отчетных документов с длительными сроками хранения уровень B-LTA является целевым, а B-LT — минимально необходимым порогом требований

Добавление доверенного времени: PAdES B-T и RFC 3161

Компонент PDFium Component преобразует подпись B-B в уровень B-T при настройке двух параметров в структуре TPadesSignOptions: установите Level в значение plB_T и укажите URL службы в свойстве TsaUrl. При наличии этих настроек библиотека хэширует байты подписи алгоритмом SHA-256, формирует запрос messageImprint по спецификации RFC 3161, отправляет его на сервер TSA по протоколу POST через WinHttp и внедряет полученный токен в качестве неподписанного атрибута signature-time-stamp (OID 1.2.840.113549.1.9.16.2.14) в структуру SignerInfo unsignedAttrs в соответствии с EN 319 142-1 §6.3. Штамп времени покрывает именно значение подписи, что жестко связывает доверенное время с этой конкретной операцией подписания

uses
  PDFium, FPdfPades;

procedure SignWithTimestamp;
var
  Pdf: TPdf;
  Options: TPadesSignOptions;
begin
  Pdf := TPdf.Create(nil);
  try
    if not Pdf.PadesCryptoAvailable then
      raise Exception.Create('PAdES signing backend is Windows-only');
    Pdf.FileName := 'contract.pdf';
    Pdf.Active := True;

    Options := TPadesSignOptions.Default;
    Options.CertificateThumbprint := 'A1B2C3D4E5F6071829304152637485960A1B2C3D';
    Options.Level  := plB_T;
    Options.TsaUrl := 'http://timestamp.example-tsa.com/tsr';
    Options.Reason := 'Contract execution';
    // Nonce left at 0: the component derives a unique anti-replay value.

    Pdf.SignPades('contract-bt.pdf', Options);
  finally
    Pdf.Free;
  end;
end;

Параметр CertificateThumbprint принимает SHA-1 хэш сертификата в шестнадцатеричном виде из хранилища текущего пользователя Windows "MY", закрытый ключ которого будет использоваться. Свойство Nonce задает защитное значение RFC 3161; оставьте его равным нулю, и компонент автоматически сформирует уникальное значение на основе SHA-256 хэша счетчика, времени, системного таймера и идентификатора процесса, чтобы два запроса в одну миллисекунду не совпали. Указывайте свое значение только при использовании проверенного криптографического генератора случайных чисел. Важное условие: уровень B-T требует сетевой доступности службы TSA, поэтому операция подписания будет выполнять внешние сетевые вызовы и зависеть от их стабильности и задержек

Внедрение данных проверки: B-LT и B-LTA в один вызов

Переход на уровень долгосрочной проверки выполняется простым изменением: установите свойство Level в значение plB_LTA при указанном TsaUrl. Компонент PDFium Component выполнит всю цепочку преобразований в рамках одного вызова метода SignPades. Он создаст базовую подпись B-B, запросит штамп времени для B-T, внедрит структуры DSS для B-LT и наложит архивный штамп для B-LTA. Каждое изменение записывается в виде инкрементного сохранения, сохраняя исходные байты нетронутыми

procedure SignForArchive;
var
  Pdf: TPdf;
  Options: TPadesSignOptions;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := 'contract.pdf';
    Pdf.Active := True;

    Options := TPadesSignOptions.Default;
    Options.CertificateThumbprint := 'A1B2C3D4E5F6071829304152637485960A1B2C3D';
    Options.Level    := plB_LTA;   // drives B-B -> B-T -> B-LT -> B-LTA
    Options.TsaUrl   := 'http://timestamp.example-tsa.com/tsr';
    Options.Location := 'Head Office';

    // One call writes the timestamp, the DSS and the archive DocTimeStamp.
    Pdf.SignPades('contract-lta.pdf', Options);
  finally
    Pdf.Free;
  end;
end;

Для реализации уровня B-LT компонент строит цепочку сертификатов с помощью системного метода CertGetCertificateChain и записывает словарь /DSS, содержащий сертификат подписанта и все промежуточные сертификаты УЦ в массиве /Certs (наряду с массивами /OCSPs и /CRLs для статусов отзывов), а также маркер /Extensions /ESIC Level 1 в точном соответствии с требованиями EN 319 142-1 §5.4.2. Корневой самоподписанный сертификат намеренно исключается, так как стандарт RFC 5652 §10.2.3 допускает это, а сам корень уже должен храниться у проверяющего в качестве доверенного якоря. В результате получается подпись, которую можно полностью проверить автономно

Для уровня B-LTA компонент добавляет штамп времени документа (Document Time-stamp): словарь подписи со значением /SubFilter /ETSI.RFC3161, диапазон /ByteRange которого покрывает весь файл, а параметр /Contents хранит токен штампа времени RFC 3161 для этого диапазона, повышая уровень маркера ESIC до Level 2. Это соответствует шагу архивирования по §5.4.3 и TS 119 142-3. Поскольку штамп DocTimeStamp покрывает как структуры DSS, так и саму подпись, он доказывает наличие и неизменность встроенных данных проверки на момент архивирования, что и требуется правилами долгосрочного хранения

Дополнение существующей подписи данными проверки

Иногда подпись уже присутствует в документе и вам нужно лишь добавить или обновить данные проверки (например, повысить стороннюю подпись B-B до уровня B-LT для архива). Компонент PDFium Component предоставляет прямой доступ к генератору DSS через метод SaveAsPadesDss, записывающий структуры /DSS и /Extensions в виде инкрементного сохранения без изменения байтов существующей подписи

procedure AddValidationStore;
var
  Pdf: TPdf;
  DssOpts: TPadesDssOptions;
  Cert: TPadesDssMaterial;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := 'already-signed.pdf';
    Pdf.Active := True;

    DssOpts := TPadesDssOptions.Default;
    DssOpts.EsicExtensionLevel := 1;         // 1 for B-LT, 2 for B-LTA
    Cert.DerBytes := LoadSignerCertDer;       // your DER-encoded certificate
    SetLength(DssOpts.Certs, 1);
    DssOpts.Certs[0] := Cert;

    Pdf.SaveAsPadesDss('already-signed-lt.pdf', DssOpts);
  finally
    Pdf.Free;
  end;
end;

Вы передаете сертификаты в кодировке DER, а также необходимые для проверки ответы OCSP и списки CRL. Установите параметр EsicExtensionLevel в значение 1 для уровня B-LT или в 2, если далее будет накладываться штамп DocTimeStamp. Оставьте свойство IncludeVri в значении False (по умолчанию): спецификация EN 319 142-1 §5.4.2.3 считает словари /VRI для отдельных подписей необязательными и не рекомендует их использовать, если этого не требуют конкретные проверяющие системы

Нюансы и ограничения

Три ограничения определяют область использования данных уровней подписи. Во-первых, криптографический бэкэнд работает только под Windows: создание подписей и запросы штампов времени используют системные библиотеки CNG и WinHttp. Поэтому на других ОС свойство PadesCryptoAvailable вернет False, а вызовы SignPades приведут к ошибке. Проверка и анализ структуры подписей остаются кросс-платформенными; создание подписей привязано к Windows. Во-вторых, уровни B-T и B-LTA зависят от серверов службы штампов времени (TSA), что добавляет сетевую активность при сохранении файла, а доступность и задержки серверов становятся частью процесса подписания

В-третьих, каждый уровень увеличивает объем файла. Так как каждый шаг записывается в виде инкрементного сохранения, контейнер DSS внедряет цепочку сертификатов, а архивный штамп резервирует место под токен, итоговый файл уровня B-LTA будет значительно больше исходного B-B файла. Это осознанный компромисс: вы тратите байты сейчас, чтобы гарантировать проверку документа в будущем. Для большинства контрактов и отчетов это оправданно, но объем стоит измерять на своих документах. Убедиться в достижении файлом нужного уровня можно методами из статьи об анализе подписей PDF и уровней PAdES, а при комплексном контроле документооборота полезно использовать проверки из материала по аудиту угроз безопасности PDF

Подписание PAdES, работа с DSS и архивными штампами времени входят в состав компонента PDFium Component для Delphi и C++Builder, страница продукта содержит подробные описания API и системных требований