مقاله فنی

تایید امضاهای دیجیتال PDF در دلفی با HotPDF

کامپوننت HotPDF امضاهای دیجیتال را در اسناد PDF بارگذاری‌شده از طریق سه متد THotPDF تایید می‌کند: GetLoadedSignatureInfo، VerifyLoadedSignature، و VerifyLoadedSignatureEx که در نسخه v2.259.0 معرفی شدند. این کامپوننت بخش‌های /ByteRange فایل اصلی را دوباره هش می‌کند، ویژگی messageDigest مربوط به CMS را بررسی می‌نماید و یک تاییدیه RSA PKCS#1 v1.5 را در برابر گواهی امضاکننده جاسازی‌شده اجرا می‌کند و در صورت دست‌نخورده بودن بایت‌های سند، مقدار svValid را برمی‌گرداند

سناریو معمولی است اما اهمیت آن بسیار بالاست. طرف مقابل قرارداد امضا شده را برمی‌گرداند، جریان کاری شما باید آن را بایگانی کند و شخصی تنها سوال مهم را می‌پرسد: آیا این دقیقاً همان سندی است که ما فرستادیم، بایت به بایت، که توسط گواهی ادعاشده امضا شده است؟ پاسخ به این سوال در کد، بخش تایید امضا را تشکیل می‌دهد;بخش امضا کردن، یعنی ساخت و جاسازی امضاهای PAdES در وهله اول، در مقاله مکمل درباره ایجاد امضاهای دیجیتال PAdES با HotPDF پوشش داده شده است. این مقاله درباره جهت دیگر است: یک PDF که از قبل امضا شده به دست شما می‌رسد و شما به جای اسکرین‌شات از علامت تیک سبز آکروبات، یک حکم برنامه‌نویسی‌شده می‌خواهید

چگونه یک PDF امضا شده ثابت می‌کند که دستکاری نشده است؟

یک امضای دیجیتال PDF محتوای منطقی سند را امضا نمی‌کند؛ بلکه محدوده‌های بایت فایل فیزیکی را امضا می‌نماید. ورودی /ByteRange در دیکشنری امضا دقیقاً ثبت می‌کند که خلاصه رمزنگاری‌شده (cryptographic digest) کدام بخش‌های فایل را پوشش می‌دهد. هر عملیات ذخیره‌سازی که آن بایت‌ها را دوباره سریال‌سازی کند — حتی اگر سندی از نظر معنایی کاملاً یکسان تولید کند — خلاصه رمزنگاری را تغییر می‌دهد و هر اعتبارسنجی امضا را خراب گزارش خواهد کرد. این ویژگی بر اساس طراحی است: امضا صحت بایت‌هایی را که امضاکننده دیده است تایید می‌کند، نه یک مدل سند انتزاعی را

به‌روزرسانی‌های افزایشی راه فراری است که مشخصات PDF ارائه می‌دهد. از آنجا که یک ذخیره افزایشی، داده‌های جدید را بعد از %%EOF اصلی اضافه می‌کند و هرگز به محدوده‌های بایتی امضا شده دست نمی‌زند، اعتبار امضای موجود در برابر بایت‌هایی که پوشش می‌دهد حفظ می‌شود. اعتبارسنج‌ها سپس تغییرات ضمیمه‌شده را به طور جداگانه دسته‌بندی می‌کنند — امضای دوم، پر کردن فرم، یادداشت‌گذاری — و تصمیم می‌گیرند که آیا این تغییرات مجاز هستند یا خیر. هر جریان کاری با چند امضا به این موضوع بستگی دارد: هر امضاکننده یک بخش افزایشی را روی بخش قبلی اضافه می‌کند. اگر در حال ساخت خط لوله‌های امضا هستید، مقاله مکمل درباره امضا و اعتبارسنجی PAdES در دلفی نحوه تعامل محدوده‌های بایت امضا و بخش‌های افزایشی را با جزئیات پوشش می‌دهد

خواندن متادیتای امضا قبل از تایید هر چیز

متد GetLoadedSignatureInfo دیکشنری امضا و کانتینر CMS آن را بدون لمس حتی یک بایت از سند تجزیه می‌کند، که باعث می‌شود در مواقعی که فقط نیاز به نمایش نام امضاکننده و زمان امضا دارید، اولین فراخوانی مناسب باشد. فیلدهای امضا از 0 بر اساس ترتیب فیلدهای فرم اندیس‌گذاری می‌شوند و متد GetLoadedSignatureFieldCount تعداد آن‌ها را به شما می‌گوید. رکورد THPDFSignatureInfo برگشتی شامل نام فیلد، /SubFilter، نام عمومی گواهی امضاکننده، نام‌های متمایز کننده موضوع و صادرکننده، شماره سریال,تاریخ‌های اعتبار، زمان امضا (از ویژگی امضا شده در صورت وجود، در غیر این صورت ورودی /M دیکشنری)، نام الگوریتم خلاصه و رشته‌های /Reason، /Location و /ContactInfo است. عضو Status آن روی svNotVerified باقی می‌ماند که برچسبی صادقانه برای "تجزیه‌شده، اما بررسی‌نشده" است

