כדי לגרום ל-PDF חתום להישאר מאומת שנים מהיום, לאחר שתוקף תעודת החתימה פג וה-CA שלה מתחלף, רכיב ה-PDFium יכול להפיק חתימות PAdES לטווח ארוך ב-Windows: חותמת זמן לפי RFC 3161 לזמן אמין, מאגר אבטחת מסמכים (Document Security Store) המטמיע את חומר האימות, וחותמת זמן של מסמך (DocTimeStamp) לארכיון על פני הקובץ כולו. שלוש תוספות אלו הן רמות הבסיס PAdES B-T, B-LT ו-B-LTA, ורכיב ה-PDFium מגיע לכולן מקריאת חתימה יחידה. זוהי יכולת ל-Windows בלבד, המוגבלת על ידי PadesCryptoAvailable
מאמר זה הוא השלישי בסדרה. הראשון, חתימה על קובצי PDF עם PAdES B-B באמצעות PDFium VCL, מכסה את החתימה הבסיסית; השני, בדיקת חתימות דיגיטליות של PDF ורמות PAdES, מכסה קריאת חתימה בחזרה וזיהוי הרמה שאליה הגיעה. כאן הנושא הוא הארכיון: חתימות שחייבות לשרוד את פקיעת תוקף התעודות ולהישאר מאומתות לתקופת שמירה הנמדדת בשנים, ולא בשבועות
מדוע חתימת PDF תקפה מפסיקה להיות מאומתת לאורך זמן?
חתימה בסיסית עונה רק על שאלה אחת: האם הבתים הללו שונו לאחר החתימה. היא אינה מוכיחה, בפני עצמה, מתי החתימה התרחשה, והפער הזה הוא מה ששובר אותה מאוחר יותר. מבנה ה-CMS נושא מאפיין של זמן חתימה, אך החותם כותב ערך זה מהשעון שלו, ולכן למאמת אין סיבה לסמוך עליו. כאשר תוקף תעודת החתימה פג מאוחר יותר או שה-CA מבטל אותה, מאמת קפדני אינו יכול עוד להבחין בין חתימה שבוצעה בזמן שהתעודה הייתה תקפה לבין חתימה שזויפה לאחר שפג תוקפה. החתימה הייתה תקינה ביום שבו היא נוצרה והופכת לבלתי ניתנת לאימות ללא שום אשמה מצדה
ארכוב לטווח ארוך מתקן זאת בשני צעדים. ראשית, צד שלישי מהימן, רשות חותמות זמן (Time Stamping Authority), מאשר את הזמן באופן קריפטוגרפי כך שהוא אינו תלוי עוד ביושרת החותם. שנית, כל תעודה, תגובת ביטול ו-CRL הנדרשים לבנייה ובדיקה של שרשרת האמון מוטמעים בתוך ה-PDF עצמו, כך שהאימות אינו תלוי בשרתים שעשויים להיעלם עד שמישהו יבדוק. PAdES, המתוקנן כ-ETSI EN 319 142-1, מציב אותם כשכבות של רמות בסיס, ורכיב ה-PDFium כותב את המבנים שכל רמה דורשת
מהי חתימת PDF עם LTV או חותמת זמן לארכיון?
PAdES מגדיר ארבע רמות בסיס הנבנות זו על גבי זו, ורכיב ה-PDFium חושף אותן דרך ה-enum TPadesLevel (plB_B, plB_T, plB_LT, plB_LTA). B-B היא החתימה הפשוטה. B-T מוסיפה חותמת זמן אמינה מעל ערך החתימה. B-LT (טווח ארוך) מוסיפה את חומר האימות המוטמע, התכונה שרוב האנשים מתכוונים אליה ב-\"LTV\". B-LTA (טווח ארוך עם חותמת זמן לארכיון) עוטפת הכל בחותמת זמן נוספת המכסה את הקובץ כולו, כך שחומר האימות עצמו נשאר שלם בצורה מוכחת מרגע הארכוב
הקריאה המעשית היא גרם מדרגות. B-T מוכיח *מתי*. B-LT מוכיח *באמצעות מה* עדיין ניתן לבדוק את החתימה. B-LTA מוכיח שכל החבילה לא שונתה מאז שנחתמה, וזו הרמה שאתה מחדש, על ידי הוספת DocTimeStamp חדש לפני שפג תוקפו של הקודם, כדי להאריך את הארכיון ללא הגבלה. עבור חוזה או רשומת תאימות עם חובת שמירה ארוכה, B-LTA הוא היעד; B-LT הוא הרצפה הפרגמטית
הוספת זמן אמין: PAdES B-T עם RFC 3161
רכיב ה-PDFium הופך חתימת B-B ל-B-T על ידי הגדרת שני שדות ב-TPadesSignOptions: העלאת ה-Level ל-plB_T ואספקת TsaUrl. כאשר שניהם קיימים, צינור החתימה מבצע hash לשמיניות החתימה הגולמיות עם SHA-256, אורז את התמצית הזו כ-messageImprint של RFC 3161, שולח את הבקשה ב-POST ל-TSA מעל WinHttp, ומטמיע את הטוקן המוחזר כמאפיין לא חתום signature-time-stamp (OID 1.2.840.113549.1.9.16.2.14) ב-unsignedAttrs [1] של ה-SignerInfo, לפי EN 319 142-1 §6.3. חותמת הזמן מכסה את ערך החתימה באופן ספציפי, וזה מה שקושר את הזמן המהימן לחתימה הספציפית הזו
uses
PDFium, FPdfPades;
procedure SignWithTimestamp;
var
Pdf: TPdf;
Options: TPadesSignOptions;
begin
Pdf := TPdf.Create(nil);
try
if not Pdf.PadesCryptoAvailable then
raise Exception.Create('PAdES signing backend is Windows-only');
Pdf.FileName := 'contract.pdf';
Pdf.Active := True;
Options := TPadesSignOptions.Default;
Options.CertificateThumbprint := 'A1B2C3D4E5F6071829304152637485960A1B2C3D';
Options.Level := plB_T;
Options.TsaUrl := 'http://timestamp.example-tsa.com/tsr';
Options.Reason := 'Contract execution';
// Nonce left at 0: the component derives a unique anti-replay value.
Pdf.SignPades('contract-bt.pdf', Options);
finally
Pdf.Free;
end;
end;
ה-CertificateThumbprint הוא ה-hash מסוג SHA-1, בהקסדצימלי, של תעודה במאגר \"MY\" של המשתמש הנוכחי שאת המפתח הפרטי שלה אתה יכול להפעיל. שדה ה-Nonce הוא ערך האנטי-שידור (anti-replay) של RFC 3161; השאר אותו אפס והרכיב יגזור ערך ייחודי לכל בקשה מקיפול SHA-256 של מונה, השעון, ספירת התיקתוקים (tick count) ומזהה התהליך, כך ששתי בקשות המופקות באותה מילישנייה לא יתנגשו. העבר ערך משלך רק כאשר יש לך מחולל מספרים אקראיים (RNG) קריפטוגרפי שאתה מעדיף לסמוך עליו. אזהרה כנה אחת: B-T דורש TSA נגיש, ולכן החתימה כרוכה כעת בקריאת רשת ויורשת את הזמינות והשהיה של ה-TSA
הטמעת חומרי אימות: B-LT ו-B-LTA בקריאה אחת
העלייה לרמת אימות לטווח ארוך היא שינוי יחיד: הגדרת Level ל-plB_LTA ושמירה על ה-TsaUrl. רכיב ה-PDFium מריץ אז את כל סולם השלבים בתוך קריאת SignPades אחת. הוא חותם B-B, מבצע חותמת זמן עבור B-T, מזריק את ה-Document Security Store עבור B-LT, ומצרף את חותמת הזמן לארכיון עבור B-LTA, כל אחד כעדכון אינקרמנטלי משלו כך שהבתים המוקדמים יותר נשארים שלמים בית-אחר-בית
procedure SignForArchive;
var
Pdf: TPdf;
Options: TPadesSignOptions;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'contract.pdf';
Pdf.Active := True;
Options := TPadesSignOptions.Default;
Options.CertificateThumbprint := 'A1B2C3D4E5F6071829304152637485960A1B2C3D';
Options.Level := plB_LTA; // drives B-B -> B-T -> B-LT -> B-LTA
Options.TsaUrl := 'http://timestamp.example-tsa.com/tsr';
Options.Location := 'Head Office';
// One call writes the timestamp, the DSS and the archive DocTimeStamp.
Pdf.SignPades('contract-lta.pdf', Options);
finally
Pdf.Free;
end;
end;
עבור שלב ה-B-LT רכיב ה-PDFium בונה את שרשרת התעודות באמצעות CertGetCertificateChain וכותב מילון /DSS הנושא את תעודת החותם בתוספת כל CA ביניים במערך /Certs (עם מערכי /OCSPs ו-/CRLs מקבילים), לצד סימן /Extensions /ESIC ברמה 1, בדיוק כפי שמכתיב EN 319 142-1 §5.4.2. שורש החתימה העצמית (self-signed root) מושמט בכוונה, מכיוון ש-RFC 5652 §10.2.3 מתיר זאת והשורש הוא כבר עוגן אמון שהמאמת מחזיק בו. התוצאה היא חתימה שקורא יכול לאמת ללא שום דבר מלבד הקובץ שבידו
עבור B-LTA הרכיב מצרף חותמת זמן של מסמך (Document Time-stamp): מילון חתימה עם /SubFilter /ETSI.RFC3161 ששלו ה-/ByteRange משתרע על פני הקובץ כולו, ה-/Contents שלו מחזיק טוקן של RFC 3161 מעל אותו טווח, והוא מקדם את סימן ה-ESIC לרמה 2. זהו שלב הארכוב של §5.4.3 ו-TS 119 142-3. מכיוון ש-DocTimeStamp מכסה את ה-DSS כמו גם את החתימה, הוא מוכיח שחומר האימות המוטמע היה קיים ולא שונה בזמן הארכוב, וזה בדיוק מה שמדיניות שמירה ארוכה צריכה להיות מסוגלת להציג
הרחבת חתימה שכבר קיימת
לפעמים החתימה כבר קיימת במסמך ואתה רק צריך להוסיף או לרענן את מאגר האימות, למשל כדי להעלות חתימת B-B של צד שלישי ל-B-LT עבור הארכיון שלך. רכיב ה-PDFium חושף את מזרק ה-DSS ישירות דרך SaveAsPadesDss, המצרף את מבני ה-/DSS וה-/Extensions כעדכון אינקרמנטלי מבלי לגעת בבתיי החתימה הקיימים
procedure AddValidationStore;
var
Pdf: TPdf;
DssOpts: TPadesDssOptions;
Cert: TPadesDssMaterial;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'already-signed.pdf';
Pdf.Active := True;
DssOpts := TPadesDssOptions.Default;
DssOpts.EsicExtensionLevel := 1; // 1 for B-LT, 2 for B-LTA
Cert.DerBytes := LoadSignerCertDer; // your DER-encoded certificate
SetLength(DssOpts.Certs, 1);
DssOpts.Certs[0] := Cert;
Pdf.SaveAsPadesDss('already-signed-lt.pdf', DssOpts);
finally
Pdf.Free;
end;
end;
אתה מספק את התעודות בקידוד DER, ובאופן אופציונלי תגובות OCSP ו-CRL, שמאמת יצטרך. הגדר EsicExtensionLevel ל-1 עבור מאגר B-LT או ל-2 כאשר תבוא בעקבותיו חותמת זמן של מסמך (DocTimeStamp). השאר את IncludeVri בברירת המחדל שלו כ-False: תקן EN 319 142-1 §5.4.2.3 מתייחס למילון ה-/VRI לכל חתימה כאופציונלי וממליץ שלא להשתמש בו אלא אם כן קורא ספציפי שאתה מייעד אליו דורש זאת
עלויות וגבולות ששווה לתכנן עבורם
שלוש מגבלות כנות מעצבות את המקום שבו רמות אלו מתאימות. ראשית, התשתית הקריפטוגרפית היא ל-Windows בלבד: חתימה וחותמות זמן עוברות דרך מאגרי ה-CNG וה-WinHttp של הפלטפורמה, ולכן PadesCryptoAvailable מחזיר False בכל מקום אחר ו-SignPades מעלה חריגה. בדיקת צד הקריאה המכוסה במאמר הנלווה נשארת חוצת-פלטפורמות; רק כתיבת החתימות מוגבלת ל-Windows. שנית, B-T ו-B-LTA תלויים ברשות חותמות זמן (TSA), מה שפירושו סבב רשת בזמן החתימה ושירות חיצוני שזמן הפעולה התקינה שלו ומגבלות הקצב שלו הופכים לחלק מנתיב החתימה שלך
שלישית, כל רמה עולה באחסון. כל שלב מצרף עדכון אינקרמנטלי במקום לכתוב מחדש את הקובץ, ה-DSS מטמיע שרשרת תעודות מלאה, וחותמת הזמן של מסמך (DocTimeStamp) לארכיון שומרת מקום לטוקן שלה, כך שקובץ B-LTA גדול משמעותית ממקור ה-B-B שלו. זוהי הפשרה המכוונת: אתה משקיע בתים כעת כדי לקנות יכולת אימות מאוחר יותר. עבור רוב ארכיוני החוזים והתאימות זוהי הפשרה הנכונה, אך שווה למדוד זאת במסמכים שלך במקום להניח מראש. כאשר אתה צריך לאשר שקובץ מוגמר אכן הגיע לרמה שהתכוונת אליה, אמת אותו באמצעות הטכניקות בבדיקת חתימות דיגיטליות של PDF ורמות PAdES, ואם אתה מקשיח צינור מסמכים באופן רחב יותר, ההערות על ביקורת סיכוני אבטחה של PDF מכסות את הבדיקות הנלוות
תכונות החתימה של PAdES, ה-DSS וחותמות הזמן לארכיון המוצגות כאן הן חלק מ-PDFium Component עבור דלפי ו-C++Builder, שדף המוצר שלו נושא את רפרנס החתימה המלא ואת דרישות הפלטפורמה