Техническа статия

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

За да направите така, че подписан PDF да се валидира и след години, след като сертификатът за подписване е изтекъл и неговият сертифициращ орган (CA) е променен, компонентът PDFium може да генерира дългосрочни подписи PAdES под Windows: времеви отпечатък по RFC 3161 за доверено време, хранилище за сигурност на документи (Document Security Store — DSS), което вгражда материалите за валидиране, и архивен DocTimeStamp над целия файл. Тези три допълнения съставляват базовите нива PAdES B-T, B-LT и B-LTA, като компонентът PDFium достига до всички тях с еднократно извикване за подписване. Тази функция е налична само под Windows и се контролира от свойството PadesCryptoAvailable

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

Защо валидният PDF подпис спира да се верифицира с течение на времето?

Базовият подпис отговаря само на един въпрос: променени ли са тези байтове след подписването. Той сам по себе си не доказва кога се е случило подписването, и тази пролука е причината той да спре да работи по-късно. CMS структурата съдържа атрибут за време на подписване, но подписващият попълва тази стойност от собствения си часовник, така че валидаторът няма основание да ѝ се довери. Когато сертификатът изтече или сертифициращият орган го оттегли, стриктният валидатор вече не може да различи подпис, направен докато сертификатът е бил валиден, от такъв, фалшифициран след неговото изтичане. Подписът е бил напълно изправен в деня на създаването му, но става некоректен без вина на самия потребител

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

Какво е LTV или архивен времеви отпечатък в PDF подпис?

PAdES дефинира четири базови нива, които се надграждат взаимно, а компонентът PDFium ги излага чрез изброяването TPadesLevel (plB_B, plB_T, plB_LT, plB_LTA). B-B е чистият подпис. B-T добавя доверен времеви отпечатък над стойността на подписа. B-LT (дългосрочен) добавя вградените материали за валидиране — характеристиката, която повечето хора имат предвид под „LTV“. B-LTA (дългосрочен с архивен времеви отпечатък) обвива всичко в още един времеви отпечатък, покриващ целия файл, така че самите вградени материали са доказуемо непроменени от момента на архивиране

Практическото разбиране представлява стълбица. B-T доказва кога. B-LT доказва с какво подписът все още може да бъде проверен. B-LTA доказва, че целият пакет не е бил подправян, откакто е запечатан, и това е нивото, което подновявате, като добавяте нов DocTimeStamp преди предишният да изтече, за да удължите архива неограничено. За договор или запис за съответствие с дългосрочно задължение за съхранение, целта е B-LTA; B-LT е прагматичният минимум

Добавяне на доверено време: PAdES B-T с RFC 3161

Компонентът PDFium превръща подписа B-B в B-T чрез задаване на две полета в TPadesSignOptions: променяте Level на plB_T и въвеждате TsaUrl. Когато и двете са налице, процесът на подписване хешира суровите октети на подписа с SHA-256, пакетира този дайджест като RFC 3161 messageImprint, изпраща POST заявка към TSA по WinHttp и вгражда върнатия токен като нелицензиран атрибут за времеви отпечатък (OID 1.2.840.113549.1.9.16.2.14) в SignerInfo unsignedAttrs [1] съгласно 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 е оставен на 0: компонентът извежда уникална стойност срещу повторно изпращане.

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

Свойството CertificateThumbprint е SHA-1 хеш (в шестнадесетичен формат) на сертификат от хранилището „MY“ на текущия потребител, чийто частен ключ можете да използвате. Полето Nonce е RFC 3161 стойност срещу повторно изпращане (anti-replay); оставете го нула и компонентът ще изведе уникална стойност за всяка заявка на базата на SHA-256 сгъване на брояч, часовник, брой тикове и ID на процеса, така че две заявки, изпратени в една и съща милисекунда, да не се сблъскат. Задайте собствена стойност само ако разполагате с криптографски генератор на случайни числа, на който имате доверие. Важно предупреждение: нивото B-T изисква достъпен TSA сървър, така че подписването вече извършва мрежова заявка и зависи от достъпността и латентността на този сървър

Вграждане на материали за валидиране: B-LT и B-LTA с едно извикване

Преминаването към дългосрочна валидация е една единствена промяна: задайте Level на plB_LTA и запазете TsaUrl. Тогава компонентът PDFium изпълнява цялата стълбица в рамките на едно извикване SignPades. Той подписва B-B, поставя времеви отпечатък за B-T, инжектира Document Security Store за 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;   // управлява процеса B-B -> B-T -> B-LT -> B-LTA
    Options.TsaUrl   := 'http://timestamp.example-tsa.com/tsr';
    Options.Location := 'Head Office';

    // Едно извикване записва времевия отпечатък, DSS и архивния DocTimeStamp.
    Pdf.SignPades('contract-lta.pdf', Options);
  finally
    Pdf.Free;
  end;
end;

За етапа B-LT компонентът PDFium изгражда веригата от сертификати с CertGetCertificateChain and записва речника /DSS, съдържащ сертификата на подписалия плюс всеки междинен сертифициращ орган (CA) в масив /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 излага инжектирането на 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 за B-LT, 2 за B-LTA
    Cert.DerBytes := LoadSignerCertDer;       // вашият DER-кодиран сертификат
    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 вгражда цяла верига от сертификати, а архивният DocTimeStamp резервира място за своя токен, така че файлът B-LTA е значително по-голям от оригиналния B-B. Това е съзнателен компромис: жертвате байтове сега, за да осигурите възможност за валидиране по-късно. За повечето архиви на договори и съответствия това е правилната сделка, но си струва да я измерите върху вашите собствени документи. Когато трябва да потвърдите, че готов файл наистина е достигнал желаното ниво, го проверете с техниките от статията за инспектиране на цифрови PDF подписи и нива на PAdES, а ако подобрявате сигурността на документите като цяло, бележките за одит на рисковете за сигурността на PDF покриват съпътстващите проверки

Функциите за PAdES подписване, DSS и архивен времеви отпечатък, показани тук, са част от PDFium Component за Delphi и C++Builder, чиято продуктова страница съдържа пълното описание за подписване и системните изисквания