Artykuł techniczny

Długoterminowe podpisy PDF w Delphi: PAdES B-LT i B-LTA

Aby podpisany plik PDF mógł zostać zweryfikowany za wiele lat, już po wygaśnięciu certyfikatu podpisującego i zmianie jego urzędu certyfikacji, komponent PDFium może wygenerować długoterminowe podpisy PAdES w systemie Windows: znacznik czasu RFC 3161 dla wiarygodnego czasu, Document Security Store (magazyn bezpieczeństwa dokumentu) osadzający materiał walidacyjny oraz archiwalny znacznik DocTimeStamp obejmujący cały plik. Te trzy dodatki reprezentują poziomy podstawowe PAdES B-T, B-LT i B-LTA, a komponent PDFium osiąga je wszystkie za pomocą jednego wywołania podpisującego. Jest to funkcja dostępna wyłącznie w systemie Windows, uwarunkowana właściwością PadesCryptoAvailable

Ten artykuł jest trzecim z serii. Pierwszy, o podpisywaniu plików PDF w standardzie PAdES B-B przy użyciu PDFium VCL, omawia podpis podstawowy; drugi, o inspekcji podpisów cyfrowych PDF i poziomów PAdES, dotyczy odczytywania podpisu z powrotem i określania, jaki poziom osiągnął. Tutaj zajmujemy się archiwum: podpisami, które muszą przetrwać wygaśnięcie certyfikatu i zachować ważność przez okres przechowywania liczony w latach, a nie tygodniach

Dlaczego prawidłowy podpis PDF z czasem przestaje być ważny?

Podstawowy podpis odpowiada tylko na jedno pytanie: czy bajty zostały zmienione po podpisaniu. Sam w sobie nie udowadnia, kiedy nastąpiło podpisanie, i ta luka jest tym, co później go unieważnia. Struktura CMS przenosi atrybut czasu podpisania, ale podpisujący wpisuje tę wartość na podstawie własnego zegara, więc walidator nie ma powodu, by jej ufać. Gdy certyfikat podpisującego później wygaśnie lub urząd certyfikacji go odwoła, rygorystyczny walidator nie może już odróżnić podpisu złożonego w okresie ważności certyfikatu od podpisu sfałszowanego po jego wygaśniu. Podpis był poprawny w dniu jego złożenia, a staje się nieweryfikowalny nie z własnej winy

Długoterminowa archiwizacja rozwiązuje ten problem w dwóch krokach. Po pierwsze, zaufana strona trzecia (urząd ds. znaczników czasu) poświadcza czas kryptograficznie, dzięki czemu nie zależy on już od uczciwości podpisującego. Po drugie, każdy certyfikat, odpowiedź o odwołaniu oraz lista CRL potrzebne do zbudowania i sprawdzenia łańcucha zaufania są osadzane w samym pliku PDF, więc walidacja nie zależy od serwerów, które mogą już nie istnieć, gdy ktoś będzie próbował ją przeprowadzić. Standard PAdES, ujednolicony jako ETSI EN 319 142-1, nakłada je jako poziomy podstawowe, a komponent PDFium zapisuje struktury wymagane przez każdy z tych poziomów

Co jest podpis PDF z LTV lub archiwalnym znacznikiem czasu?

PAdES definiuje cztery poziomy podstawowe, które budują się na sobie nawzajem, a komponent PDFium udostępnia je poprzez wyliczenie TPadesLevel (plB_B, plB_T, plB_LT, plB_LTA). Poziom B-B to zwykły podpis. Poziom B-T dodaje zaufany znacznik czasu nad wartością podpisu. B-LT (long-term) dodaje osadzone materiały walidacyjne, czyli właściwość, którą większość ludzi nazywa „LTV”. Z kolei B-LTA (long-term z archiwalnym znacznikiem czasu) otacza wszystko jeszcze jednym znacznikiem czasu obejmującym cały plik, dzięki czemu same osadzone materiały są w sposób udowodniony nienaruszone od momentu archiwizacji