var
  Pdf: THotPDF;
  Info: THPDFSignatureInfo;
  I: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.LoadFromFile('signed-contract.pdf');
    for I := 0 to Pdf.GetLoadedSignatureFieldCount - 1 do
    begin
      Info := Pdf.GetLoadedSignatureInfo(I);
      Writeln('Field:     ', Info.FieldName);
      Writeln('Signer:    ', Info.SignerName);
      Writeln('Issuer:    ', Info.IssuerDN);
      Writeln('Algorithm: ', Info.HashAlgorithm);
      Writeln('SubFilter: ', Info.SubFilter);
    end;
  finally
    Pdf.Free;
  end;
end;

اجرای بررسی رمزنگاری

متد VerifyLoadedSignatureEx تایید کامل یک سند بارگذاری‌شده از فایل را انجام می‌دهد و رکورد اطلاعات پر شده را در یک فراخوانی برمی‌گرداند: فایل منبع را دوباره باز می‌کند، بخش‌های /ByteRange را با الگوریتم خلاصه SignerInfo هش می‌نماید، نتیجه را با ویژگی امضا شده messageDigest (استاندارد RFC 5652 §5.4) مقایسه می‌کند و سپس تاییدیه RSA را روی کدگذاری مجدد DER SET ویژگی‌های امضا شده اجرا می‌کند. هنگامی که یک امضا فاقد ویژگی‌های امضا شده باشد، بررسی RSA مستقیماً روی هش سند اجرا می‌شود. امضاهای پشتیبانی‌شده عبارتند از RSA PKCS#1 v1.5 با خلاصه‌های SHA-1، SHA-256,SHA-384 یا SHA-512، که شامل ساب‌فیلترهای adbe.pkcs7.detached و ETSI.CAdES.detached تولید شده توسط ابزارهای امضای رایج می‌شود

var
  Status: THPDFSignatureVerifyStatus;
  Info: THPDFSignatureInfo;
begin
  Status := Pdf.VerifyLoadedSignatureEx(0, Info);
  case Status of
    svValid:
      if Info.CoversWholeDocument then
        Writeln('Valid; signature covers the whole file')
      else
        Writeln('Valid; file was extended after signing');
    svDigestMismatch:
      Writeln('Document bytes changed after signing');
    svSignatureInvalid:
      Writeln('RSA check failed over signed attributes');
    svUnsupportedAlgorithm:
      Writeln('Non-RSA key or unknown digest algorithm');
    svMalformed:
      Writeln('CMS container could not be parsed');
    svSourceUnavailable:
      Writeln('No source bytes; use the TStream overload');
  end;
end;

دانستن دو جزئیات پیاده‌سازی ارزشمند است زیرا شکست‌هایی را توضیح می‌دهند که از بیرون مرموز به نظر می‌رسند. اول اینکه، بررسی ویژگی‌های امضا شده نسبت به کدگذاری بسیار حساس است: در داخل فایل ویژگی‌ها با تگ [0] IMPLICIT مشخص می‌شوند، اما امضا روی فرم DER SET OF آن‌ها محاسبه شده است، بنابراین تاییدکننده قبل از هش کردن تگ‌ها را تغییر می‌دهد، دقیقاً همان‌طور که استاندارد RFC 5652 §5.4 ایجاب می‌کند. یک تاییدکننده دستی که بایت‌ها را همان‌طور که در فایل ظاهر می‌شوند هش می‌کند، هر سند به درستی امضا شده را رد خواهد کرد. دوم اینکه، /Contents به طور سنتی با صفرهای اضافی تا یک بافر بایت رزرو شده پر می‌شود، بنابراین تاییدکننده قبل از تجزیه، باینری DER را به طول واقعی SEQUENCE بیرونی آن کوتاه می‌کند؛ صفرهای اضافی انتهایی که شبیه به خرابی به نظر می‌رسند، طبیعی هستند و به معنی خرابی فایل نیستند. همین خانواده از خطرات تجزیه ASN.1 در سمت وارد کردن گواهی، موضوع مقاله امنیت و مقاوم‌سازی PKCS#12 و ASN.1 در HotPDF است

یک امضای معتبر در واقع چه چیزی را تضمین می‌کند؟

یک امضای معتبر در واقع چه چیزی را تضمین می‌کند؟

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

