مقاله فنی

شواهد LTV و مقادیر seed در PAdES با HotPDF

PDFای که همین حالا امضا کرده‌اید یک امضای B-B است و بس. ثابت می‌کند چه کسی امضا کرده و اینکه بایت‌ها جابه‌جا نشده‌اند، اما هیچ مدرکی حمل نمی‌کند که گواهی امضاکننده در زمان امضا معتبر بوده، پس یک راستی‌آزمایی‌گر چند سال بعد باید دنبال داده‌های ابطال بگردد که شاید دیگر وجود نداشته باشند. بستن این شکاف یعنی نوشتن پاسخ‌های OCSP و CRLها در Document Security Store سطح سند، و در HotPDF این یک فراخوانی است: PopulatePAdESLTVEvidence هر امضای بارگذاری‌شده را می‌پیماید، درخواست‌های ابطال را از مجموعه گواهی استخراج می‌کند، آن‌ها را از طریق ترنسپورتی که شما تأمین می‌کنید اجرا می‌کند، و مواد گرفته‌شده به‌علاوه زنجیره CMS را در DSS می‌نویسد. تعداد امضاهایی را برمی‌گرداند که شواهدشان فرود آمده، یا منفی یک وقتی سند اصلاً هیچ فیلد امضایی ندارد

تصمیم طراحی‌ای که پیش از استفاده ارزش فهمیدن دارد این است که کتابخانه هرگز سوکتی باز نمی‌کند. هر بایتی که از شبکه می‌رسد از طریق callbackای می‌رسد که شما نوشته‌اید. این احتیاط برای خودِ احتیاط نیست؛ تنها راهی است که این ویژگی می‌تواند درون محیط‌هایی که واقعاً اعتبارسنجی بلندمدت را مطالبه می‌کنند کار کند

چرا کتابخانه حاضر نیست HTTP خودش را انجام دهد؟

چون جاهایی که امضاهای B-LT را مطالبه می‌کنند جاهایی هستند که نمی‌توان شبکه را به یک کتابخانه سپرد. سرویس‌های امضا پشت پروکسی‌های احراز هویت‌کننده با rootهای سازمانی اجرا می‌شوند. لایه‌های امضای جدا از هوا هیچ مسیری به یک پاسخ‌دهنده ندارند و باید با شواهد کش‌شده تغذیه شوند. رژیم‌های ممیزی می‌خواهند هر درخواست خروجی توسط برنامه ثبت شود، نه دفن در یک وابستگی. و مجموعه‌های تست به پاسخ‌های قطعیت‌مند نیاز دارند، که اگر کتابخانه خودش شماره‌گیری کند ناممکن است

ترنسپورت یک مرجع تابع ساده با یک شکل ثابت است، پس خط‌مشی مال شما می‌ماند. HotPDF یک رکورد درخواست به شما می‌دهد که دقیقاً توصیف می‌کند چه چیزی گرفته شود، از جمله نوع محتوا و سقف اندازه پاسخ، و شما بایت‌ها به‌علاوه یک وضعیت برمی‌گردانید

جریان PopulatePAdESLTVEvidence در HotPDF: ترنسپورت FetchEvidence تأمین‌شده توسط فراخواننده، فیلدهای رکورد درخواست و نتایج وضعیت به‌ازای هر امضا
هر بایت شبکه از callback مربوط به FetchEvidence شما می‌گذرد، و هر امضا وضعیت خودش را می‌گیرد تا یک timeout هرگز کل گذر را قطع نکند
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 می‌گوید این یک POST مربوط به OCSP است یا یک GET مربوط به CRL؛
    // 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 اجازه می‌دهد خط‌مشی تلاش مجدد عقب‌نشینی کند؛ برای
      // یک 404 یا یک URL بد از setsPermanentFailure استفاده کنید
      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;

شکست‌ها به‌ازای هر امضاست، نه هر سند. پاسخ‌دهنده‌ای که برای یک امضاکننده timeout می‌شود مواد آن امضاکننده را رد می‌کند و بقیه گذر را سالم می‌گذارد، که رفتاری است که در یک دسته می‌خواهید: شواهد جزئی بهتر از یک اجرای سقط‌شده است، و مقدار بازگشت می‌گوید چند امضا واقعاً بهبود یافتند

زنجیره‌ای که CMS فراموش کرد همراهش بیاورد

بررسی ابطال به گواهی صادرکننده نیاز دارد، و تعداد شگفت‌آوری از استک‌های امضا میانی‌ها را از ظرف CMS حذف می‌کنند. مسیر ریکاوری اکستنشن Authority Information Access است، روش دسترسی 1.3.6.1.5.5.7.48.2، که URLای را اعلام می‌کند که گواهی صادرکننده از آن قابل دانلود است. HPDFFetchAIAIntermediates آن URLها را از طریق همان ترنسپورت می‌پیماید، DER را از هر پاسخ بیرون می‌کشد، و فقط گواهی‌هایی را برمی‌گرداند که CMS از قبل حمل نمی‌کرده، با کلید hash مربوط به DER تا تکراری‌ها و حلقه‌ها نچرخند