W ujęciu praktycznym są to schody. Poziom B-T udowadnia, kiedy. B-LT udowadnia, za pomocą czego podpis może nadal zostać zweryfikowany. B-LTA udowadnia, że cały pakiet nie został naruszony od momentu jego zapieczętowania, i jest to poziom, który odnawiasz, dodając świeży znacznik DocTimeStamp przed wygaśnięciem poprzedniego, aby przedłużać ważność archiwum w nieskończoność. W przypadku umowy lub rejestru zgodności z długim obowiązkiem przechowywania, celem jest poziom B-LTA; B-LT to pragmatyczne minimum

Dodawanie wiarygodnego czasu: PAdES B-T z RFC 3161

Komponent PDFium zamienia podpis B-B w B-T poprzez ustawienie dwóch pól w TPadesSignOptions: podniesienie wartości Level do plB_T i podanie TsaUrl. Gdy oba te parametry są obecne, potok podpisujący haszuje surowe oktety podpisu algorytmem SHA-256, pakuje ten skrót jako messageImprint RFC 3161, wysyła żądanie POST do urzędu TSA za pośrednictwem WinHttp i osadza zwrócony token jako niepodpisany atrybut znacznika czasu podpisu (OID 1.2.840.113549.1.9.16.2.14) w strukturze unsignedAttrs [1] sekcji SignerInfo, zgodnie z normą EN 319 142-1 §6.3. Znacznik czasu obejmuje konkretnie wartość podpisu, co wiąże wiarygodny czas z tym konkretnym podpisem

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;

Właściwość CertificateThumbprint to skrót SHA-1 (w postaci szesnastkowej) certyfikatu z magazynu „MY” bieżącego użytkownika, którego klucza prywatnego możesz użyć. Pole Nonce to wartość zapobiegająca powtórzeniom RFC 3161; pozostaw je jako zero, a komponent wygeneruje unikalną wartość dla każdego żądania ze skrótu SHA-256 licznika, zegara, liczby taktów i identyfikatora procesu, dzięki czemu dwa żądania wydane w tej samej milisekundzie nie zderzą się ze sobą. Przekaż własną wartość tylko wtedy, gdy masz kryptograficzny generator liczb losowych, któremu bardziej ufasz. Jedno szczere zastrzeżenie: poziom B-T wymaga dostępnego serwera TSA, więc podpisanie powoduje teraz wywołanie sieciowe i zależy od dostępności oraz opóźnień TSA

Osadzanie materiałów walidacyjnych: B-LT i B-LTA w jednym wywołaniu

Krok w kierunku długoterminowej walidacji to pojedyncza zmiana: ustaw właściwość Level na plB_LTA i zachowaj TsaUrl. Komponent PDFium uruchamia wówczas pełne schody w ramach jednego wywołania SignPades. Podpisuje w standardzie B-B, nakłada znacznik czasu dla B-T, wstrzykuje Document Security Store dla B-LT i dołącza archiwalny znacznik czasu dla B-LTA, każdy jako własną aktualizację przyrostową, dzięki czemu wcześniejsze bajty pozostają nienaruszone bajt po bajcie

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;

Dla etapu B-LT komponent PDFium buduje łańcuch certyfikatów za pomocą funkcji CertGetCertificateChain and writes a /DSS dictionary carrying the signer certificate plus every intermediate CA in a /Certs array (with parallel /OCSPs and /CRLs arrays), alongside an /Extensions /ESIC marker at Level 1, exactly as EN 319 142-1 §5.4.2 prescribes. Samopodpisany certyfikat główny jest celowo pomijany, ponieważ sekcja §10.2.3 normy RFC 5652 na to zezwala, a certyfikat główny jest już kotwicą zaufania, którą posiada walidator. Wynikiem jest podpis, który czytnik może zweryfikować, mając w ręku wyłącznie plik