پرچم CoversWholeDocument از یک شکاف ظریف‌تر محافظت می‌کند. یک امضا فقط /ByteRange خود را پوشش می‌دهد و سازوکار به‌روزرسانی افزایشی PDF اجازه می‌دهد محتوایی را بعد از یک امضا بدون باطل کردن آن ضمیمه کنید، که این بر اساس طراحی است و نحوه عملکرد جریان‌های کاری چند امضایی است. این پرچم در طول تایید محاسبه می‌شود و تنها زمانی درست است که دو بخش به علاوه شکاف /Contents کل فایل را پوشش دهند. هنگامی که svValid با مقدار نادرست CoversWholeDocument بازگردانده می‌شود، نسخه امضا شده دست‌نخورده است اما فایل شامل اضافات بعدی است، و اینکه آن اضافات چه چیزی را تغییر داده‌اند تصمیمی است که جریان کاری شما باید درباره تحمل آن تصمیم بگیرد

اسناد بارگذاری‌شده از جریان و رمزگذاری‌شده به بایت‌های منبع خود نیاز دارند

متدهای بدون پارامتر VerifyLoadedSignature and VerifyLoadedSignatureEx به این بستگی دارند که کامپوننت به خاطر آورد سند از کدام فایل آمده است. اگر سند را از یک جریان بارگذاری کنید، نام فایلی برای باز کردن مجدد وجود ندارد؛ همین موضوع پس از مسیر بارگذاری مجدد با رمز عبور که برای اسناد رمزگذاری‌شده استفاده می‌شود نیز صادق است، یعنی جریان کاری که در مقاله رمزگذاری AES-256 در PDF با HotPDF توضیح داده شده است. در هر دو حالت، متدهای بارگذاری‌شده از فایل به جای حدس زدن، مقدار svSourceUnavailable را برمی‌گردانند. راه‌حل استفاده از متد بارگذاری‌شده با TStream است که به شما امکان می‌دهد بایت‌های خام اصلی را از هر کجا که نگه داشته‌اید (فایلی که هنوز دارید، یک بافر حافظه یا یک باینری پایگاه داده) تحویل دهید

var
  Src: TFileStream;
  Status: THPDFSignatureVerifyStatus;
  Info: THPDFSignatureInfo;
begin
  // سند بارگذاری‌شده از جریان: کامپوننت نام فایل منبع را نگه
  // نمی‌دارد، بنابراین بایت‌های اصلی را خودتان ارائه دهید.
  Src := TFileStream.Create('signed-contract.pdf',
    fmOpenRead or fmShareDenyWrite);
  try
    Status := Pdf.VerifyLoadedSignature(0, Src, Info);
    if Status <> svValid then
      Writeln('Verification failed: ', Ord(Status));
  finally
    Src.Free;
  end;
end;

گزارش مواردی که نمی‌توانید تایید کنید

تاییدکننده‌ای که فقط "معتبر" و "نامعتبر" را می‌شناسد، اسنادی را که صرفاً درک نمی‌کند به اشتباه گزارش می‌دهد، بنابراین این شمارش وضعیت، مواردی را که رابط کاربری شما باید متمایز کند جدا می‌سازد. svDigestMismatch به این معنی است که بایت‌های سند پس از امضا تغییر کرده‌اند که این نشانه کلاسیک دستکاری است. svSignatureInvalid یعنی بایت‌ها به درستی هش می‌شوند اما بررسی RSA شکست خورده است، که به مقدار امضای خراب یا جعلی اشاره دارد. svUnsupportedAlgorithm پاسخ صادقانه برای کلیدهای ECDSA و خلاصه‌های ناشناخته است: امضا ممکن است کاملاً خوب باشد، اما HotPDF به سادگی نمی‌تواند آن را بررسی کند و گزارش آن به عنوان "نامعتبر" به یک سند سالم آسیب می‌زند. svMalformed نشان‌دهنده یک کانتینر CMS است که اصلاً قابل تجزیه نبوده است. برای بررسی‌های گیت‌مانند، متد VerifyAllLoadedSignatures تنها زمانی true را برمی‌گرداند که حداقل یک فیلد امضا وجود داشته باشد و تک‌تک آن‌ها به عنوان svValid تایید شوند، که این یک مقدار بولی راحت برای خط لوله دریافت آرشیو است که کمتر از این را نمی‌پذیرد

تایید امضا، امضای PAdES، رمزگذاری AES-256 و API ویرایش سند بارگذاری‌شده همگی در یک کتابخانه بومی VCL برای دلفی و C++Builder، بدون وابستگی‌های DLL خارجی ارائه می‌شوند؛ لیست کامل ویژگی‌ها و نسخه‌های IDE پشتیبانی‌شده در صفحه محصول کامپوننت HotPDF قرار دارد