دو جزئیات تصمیم می‌گیرند که آیا این در برابر مراجع گواهی واقعی کار می‌کند. اولی رمزگذاری است: نقاط پایانی CA گواهی را تقریباً به همان اندازه که PEM زره‌پوش سرو می‌کنند به‌صورت DER لخت سرو می‌کنند، و هیچ نوع محتوای قابل‌اعتمادی برای تفکیک‌شان نیست. پروب مطمئن متنی است، سپس ساختاری. دنبال نشان -----BEGIN CERTIFICATE----- بگردید، اگر بود زره را بکنید و base64 را رمزگشایی کنید، و در هر دو مسیر تأیید کنید که اولین بایت نتیجه $30 است، تگ DER برای یک SEQUENCE. دومی عمق است: یک میانیِ گرفته‌شده می‌تواند خودش یک URL مربوط به AIA برای صادرکننده خودش اعلام کند، پس پیمایش نامزدهای جدید را به صف می‌افزاید و زنجیره‌هایی که دو یا سه پرش کم دارند را کامل می‌کند. این باید سقف داشته باشد، که کار پارامتر MaxFetch است

نمودار تکمیل زنجیره AIA برای HotPDF: گرفتن URL مربوط به caIssuers، پروب PEM در برابر DER، حذف تکرار با hash مربوط به DER و سقف عمق MaxFetch
HPDFFetchAIAIntermediates URLهای caIssuers را از طریق همان ترنسپورت می‌پیماید، زره PEM را پروب می‌کند و صف را با MaxFetch سقف می‌زند

مقدار seed امضا چیست، و چرا بی‌سروصدا شکست می‌خورد؟

مقدار seed قیدی است که مؤلف سند به یک فیلد امضا می‌چسباند تا به امضاکننده بگوید چه جور امضایی قابل قبول است: کدام SubFilter، کدام الگوریتم digest، کدام دلیل‌ها، کدام حداقل نسخه PDF، آیا اطلاعات ابطال باید تعبیه شود. در یک دیکشنری /SV روی فیلد زندگی می‌کند و در ISO 32000-1 §12.7.5.5 تعریف شده است. HotPDF آن را با AttachPAdESSeedValue می‌نویسد و با CheckLoadedSignatureSeedValue بررسی می‌کند، که وقتی فیلد بدون قید است یا هر قید حاضر رد می‌شود True برمی‌گرداند، و در صورت False نخستین قید مردود را از طریق یک پارامتر خروجی نام می‌برد که می‌توانید مستقیم در یک پیام خطا بگذارید

مکانیزمی که مقادیر seed را به‌آسانی قابل اشتباه می‌کند entry پرچم‌های /Ff است که در §12.7.5.5.3 توصیف شده. یک بیت ست‌شده قیدش را اجباری علامت می‌زند: یک ناهمخوانی خطاست و امضاکننده باید رد کند. یک بیت صفر همان قید را ترجیح علامت می‌زند: مقدار فیلتر می‌کند که UI چه چیزی پیشنهاد دهد و بس. دو تله از آن پیروی می‌کنند. اول، /Ff درون دیکشنری /SV زندگی می‌کند نه روی annotation ویجت، پس کدی که /Ff سطح فیلد را می‌خواند برای همیشه پاسخ خالی می‌گیرد و نتیجه می‌گیرد که هیچ‌چیز اجباری نیست. دوم، تخصیص بیت‌ها یک دنباله ساده یک، دو، چهار، هشت نیست؛ در HotPDF نویسنده 2 را برای SubFilter، 4 را برای MinVersion، 32 را برای AddRevInfo و 64 را برای DigestMethod ساطع می‌کند. خواننده‌ای که بیت‌های ترتیبی فرض کند هر قیدی را اختیاری رمزگشایی می‌کند و همه تست‌ها را پاس می‌کند به‌جز همان که اهمیت دارد

جدول بیت پرچم مقدار seed برای امضای PAdES در HotPDF که بیت‌های Ff مربوط به 2 و 4 و 32 و 64 و برخورد قید اجباری در برابر ترجیحی را نشان می‌دهد
entry مربوط به /Ff درون /SV زندگی می‌کند، و هر جایگاه بیت تصمیم می‌گیرد که یک ناهمخوانی رد قطعی است یا یک ترجیح UI
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 آشکار می‌کند، که یک مهر زمانی جدید را به‌عنوان یک revision افزایشی می‌چسباند و هر امضای پیشین، مهر زمانی و entry مربوط به DSS را دست‌نخورده نگه می‌دارد

مدل به‌روزرسانی افزایشی تنها راه درست افزودن شواهد به یک سند امضاشده است، چون بازنویسی فایل بازه‌های بایتی را که امضاهای موجود پوشش می‌دهند می‌شکند. اگر لازم است درباره اینکه میان revisionها چه تغییر کرده و آیا آن تغییرها از جنس مجاز امضاست استدلال کنید، آن تحلیل جداگانه در تحلیل revision مربوط به DocMDP و FieldMDP پوشش داده شده است. خط لوله امضا خودش، شامل منابع گواهی و تله‌های ترتیب بایت، در آموزش گام‌به‌گام امضای PAdES است، و سمت اعتبارسنجی در تأیید امضاها روی اسناد بارگذاری‌شده است

یک هشدار عملی درباره ترتیب. شواهد را به محض ممکن پس از امضا جمع کنید، ترجیحاً در همان کار. پاسخ‌دهنده‌هایی که می‌توانند از یک گواهی پاسخ بدهند تا وقتی گواهی جاری است آنلاین‌اند و چند سال بعد رفته‌اند، پس سندی که خط لوله شما را به‌صورت B-B ترک کند شاید دیگر هرگز قابل ارتقا نباشد. HotPDF به‌عنوان یک مؤلفه VCL بومی برای Delphi و C++Builder اجرا می‌شود، و کل گذر شواهد درون-فرایندی است به‌جز ترنسپورت خودتان؛ پروفایل‌های پشتیبانی‌شده در صفحه محصول HotPDF Delphi PDF component فهرست شده‌اند