Dla poziomu B-LTA komponent dołącza znacznik czasu dokumentu (Document Time-stamp): słownik podpisu z wartością /SubFilter /ETSI.RFC3161, którego zakres /ByteRange obejmuje cały plik, a jego wpis /Contents przechowuje token RFC 3161 na tym zakresie, podnosząc jednocześnie znacznik ESIC do poziomu 2. Jest to etap archiwizacji opisany w sekcji §5.4.3 i normie TS 119 142-3. Ponieważ znacznik DocTimeStamp obejmuje słownik DSS, jak również sam podpis, udowadnia on, że osadzone materiały walidacyjne były obecne i nienaruszone w momencie archiwizacji, co jest dokładnie tym, co musi wykazać polityka długiego przechowywania dokumentów

Rozszerzanie podpisu, który już istnieje

Czasami podpis znajduje się już w dokumencie i wystarczy jedynie dodać lub odświeżyć magazyn walidacyjny, na przykład w celu podniesienia zewnętrznego podpisu B-B do poziomu B-LT na potrzeby swojego archiwum. Komponent PDFium udostępnia wtryskiwacz DSS bezpośrednio za pomocą metody SaveAsPadesDss, która dołącza struktury /DSS oraz /Extensions jako aktualizację przyrostową bez naruszania bajtów istniejącego podpisu

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;

Dostarczasz certyfikaty zakodowane w formacie DER oraz opcjonalnie odpowiedzi OCSP i listy CRL, których będzie potrzebował walidator. Ustaw właściwość EsicExtensionLevel na 1 dla magazynu B-LT lub na 2, gdy po nim nastąpi znacznik DocTimeStamp. Pozostaw IncludeVri przy domyślnej wartości False: sekcja §5.4.2.3 normy EN 319 142-1 traktuje słownik /VRI dla poszczególnych podpisów jako opcjonalny i odradza jego stosowanie, chyba że wymaga tego konkretny czytnik docelowy

Koszty i granice, które warto zaplanować

Trzy szczere ograniczenia kształtują zastosowanie tych poziomów. Po pierwsze, backend kryptograficzny jest dostępny wyłącznie w systemie Windows: podpisanie i nakładanie znacznika czasu odbywa się za pośrednictwem systemowego CNG i biblioteki WinHttp, więc właściwość PadesCryptoAvailable zwraca wartość False na innych platformach, a wywołanie SignPades zgłasza błąd. Opisana w powiązanym artykule inspekcja po stronie odczytu pozostaje wieloplatformowa; jedynie zapisywanie podpisów jest powiązane z systemem Windows. Po second, poziomy B-T i B-LTA zależą od urzędu ds. znaczników czasu, co oznacza wywołanie sieciowe w momencie podpisywania oraz zależność od usługi zewnętrznej, której czas sprawności i limity stają się częścią Twojej ścieżki podpisywania. Po trzecie, każdy poziom niesie za sobą koszt pamięciowy: każdy etap dołącza aktualizację przyrostową zamiast nadpisywać plik, słownik DSS osadza pełny łańcuch certyfikatów, a archiwalny znacznik DocTimeStamp rezerwuje miejsce na swój token, więc plik B-LTA jest znacznie większy niż oryginał B-B. To celowy kompromis: zużywasz bajty teraz, by kupić weryfikowalność później. W przypadku większości archiwów umów i zgodności jest to właściwy wybór, ale warto zmierzyć te parametry na własnych dokumentach, zamiast przyjmować je w ciemno. Gdy musisz potwierdzić, czy gotowy plik rzeczywiście osiągnął zamierzony poziom, zweryfikuj go technikami opisanymi w artykule na temat inspekcji podpisów cyfrowych PDF i poziomów PAdES, a jeśli zabezpieczasz potok dokumentów w szerszym zakresie, powiązane testy omawia artykuł o audycie ryzyk bezpieczeństwa PDF

Pokazane tutaj funkcje podpisywania PAdES, DSS i archiwalnego znacznika czasu są częścią komponentu PDFium dla Delphi i C++Builder, którego strona produktu zawiera pełną dokumentację podpisywania oraz wymagania systemowe