تصمیمگرفتن اینکه یک امضای PDF تحت eIDAS موصوف است یعنی پاسخدادن به پرسشی که هیچ ربطی به رمزنگاری ندارد: آیا گواهی توسط یک سرویس اعتماد صادر شده که یک کشور عضو بهعنوان موصوف فهرست کرده، در لحظهای که امضا انجام شد. پاسخ در یک فهرست مطمئن زندگی میکند، یک سند XML که بهازای هر قلمرو منتشر میشود، و کل ارزش آن سند به اصالتش وابسته است. پس مؤلفه PDFium حاضر نیست داخل یکی را نگاه کند تا وقتی کسی برایش ضمانت کرده باشد. TPdfEuropeanTrustedList.ParseAuthenticated بایتهای خام کامل را پیش از تجزیه حتی یک سرویس به یک IPdfTrustedListAuthenticator تأمینشده توسط فراخواننده میدهد، و فقط وقتی آن احرازکننده صریحاً قبول کند یک snapshot میسازد
آن ترتیب خودِ طراحی است. همهچیز دیگر در این ویژگی از آن پیروی میکند، شامل بخشهایی که ناخوشایند بهنظر میرسند
تجزیهشده یعنی مطمئن نیست
فهرست مطمئنی که تمیز تجزیه میشود به شما میگوید XML خوشفرم است. درباره اینکه چه کسی نوشتهاش هیچ نمیگوید. چون فهرست همان چیزی است که کل تصمیم وضعیت-موصوف شما رویش تکیه دارد، پذیرفتنش بهخاطر تجزیهشدن تصمیم را بیمعنا میکند: مهاجمی که میتواند فهرست را جایگزین کند میتواند مرجع گواهی خودش را موصوف اعلام کند
همان استدلال روی کشکردن هم صدق میکند، و این تلهای است که ارزش نامبردن دارد. حافظه کش snapshot XML اصلی را همراه با یک digest مربوط به SHA-256 ذخیره میکند، و بهآسانی میتوان یک digest منطبق هنگام بارگذاری را مدرن اصالت فهرست دانست. نیست. یک digest که توسط همان فرایندی که فایل را ذخیره کرد محاسبه شده، بدون دخالت هیچ کلیدی، فقط راستیآزمایی میکند که بایتها از وقتی نوشتید تغییر نکردهاند؛ اگر فهرست هنگام کششدن متقلبانه بود، digest تأیید میکند که همان فهرست متقلبانه است. پس بارگذاری یک snapshot کششده از همان احرازکنندهای میگذرد که تجزیه یک نمونه تازه. یکپارچگی و اصالت دو پراپرتی متفاوتاند و فقط یکی از آنها به کلید نیاز دارد
uses
FPdfTrustedList;
type
TListAuthenticator = class(TInterfacedObject, IPdfTrustedListAuthenticator)
public
function Authenticate(const XmlData: TBytes;
out AuthenticationDetails: string): Boolean;
end;
function TListAuthenticator.Authenticate(const XmlData: TBytes;
out AuthenticationDetails: string): Boolean;
begin
// خطمشی شما اینجا زندگی میکند: امضای پوشیده XMLDSIG را در برابر
// گواهی امضاکننده فهرستی که بیرون از باند پین کردهاید راستیآزمایی کنید،
// و توصیف کنید چه را برای رد ممیزی بررسی کردید
Result := VerifyEnvelopedXmlSignature(XmlData, FPinnedListSigner);
if Result then
AuthenticationDetails := 'XMLDSIG verified against pinned LOTL signer';
end;
var
List: TPdfEuropeanTrustedList;
Cache: TFileStream;
begin
List := TPdfEuropeanTrustedList.ParseAuthenticated(RawXml,
TListAuthenticator.Create, TPdfTrustedListOptions.Default);
// snapshot فقط به این دلیل وجود دارد که احرازکننده گفت بله
Cache := TFileStream.Create('tl-de.snapshot', fmCreate);
try
List.SaveCache(Cache);
finally
Cache.Free;
end;
end;
اعتبارسنج مالک خطمشی شبکه نیست
یک اعتبارسنج PAdES کاری با تصمیمگرفتن ندارد که چطور به فهرست فهرستهای مطمئن برسد، آیا از پروکسی بگذرد، چند وقت یکبار تلاش مجدد کند، یا وقتی یک قلمرو در دسترس نیست چه کند. آنها تصمیمهای برنامه و استقرارند، و در محیطهای مقررهای اغلب ممیزی میشوند. پس بهروزرسانیها از طریق IPdfTrustedListSource میرسند، که یک URI و یک سقف بایتی به آن داده و بایت برمیگرداند
آنچه مؤلفه اجرا میکند ناورداهایی است که یک بهروزرسانی را بهروزرسانی نگه میدارند نه جایگزینی. Update میطلبد که قلمرو تغییرنکرده باشد، که شماره دنباله اکیداً افزایش یابد، و که زمان صدور به عقب حرکت نکند. آن سه بررسی، آشکارترین حملات تنزلدادن را شکست میدهند: بازپخش فهرستی قدیمیتر که هنوز سرویسی را فهرست میکند که از آن پس پسگرفته شده، یا جازدن فهرست قلمرو دیگری که سرویسهایش هرگز قصد اعتمادشان را نداشتید
حدود تجزیهگر، و اصلاً هیچ DTDای نیست
TPdfTrustedListOptions اندازه XML، تعداد توکن، عمق تودرتو، تعداد سرویسها، تعداد گواهیها و اندازه یک گواهی منفرد را سقف میزند، با تابع کلاس Default که مقادیر قابل استفاده فراهم میکند. فهرستهای مطمئن اسناد منتشرشدهای با اندازه قابل پیشبینیاند، پس گذاشتن کرانها ارزان است و هیچ فهرست مشروعی نیست که لازم داشته باشد از آنها فراتر رود
جداگانه و بدون قید و شرط، تجزیهگر اعلانهای DTD و entity را رد میکند. آن هم انکار سرویس توسعه-entity و هم مسیر افشای entity خارجی را در یک رد میبندد، و هیچ هزینهای ندارد چون فهرستهای مطمئن از entity استفاده نمیکنند. هر تجزیهگر XMLای که از ورودی نامطمئن قابلرسیدن است باید اینشکل پیکربندی شود؛ تفاوت اینجا این است که رد قابل پیکربندی نیست، پس نمیتوان با یک تغییر گزینه خیرخواهانه خاموشش کرد
وضعیت موصوف کنار اعتماد زنجیره ثبت میشود، نه ادغام در آن
سمت ارزیابی عمداً جداگانه است. TPadesTrustValidationOptions.QualifiedTrustEvaluator یک IPdfQualifiedTrustEvaluator میگیرد، که snapshot فهرست مطمئن پیادهسازیاش میکند. حین اعتبارسنجی ارزیاب گواهی برگ، زنجیره و یک زمان اعتبارسنجی میگیرد، گواهیهای سرویس را با مقایسه دقیق DER در برابر امضاکننده و زنجیره تطبیق میدهد، وضعیت سرویس، شناسه نوع سرویس و URIهای qualifier را در آن مقطع زمانی ترکیب میکند، و یک رکورد ارزیابی برمیگرداند
نتیجه روی هر امضا در دو جا فرود میآید: QualifiedTrustStatus بهعنوان یک وضعیت درشت، و QualifiedTrust بهعنوان ارزیابی کامل با قلمرو، نام ارائهدهنده، نام سرویس، شناسه نوع، وضعیت و زمان شروع وضعیت. کاری که نمیکند تغییر CertificateTrustStatus است. اعتماد زنجیره سیستمی و وضعیت موصوف به پرسشهای متفاوتی پاسخ میدهند، و گزارشی که آنها را فرومیریزد نمیتواند «مطمئن ولی موصوف نیست» را از «موصوف ولی زنجیره اعتبارسنجی نمیشود» تمیز دهد، که هر دو واقعیاند و هر دو به برخورد متفاوت نیاز دارند
var
Options: TPadesTrustValidationOptions;
Report: TPadesValidationResult;
I: Integer;
begin
Options := TPadesTrustValidationOptions.Default;
Options.CheckRevocation := True;
Options.QualifiedTrustEvaluator := List; // snapshot احرازشده
Options.QualifiedValidationTime := SigningTime; // نه Now
Report := Pdf.ValidatePadesTrust(Options);
for I := 0 to High(Report.Signatures) do
if Report.Signatures[I].QualifiedTrustStatus = pcsValid then
Writeln(Format('signature %d qualified by %s / %s (%s)',
[I, Report.Signatures[I].QualifiedTrust.Territory,
Report.Signatures[I].QualifiedTrust.ProviderName,
Report.Signatures[I].QualifiedTrust.ServiceName]))
else if Report.Signatures[I].QualifiedTrustStatus = pcsIndeterminate then
// بدون سرویس منطبق، یا snapshot نمیتواند برای این زمان پاسخ دهد
Writeln(Format('signature %d: qualified status undetermined', [I]));
end;
چرا زمان اعتبارسنجی همین حالا نیست
چون موصوفبودن یک پراپرتیِ یک لحظه است. یک سرویس اعتماد میتواند وضعیت موصوف بگیرد، بعداً پس گرفته شود، و بعدتر از آن دوباره برقرار شود، و هر یک از آن گذرها یک زمان شروع در فهرست حمل میکند. امضایی که هنگام موصوفبودن سرویس انجام شده بعدش موصوف میماند؛ امضایی که پیش از اعطا انجام شده بهطور گذشتهنگر موصوف نمیشود. پس ارزیابی در برابر زمان جاری در هر دو جهت پاسخ غلط میدهد
فهرست آنچه برای این لازم است را حمل میکند: هر رکورد سرویس یک زمان شروع وضعیت و یک پرچم که entryهای تاریخی را از جاریها تفکیک میکند دارد، و ارزیاب آنها را در برابر زمانی که شما تأمین میکنید ترکیب میکند. در عمل آن زمان از یک مهر زمانی مطمئن روی امضا میآید نه از زمان امضای ادعاشده در CMS، که دلیل این است که مواد اعتبارسنجی بلندمدت حتی برای پرسشی که شبیه یک جستوجوی خطمشی است اهمیت دارد؛ سمت مهر زمانی و DSS در مقاله امضای بلندمدت پوشش داده شده است
چه چیزهایی هنوز باید بسازید
سه چیز، و هیچکدام جایشان در یک کتابخانه PDF نیست. احرازکننده، یعنی راستیآزمایی واقعی XMLDSIG در برابر گواهی امضاکننده فهرستی که از طریق مجرایی که به آن اعتماد دارید گرفتهاید. خطمشی واکشی، یعنی چطور و چقدر اغلب تازه میکنید، و برنامه شما وقتی یک تازهسازی شکست میخورد چه میکند. و دامنه سرزمینی، یعنی اصلاً کدام فهرستها را حمل میکنید، که یک تصمیم تجاری درباره اینکه طرفهای حساب شما در کدام کشورهای عضو امضا میکنند است
آنچه از مؤلفه میگیرید بخشی است که بهآسانی میتوان بهشکل ظریف غلطش گرفت: ترتیب احراز-پیش-از-تجزیه، تجزیه XML کراندار و عاری از entity، ناورداهای بهروزرسانی یکنوا، تطبیق سرویس با DER دقیق، ارزیابی وضعیت تاریخی، و نتیجهای که از اعتماد زنجیره عادی جدا میماند. اگر مشکل فوری شما بنیادیتر است، اینکه اعتبارسنج امضایی را که فکر میکنید سالم است رد میکند، علتهای معمول در چرا اعتبارسنجها امضاهای PAdES را رد میکنند فهرست شده، و سطح بازرسی امضا در بازرسی امضاها و سطوح PAdES توصیف شده است. قابلیتهای مؤلفه در صفحه محصول PDFium Delphi component فهرست شده است