PDF, який ви щойно підписали, — це підпис B-B і нічого більше. Він доводить, хто підписав і що байти не зрушили, але не несе доказу, що сертифікат підписувача був чинний на момент підписування, тож перевіряльник за роки віднині муситиме шукати дані відкликання, яких може вже не існувати. Закриття цієї прогалини означає запис відповідей OCSP та CRL у документне Document Security Store, і в HotPDF це один виклик: PopulatePAdESLTVEvidence проходить кожен завантажений підпис, виводить запити відкликання з множини сертифікатів, виконує їх через транспорт, який ви постачаєте, і записує отриманий матеріал разом із ланцюгом CMS до DSS. Вона повертає кількість підписів, чиї докази осіли, або мінус один, коли документ узагалі не має поля підпису
Рішення проєктування, варте розуміння перед використанням: бібліотека ніколи не відкриває сокет. Кожен байт, що приходить із мережі, приходить через зворотний виклик, який ви написали. Це не обережність заради самої обережності; це єдиний спосіб, яким ця можливість може працювати всередині середовищ, що справді вимагають довгострокової валідації
Чому бібліотека відмовляється робити власний HTTP?
Бо місця, що вимагають підписів B-LT, — це місця, де бібліотеці не можна довіряти мережу. Служби підписування працюють за автентифікаційними проксі з корпоративними коренями. Відрізані від мережі рівні підписування не мають маршруту до респондера і мусять харчуватися кешованими доказами. Режими аудиту вимагають, щоб кожен вихідний запит журналював застосунок, а не ховався в залежності. І тестові набори потребують детермінованих відповідей, що неможливо, якщо бібліотека сама набирає номер
Транспорт — просте посилання на функцію з фіксованою формою, тож політика лишається вашою. HotPDF передає вам запис запиту, що описує точно, що отримати, включно з типом вмісту та стелею розміру відповіді, а ви повертаєте байти плюс статус
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 каже, чи це OCSP POST, чи CRL GET;
// Request.ContentType і Request.Body вже підготовлені,
// а Request.MaxResponseBytes — стеля, яку ви мусите дотримати
Response := HttpExchange(Request.URI, Request.ContentType,
Request.Body, Request.MaxResponseBytes);
Result := setsSucceeded;
except
on E: Exception do
begin
ErrorMessage := E.Message;
// setsRetry дозволяє політиці повторів відступити;
// setsPermanentFailure використовуйте для 404 або поганої URL
Result := setsRetry;
end;
end;
end;
// Оновлення B-B до B-LT одним викликом для кожного підпису
// у завантаженому файлі
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
// Збереження лише з додаванням: байти, які покривають
// наявні підписи, зберігаються дослівно
Pdf.SaveIncrementalUpdate('signed-lt.pdf');
finally
Pdf.Free;
end;
end;
Відмови — на підпис, а не на документ. Респондер, що вичерпує час для одного підписувача, пропускає матеріал того підписувача і лишає решту проходу цілою, що і є бажана поведінка в партії: часткові докази кращі за перерваний запуск, а значення повернення каже, скільки підписів справді покращилися
Ланцюг, який CMS забула вмістити
Перевірці відкликання потрібен сертифікат видавця, і дивовижна кількість стеків підписування опускає проміжні з контейнера CMS. Маршрут відновлення — розширення Authority Information Access, метод доступу 1.3.6.1.5.5.7.48.2, який оголошує URL, звідки можна завантажити сертифікат видавця. HPDFFetchAIAIntermediates проходить ті URL через той самий транспорт, розбирає DER з кожної відповіді й повертає лише ті сертифікати, яких CMS уже не мала, з ключем за хешем DER, тож дублікати та цикли не можуть закрутитися
Дві деталі вирішують, чи це працює проти справжніх центрів сертифікації. Перша — кодування: кінцеві точки CA подають сертифікат як голий DER майже так само часто, як і в PEM-броні, і немає надійного типу вмісту, щоб розрізнити їх. Стійка проба — текстова, потім структурна. Шукайте маркер -----BEGIN CERTIFICATE-----, знімайте броню та декодуйте base64, якщо вона є, а в обох шляхах пересвідчіться, що перший байт результату — $30, тег DER для SEQUENCE. Друга — глибина: отриманий проміжний сам може оголошувати AIA URL для власного видавця, тож прохід додає нові кандидатів до черги й завершує ланцюги, яким бракує двох-трьох стрибків. Це треба обмежувати, для чого і слугує параметр MaxFetch
Що таке початкове значення підпису і чому воно мовчки зазнає невдачі?
Початкове значення (seed value) — це обмеження, яке автор документа чіпляє до поля підпису, щоб сказати підписувачу, який вид підпису прийнятний: який SubFilter, який алгоритм дайджесту, які причини, яка найменша версія PDF, чи мусять дані відкликання бути вбудовані. Воно живе в словнику /SV на полі і визначене в ISO 32000-1 §12.7.5.5. HotPDF записує його через AttachPAdESSeedValue і перевіряє через CheckLoadedSignatureSeedValue, що повертає True, коли поле без обмежень або кожна присутня умова проходить, а на False називає першу невдалу умову через вихідний параметр, який можна класти прямо в повідомлення помилки
Механізм, через який початкові значення легко зробити неправильно, — запис прапорців /Ff, описаний у §12.7.5.5.3. Встановлений біт позначає свою умову як обов'язкову: невідповідність — помилка, і підписувач мусить відмовити. Очищений біт позначає ту саму умову як уподобання: значення фільтрує те, що інтерфейс має пропонувати, і нічого більше. З цього випливають дві пастки. Перша: /Ff живе всередині словника /SV, а не на анотації віджета, тож код, що читає рівень поля /Ff, вічно отримує порожню відповідь і робить висновок, що нічого не примусово. Друга: призначення бітів — не проста низка один, два, чотири, вісім; у HotPDF записувач випромінює 2 для SubFilter, 4 для MinVersion, 32 для AddRevInfo і 64 для DigestMethod. Читач, що припускає послідовні біти, декодує кожну умову як необов'язкову і проходить кожен тест, окрім того одного, що важить
var
Violation: AnsiString;
begin
// Запитайте в поля, чи дозволений профіль, яким збираємося підписувати
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));
// Умову задоволено: продовжуємо прохід підписування
end;
Тест, що викрив оригінальну ваду декодування, не був позитивним тестом. Це було твердження, що примусова невідповідність мусить бути відкинута, і це єдиний вид тесту, який може зловити цей клас вад: декодер, що читає неправильний словник чи неправильні позиції бітів, дає «жодну умову не порушено» для кожного входу, що виглядає точно як правильна поведінка, доки ви навмисно не порушите одну
Де це на драбині LTV
Чотири сходинки, і кожна потребує нижчу. B-B — голий підпис. B-T додає довірений часовий штамп, який фіксує час підписування, тож перевіряльник знає, на який момент оцінювати відкликання. B-LT додає докази відкликання до DSS, що і автоматизує PopulatePAdESLTVEvidence. B-LTA додає часові штампи документа, які поновлюються до того, як попередній слабне, подовжуючи чинність нескінченно; HotPDF відкриває це як RenewPAdESLTATimestamp, що додає новий штамп як інкрементальну редакцію і лишає кожен попередній підпис, штамп і запис DSS недоторканими
Модель інкрементальних оновлень — єдиний правильний спосіб додавати докази до підписаного документа, бо переписування файла зламало б діапазони байтів, які покривають наявні підписи. Якщо вам треба міркувати про те, що змінилося між редакціями, і чи ті зміни такого сорту, який підпис дозволяє, — той аналіз охоплено окремо в аналізі редакцій DocMDP і FieldMDP. Сам конвеєр підписування, включно з джерелами сертифікатів і пастками порядку байтів, — у поясненні підписування PAdES, а сторона перевірки — у перевірці підписів на завантажених документах
Одне практичне попередження про порядок. Збирайте докази якнайшвидше після підписування, в ідеалі в тому самому завданні. Респондери, які можуть відповісти за сертифікат, онлайн, поки сертифікат чинний, і зникають роками пізніше, тож документ, що лишає ваш конвеєр як B-B, може більше ніколи не бути оновлюваним. HotPDF працює як власний компонент VCL для Delphi та C++Builder, і весь прохід доказів — у процесі, окрім вашого власного транспорту; підтримувані профілі наведені на сторінці продукту HotPDF Delphi PDF component