Świeżo podpisany PDF to podpis B-B i nic więcej. Dowodzi, kto podpisał i że bajty się nie ruszyły, ale nie niesie dowodu, że certyfikat podpisującego był ważny w chwili podpisu, więc walidator za lata musi szukać danych o unieważnieniach, które mogą już nie istnieć. Zasypanie tej luki znaczy zapisanie odpowiedzi OCSP i list CRL do poziomu dokumentu, w Document Security Store, a w HotPDF to jedno wywołanie: PopulatePAdESLTVEvidence przechodzi przez każdy wczytany podpis, wyprowadza żądania unieważnień ze zbioru certyfikatów, wykonuje je przez transport, który dostarczysz, i zapisuje pobrany materiał plus łańcuch CMS do DSS. Zwraca liczbę podpisów, których dowody wylądowały, albo minus jeden, gdy dokument w ogóle nie ma pola podpisu
Decyzją projektową wartą zrozumienia przed użyciem jest to, że biblioteka nigdy nie otwiera gniazda. Każdy bajt przybywający z sieci przybywa przez callback, który napiszesz. To nie ostrożność sama w sobie; to jedyny sposób, by ta funkcja działała wewnątrz środowisk, które faktycznie wymagają walidacji długoterminowej
Dlaczego biblioteka odmawia własnego HTTP?
Bo miejsca wymagające podpisów B-LT to miejsca, gdzie biblioteki nie wolno ufać z siecią. Usługi podpisywania działają za proxy z uwierzytelnianiem i korporacyjnymi rootami. Odcięte od sieci poziomy podpisywania nie mają trasy do respondenta i trzeba je karmić zbuforowanymi dowodami. Reżimy audytowe wymagają, by każde wychodzące żądanie było logowane przez aplikację, nie zakopane w zależności. I zestawy testowe potrzebują deterministycznych odpowiedzi, co jest niemożliwe, gdy biblioteka sama się wybiera w świat
Transport to zwykła referencja funkcji o stałym kształcie, więc polityka pozostaje twoja. HotPDF wręcza ci rekord żądania opisujący dokładnie, co pobrać, łącznie z typem zawartości i limitem rozmiaru odpowiedzi, a ty zwracasz bajty plus status
function FetchEvidence(const Request: THPDFSignatureEvidenceRequest;
Attempt: Integer; CancellationToken: THPDFCancellationToken;
out Response: TBytes; out RetryAfterMS: Cardinal;
out ErrorMessage: UnicodeString): THPDFSignatureEvidenceTransportStatus;
begin
RetryAfterMS := 0;
try
// Request.Kind mówi, czy to POST OCSP czy GET CRL;
// Request.ContentType i Request.Body są już przygotowane,
// a Request.MaxResponseBytes to limit, który musisz uszanować
Response := HttpExchange(Request.URI, Request.ContentType,
Request.Body, Request.MaxResponseBytes);
Result := setsSucceeded;
except
on E: Exception do
begin
ErrorMessage := E.Message;
// setsRetry pozwala polityce ponowień się wycofać; użyj
// setsPermanentFailure dla 404 albo złego URL-a
Result := setsRetry;
end;
end;
end;
// Aktualizacja B-B do B-LT jednym wywołaniem dla każdego podpisu
// w wczytanym pliku
var
Pdf: THotPDF;
Upgraded: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.BeginIncrementalUpdate('signed.pdf');
Upgraded := Pdf.PopulatePAdESLTVEvidence(FetchEvidence,
THPDFSignatureEvidenceRetryPolicy.Default);
if Upgraded > 0 then
// Zapis tylko-dodający: bajty pokrywane przez istniejące podpisy
// są zachowane co do znaku
Pdf.SaveIncrementalUpdate('signed-lt.pdf');
finally
Pdf.Free;
end;
end;
Awarie są per podpis, nie per dokument. Respondent, który przekracza limit czasu dla jednego podpisującego, pomija materiał tego podpisującego i zostawia resztę przebiegu nietkniętą, co jest zachowaniem, jakiego chcesz w batchu: częściowe dowody biją przerwaną próbę, a wartość zwrotna mówi, ile podpisów faktycznie zyskało
Łańcuch, którego CMS zapomniał dołączyć
Sprawdzanie unieważnień potrzebuje certyfikatu wystawcy, a zaskakująco wiele stosów podpisujących pomina pośredników w kontenerze CMS. Drogą naprawczą jest rozszerzenie Authority Information Access, metoda dostępu 1.3.6.1.5.5.7.48.2, która podaje URL, skąd certyfikat wystawcy można pobrać. HPDFFetchAIAIntermediates przechodzi te URL-e przez ten sam transport, wycina DER z każdej odpowiedzi i zwraca tylko te certyfikaty, których CMS już nie niósł, kluczowane haszem DER, żeby duplikaty i pętle nie mogły się kręcić
Dwa szczegóły decydują, czy to działa wobec prawdziwych urzędów certyfikacji. Pierwszy to kodowanie: punkty końcowe CA serwują certyfikat jako goły DER mniej więcej tak często, jak w pancerzu PEM, i nie ma niezawodnego typu zawartości, by je odróżnić. Solidna sonda jest najpierw tekstowa, potem strukturalna. Szukaj znacznika -----BEGIN CERTIFICATE-----, zdejmij pancerz i zdekoduj base64, jeśli jest obecny, a w obu ścieżkach potwierdź, że pierwszy bajt wyniku to $30, tag DER dla SEQUENCE. Drugi to głębokość: pobrany pośrednik może sam podawać URL AIA dla własnego wystawcy, więc przejście dokłada nowych kandydatów do kolejki i domyka łańcuchy krótkie o dwa lub trzy skoki. To trzeba ograniczyć sufitem i do tego służy parametr MaxFetch
Czym jest wartość seed podpisu i czemu zawodzi po cichu?
Wartość seed to ograniczenie, które autor dokumentu doczepia do pola podpisu, by powiedzieć podpisującemu, jaki rodzaj podpisu jest akceptowalny: jaki SubFilter, jaki algorytm skrótu, jakie powody, jaka minimalna wersja PDF, czy informacje o unieważnieniach muszą być osadzone. Mieszka w słowniku /SV na polu i jest zdefiniowana w ISO 32000-1 §12.7.5.5. HotPDF zapisuje ją przez AttachPAdESSeedValue i sprawdza przez CheckLoadedSignatureSeedValue, która zwraca True, gdy pole jest nieograniczone albo każde obecne ograniczenie przechodzi, a przy False nazywa pierwsze zawodzące ograniczenie przez parametr wyjściowy, który możesz wstawić prosto do komunikatu błędu
Mechanizmem, który sprawia, że wartości seed łatwo zepsuć, jest wpis flag /Ff opisany w §12.7.5.5.3. Ustawiony bit oznacza jego ograniczenie jako wymagane: niezgodność jest błędem, a podpisujący musi odmówić. Zerowany bit oznacza to samo ograniczenie jako preferencję: wartość filtruje, co interfejs powinien oferować, i nic więcej. Płyną z tego dwie pułapki. Po pierwsze, /Ff mieszka wewnątrz słownika /SV, nie na adnotacji widgetu, więc kod czytający /Ff poziomu pola dostaje pustą odpowiedź na zawsze i wnioskuje, że nic nie jest wymuszone. Po drugie, przypisania bitów nie są prostym biegiem jeden, dwa, cztery, osiem; w HotPDF writer emituje 2 dla SubFilter, 4 dla MinVersion, 32 dla AddRevInfo i 64 dla DigestMethod. Czytnik zakładający kolejne bity dekoduje każde ograniczenie jako opcjonalne i przechodzi każdy test poza tym jednym, który się liczy
var
Violation: AnsiString;
begin
// Zapytaj pole, czy profil, którym zaraz podpiszemy, jest dozwolony
if not Pdf.CheckLoadedSignatureSeedValue(0, 'ETSI.CAdES.detached',
'SHA256', 'Approved for payment', 1, Violation) then
raise Exception.Create('Signature field rejects this profile: ' +
String(Violation));
// Ograniczenie spełnione: kontynuuj przebieg podpisywania
end;
Test, który wystawił pierwotny błąd dekodowania, nie był testem pozytywnym. Była to asercja, że wymuszona niezgodność musi zostać odrzucona, i to jedyny rodzaj testu, który potrafi złapać tę klasę defektów: dekoder czytający zły słownik albo złe pozycje bitów produkuje „żadne ograniczenie nie naruszone” dla każdego wejścia, co wygląda dokładnie jak poprawne zachowanie, dopóki celowo nie naruszysz jednego
Gdzie to siedzi na drabinie LTV
Cztery szczeble i każdy potrzebuje tego pod sobą. B-B to goły podpis. B-T dodaje zaufany znacznik czasu, który fiksuje czas podpisania, więc walidator wie, względem którego momentu oceniać unieważnienia. B-LT dodaje dowody unieważnień do DSS, co automatyzuje PopulatePAdESLTVEvidence. B-LTA dodaje znaczniki czasu dokumentu odnawiane, zanim poprzedni osłabnie, przedłużając ważność nieograniczenie; HotPDF wystawia to jako RenewPAdESLTATimestamp, który dokleja nowy znacznik jako przyrostową rewizję i zachowuje każdy wcześniejszy podpis, znacznik i wpis DSS nietknięte
Model aktualizacji przyrostowej to jedyny poprawny sposób dodawania dowodów do podpisanego dokumentu, bo przepisanie pliku połamałoby zakresy bajtów pokrywane przez istniejące podpisy. Jeśli musisz rozumować o tym, co się zmieniło między rewizjami i czy te zmiany są rodzaju, który podpis pozwala, ta analiza jest omówiona osobno w analizie rewizji DocMDP i FieldMDP. Sam potok podpisywania, łącznie ze źródłami certyfikatów i pułapkami kolejności bajtów, jest w przewodniku podpisywania PAdES, a strona walidacji w weryfikacji podpisów na wczytanych dokumentach
Jedno praktyczne ostrzeżenie o kolejności. Zbieraj dowody jak najszybciej po podpisaniu, najlepiej w tym samym zadaniu. Respondenci, którzy potrafią odpowiedzieć za certyfikat, są online, dopóki certyfikat jest aktualny, i znikają po latach, więc dokument, który opuszcza twój potok jako B-B, może nigdy nie dać się już podnieść. HotPDF działa jako natywny komponent VCL dla Delphi i C++Builder, a cały przebieg dowodów jest w procesie poza twoim własnym transportem; wspierane profile są wypisane na stronie produktu HotPDF Delphi PDF component