validate کردن یک signature در PAdES یعنی بررسی سه چیز مستقل، و یک check mark سبز در یک viewer فقط دربارهٔ سومی به شما میگوید. اول، array /ByteRange باید byteهای درست را پوشش دهد: spanهایی که name میبرد باید دقیقاً همان inputای را reconstruct کنند که CMS digest روی آن گرفته شده، بدون اینکه هیچ byte signedای بیرون از آنها بماند. دوم، certificate داخل CMS باید به یک root که به آن اعتماد دارید chain شود و attribute امضاشدهٔ signing-certificateای که PAdES لازم دارد را حمل کند. سوم، اگر profile یک timestamp ادعا میکند، یک token RFC 3161 باید signature value را به یک نقطه در زمان قبل از انقضای certificate bind کند. Acrobat هر سه را در یک icon collapse میکند؛ یک conformance checker آنها را جدا نگه میدارد، و codeای که این fileها را تولید میکند هم باید همینطور. losLab PDF Library (PDF Library for Delphi) طرف signing آن، re-embed کردن timestamp، و callهای audit برای inspect کردن یک ByteRange قبل از اعتماد به آن را به شما میدهد
یک تمایز تقریباً هر implementation اولیهٔ PAdES را بههم میریزد، پس قبل از هر codeای ارزش بیان شدن دارد. یک signature نوشتهشده با /SubFilter /adbe.pkcs7.detached یک signature کاملاً سالم ISO 32000-1 §12.8 است که Acrobat آن را بهعنوان valid گزارش میدهد. این همچنین یک signature در PAdES نیست، چون ETSI EN 319 142-1 در هر baseline level به ETSI.CAdES.detached نیاز دارد. یک conformance checker در eIDAS اولی را reject و دومی را میپذیرد حتی اگر cryptography یکسان باشد. profile یک claim است که document دربارهٔ خود میکند، و درست آوردن آن claim یک call در PDF Library for Delphi است
آنچه یک signature در PDF را به یک signature در PAdES تبدیل میکند
ETSI EN 319 142-1 چهار baseline level را روی format در CMS تعریف میکند. PAdES-B-B entry point است: یک signature در CAdES در یک signature field در PDF با SubFilter ETSI.CAdES.detached و یک attribute امضاشدهٔ signing-certificate. PAdES-B-T یک timestamp RFC 3161 روی signature value اضافه میکند، ثابت میکند signature قبل از یک نقطه در زمان که هیچکس نمیتواند backdate کند وجود داشته. PAdES-B-LT certificateها، CRLها و responseهای OCSP لازم برای validation را در یک Document Security Store embed میکند، تا file بعد از بازنشستگی infrastructure توسط CA صادرکننده قابل verify بماند. PAdES-B-LTA با یک document timestamp که evidence انباشتهشده را هنگام ضعیفشدن algorithmها دوباره حفاظت میکند، stack را cap میکند
PDF Library for Delphi این conceptها را روی sign-process API خود نگاشت میکند. profile marker با SetSignProcessCustomSubFilter است. اگر policy شما به یک commitment-type indication نیاز دارد (proof of origin، proof of approval، یا یکی از identifierهای دیگر ETSI با شمارهٔ ۱ تا ۶)، آن از طریق SetSignProcessCommitmentType میرود. یک signature policy صریح با SetSignProcessSignaturePolicy attach میشود، که policy OID و digest آن را میگیرد. یک default توجه میطلبد: با digest algorithm روی auto، کتابخانه برای signatureهای ETSI و adbe.pkcs7.detached الگوریتم SHA-256 را انتخاب میکند و فقط در مسیر legacy adbe.pkcs7.sha1 به SHA-1 برمیگردد. بههرحال آن را صریح set کنید. auditorها میپرسند از کدام hash استفاده کردهاید، و یک مقدار صریح در code آسانتر از یک default که باید برای توضیحش manual را بخوانید قابل دفاع است
تولید signature baseline
flat API signing را بهشکل یک state machine یکشاتی drive میکند: یک process روی source file باز کنید، آن را پیکربندی کنید، به یک output file تمام کنید، result code را بخوانید. sequence زیر یک signature PAdES-B-B با SHA-256 تولید میکند. خطی که بیشتر از همه اهمیت دارد هیچ ربطی به خود signature ندارد. آن reservation عمداً بزرگنماییشدهٔ /Contents است، چون تنها چیزی است که بعداً نمیتوانید تغییر دهید اگر تابهحال یک timestamp قرار است به این signature اضافه شود
var
Pdf: TPDFlib;
SignId: Integer;
begin
Pdf := TPDFlib.Create;
try
SignId := Pdf.NewSignProcessFromFile('invoice.pdf', '');
if SignId = 0 then
raise Exception.Create('cannot open source PDF');
Pdf.SetSignProcessField(SignId, 'Sig1');
Pdf.SetSignProcessPFXFromFile(SignId, 'company.pfx', PfxPassword);
Pdf.SetSignProcessInfo(SignId, 'Approved', 'Vienna', 'billing@example.com');
Pdf.SetSignProcessCustomSubFilter(SignId, 'ETSI.CAdES.detached');
Pdf.SetSignProcessDigestAlgorithm(SignId, 2); // SHA-256
Pdf.SetSignProcessReserveContentsBytes(SignId, 8192); // room for a timestamp later
Pdf.EndSignProcessToFile(SignId, 'invoice-signed.pdf');
if Pdf.GetSignProcessResult(SignId) <> 1 then
raise Exception.CreateFmt('signing failed, code %d',
[Pdf.GetSignProcessResult(SignId)]);
Pdf.ReleaseSignProcess(SignId);
finally
Pdf.Free;
end;
end;
NewSignProcessFromFile وقتی source اصلاً نمیتواند باز شود ۰ برمیگرداند. بعد از آن، GetSignProcessResult حالتهای شکستی را که واقعاً در production رخ میدهند جدا میکند: ۴ یعنی یک PDF password اشتباه، ۷ یک PFX password اشتباه، ۹ یک certificate file بدون private key، ۱۰ یک output path غیرقابلنوشتن، ۱۱ یک failure هنگام اعمال کردن byteهای signature. log کردن code عددی کنار input file name یک ticket پشتیبانی مبهم را به یک diagnosis یکدقیقهای تبدیل میکند
افزودن timestamp RFC 3161 که کتابخانه برایتان fetch نمیکند
PDF Library for Delphi هیچ TSA clientای ship نمیکند، و این یک مرز عمدی بهجای یک gap است. کتابخانه hashای که timestamp authority باید countersign کند را محاسبه میکند و CMS augmented را بعداً re-embed میکند؛ exchange در HTTP و surgery در CMS بین آن دو به caller تعلق دارد. یک دلیل فنی سخت برای آن split هست. control در Windows CryptoAPI که nominal unsigned attributeها را اضافه میکند، CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR، روی layout detached SignedData که PAdES استفاده میکند با CRYPT_E_INVALID_INDEX fail میشود. پس CMS enhanced باید از یک CMS encoder تحت کنترل خودتان بیاید. هیچ کتابخانهای نمیتواند token را با یک system call بیصدا fold کند، و هر آن که ادعا میکند surgery را جایی که نمیتوانید ببینید انجام میدهد
var
Pdf: TPDFlib;
StsId: Integer;
HashHex, TstDer, TsAttr, AugmentedCms: AnsiString;
begin
Pdf := TPDFlib.Create;
try
StsId := Pdf.NewPAdESSignatureTimeStampProcessFromFile('invoice-signed.pdf', '');
Pdf.SetPAdESSignatureTimeStampField(StsId, 'Sig1');
Pdf.SetPAdESSignatureTimeStampDigestAlgorithm(StsId, 2);
HashHex := Pdf.GetPAdESSignatureValueHashHex(StsId);
// هر دو فراخوانی زیر کد برنامهاند: یک HTTP POST به TSA،
// و یک CMS re-encode که token را بهعنوان attribute بدون امضا ضمیمه میکند
TstDer := RequestTimeStampToken(HashHex);
TsAttr := Pdf.BuildPAdESSignatureTimeStampAttribute(TstDer);
AugmentedCms := AttachUnsignedAttribute(Pdf.GetPAdESSignatureCMSBytes(StsId), TsAttr);
Pdf.SetPAdESSignatureCMSBytes(StsId, AugmentedCms);
Pdf.EndPAdESSignatureTimeStampProcessToFile(StsId, 'invoice-bt.pdf');
if Pdf.GetPAdESSignatureTimeStampProcessResult(StsId) <> 1 then
raise Exception.Create('timestamp embedding failed');
Pdf.ReleasePAdESSignatureTimeStampProcess(StsId);
finally
Pdf.Free;
end;
end;
به result codeها اینجا توجه کنید: ۱۲ یعنی signature field نامبرده وجود ندارد، ۱۱ که CMS موجود نمیتوانست parse شود، و ۱۳ که CMS augmented دیگر در placeholder /Contents reserved جا نمیشود. code ۱۳ آنی است که آسیب میزند، چون تنها fix re-sign کردن است: یک timestamp token معمولی با certificate chainاش ۴ تا ۶ KB است، و reservation ۸۱۹۲-byteای ساختهشده در طول step B-B دقیقاً برای آن وجود دارد که این step جایی برای land داشته باشد
validation از ByteRange شروع میشود، نه certificate chain
یک check mark سبز در یک viewer یک تصمیم اعتماد در برابر certificate store آن machine است، نه یک verdict ساختاری دربارهٔ file. validation برنامهای باید پایینتر شروع شود، با سوالی که incremental updateها subtle میکنند: کدام byteها را هر signature واقعاً پوشش میدهد؟ هر enhancementای که اینجا بحث شد، چه یک signature دوم، یک dictionary DSS، یا یک document timestamp، از طریق incremental update میرسد، و هر update byteهایی بیرون از /ByteRange signature قبلی append میکند. آن byteهای appendشده legit هستند. یک validator همچنان باید آنها را در برابر modification policy document classify کند، و level DocMDP per-field که آن policy در آن زندگی میکند با GetSignatureDocMDPLevelByName قابلخواندن است
var
Doc: TPDFlibSignDoc;
Names: TStringList;
I: Integer;
B0, B1, B2, B3, FileSize: Int64;
begin
FileSize := TFile.GetSize('invoice-bt.pdf'); // before Open: SignDoc holds a share lock
Doc := TPDFlibSignDoc.Create;
try
if not Doc.Open('invoice-bt.pdf', '', False) then
raise Exception.Create('cannot open for audit');
Names := TStringList.Create;
try
Doc.GetSignatureFieldNames(Names);
for I := 0 to Names.Count - 1 do
if Doc.GetSignatureValueObjNum(Names[I]) > 0 then // >0 یعنی واقعاً امضا شده
begin
B0 := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 11)));
B1 := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 12)));
B2 := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 13)));
B3 := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 14)));
if (B0 = 0) and (B2 + B3 = FileSize) then
Writeln(Names[I], ': covers the file to EOF')
else
Writeln(Names[I], ': earlier revision, or unexpected ByteRange layout');
end;
finally
Names.Free;
end;
Doc.Close;
finally
Doc.Free;
end;
end;
دو تله در این audit path زندگی میکنند. TPDFlibSignDoc.Open file را با یک exclusive share lock نگه میدارد، پس یک validator که همچنین میخواهد byteهای raw file را برای CMS verification هش کند باید file را قبل از باز کردن برای audit در memory بخواند. آن ترتیب را برعکس کنید و read روی lockای که خودتان set کردهاید fail میشود. تلهٔ دوم بیصدا بهجای پر صدا است: counterpart در flat-API GetSignProcessByteRange Integer برمیگرداند در حالی که offsetهای زیرین Int64 هستند، پس بالای ۲ GB flat call بدون شکایت truncate میکند، که چرایی آن است که این example offsetها را از طریق class audit میکشد. یک غیبت نیز ارزش نامبردن دارد. flat layer اصلاً wrapper VerifySignature ندارد. verdictهای cryptographic از TPDFlibSignatureVerifier در level class میآیند، که vsValid، vsInvalid یا vsUnknown برمیگرداند، یا از یک validator خارجی که policy compliance شما از قبل به آن اعتماد دارد
validation طولانیمدت: DSS، VRI و document timestamp
PAdES-B-LT وجود دارد چون infrastructure revocation فانی است. ETSI EN 319 142-1 §5.4.2.2 Document Security Store را مشخص میکند: یک dictionary در سطح document که certificateها، CRLها و responseهای OCSP را حمل میکند، بهاختیار per signature از طریق entryهای VRI که بر اساس hash از /Contents هر signature key میشوند، index میشوند. flow در PDF Library for Delphi design timestamp را بازتاب میدهد. NewPAdESDSSProcessFromFile process را باز میکند؛ AddPAdESDSSCertificate، AddPAdESDSSCRL و AddPAdESDSSOCSP blobهای DER را میپذیرند؛ AddPAdESDSSVRI material انتخابی را به یک signature bind میکند؛ EndPAdESDSSProcessToFile همهچیز را بهعنوان یک incremental update مینویسد. قسمت سخت سمت شما میماند. fetch کردن material revocation، و قضاوت اینکه آیا بهاندازهٔ کافی تازه است که ارزش embed شدن داشته باشد، کار caller است. کتابخانه تضمین میکند dictionaryها از نظر ساختاری conformant هستند؛ نمیتواند تضمین کند responder OCSP شما راست گفته است
endpoint archival، B-LTA، یک document timestamp اضافه میکند: یک signature field مجزا که نوع آن بهجای Sig، DocTimeStamp است، تولیدشده از طریق SetSignProcessDocTimeStamp با یک signature length reserved. این signature timestamp از step B-T را replace نمیکند. signature timestamp ثابت میکند چه زمانی یک signature خاص وجود داشته؛ document timestamp کل file، شامل evidence در DSS، را حفاظت میکند، و عنصری است که یک archive طولانیمدت هر چند سال یکبار هنگام ضعیفشدن algorithmها renew میکند. یک profile archival بالغ هر دو را حمل میکند. برای readerهایی که پیش از این ساختارها هستند، TPDFlibSignDoc.EnsurePAdESExtensions extension توسعهدهندهٔ ESIC را در catalog document ضبط میکند، اعلام میکند که file از featureهای تعریفشدهٔ ETSI استفاده میکند
یک واکنش به همهٔ اینها ارزش دفع شدن دارد، چون شبیه یک bug بهنظر میرسد و نیست. یک viewer اغلب روی fileای که structure در PAdES آن کاملاً درست است «validity unknown» گزارش میدهد. trust و structure محور مستقل هستند. viewer سادهاند نمیتواند signer را به rootای روی آن machine chain کند، که با CAهای private و certificateهای test معمول است، حتی وقتی audit ByteRange و verify کردن CMS هر دو pass میشوند. fix آن است که root certificate را بهدرستی distribute کنید، یا در برابر trusted listهای اروپایی ارزیابی کنید وقتی status واجد شرایط eIDAS هدف واقعی است، بهجای دست زدن به code signing
برای دیدگاه سمت audit، یعنی enumerate کردن signature fieldها در سراسر یک corpus، dump کردن layoutهای ByteRange و خواندن levelهای DocMDP بهصورت bulk، به قطعه همراه دربارهٔ workbench compliance و signing نگاه کنید. documentهای signed که همچنین باید policy archival را برآورده کنند در workflow توصیفشده در preflight در PDF/A و PDF/UA در Delphi متعلق هستند. مستندات کامل API و downloadهای evaluation در page محصول losLab PDF Library برای Delphi هستند