مقاله فنی

امضای PDF پسا-کوانتومی و EdDSA با HotPDF در Delphi

HotPDF امضاهای CMS برای ML-DSA-44، ML-DSA-65، ML-DSA-87، Ed25519 و Ed448 را در اسناد PDF بارگذاری‌شده اعتبارسنجی می‌کند، و از طریق providerهای قابل تعویض امضا می‌کند تا کلید خصوصی هرگز مجبور نباشد درون پردازش Delphi شما زندگی کند. نیمه دوم همان بخشی است که اکثر تیم‌ها اول نیاز دارند. یک توکن سخت‌افزاری، یک سرویس امضای راه‌دور و یک کارت eID ملی همگی از تحویل دادن کلید امتناع می‌کنند، و تا زمانی که pipeline امضا از key store جدا نشده باشد، هیچ‌کدام قابل استفاده نیست

جدا کردن، نکته THPDFSignatureProvider است. HotPDF بخش‌هایی را که باید مالک باشد نگه می‌دارد — parse کردن CMS، ساخت SignedData، چیدمان /ByteRange — و آن یک عملیاتی را که نمی‌تواند مالک باشد واگذار می‌کند، که تبدیل یک digest به یک امضا با کلیدی است که اجازه دیدنش را ندارد. همه‌چیز در زیر از آن تقسیم پیروی می‌کند

چرا یک امضای ML-DSA معتبر در اعتبارسنجی شکست می‌خورد؟

چون HotPDF از ML-DSA روی یک سند بارگذاری‌شده که پسوند آن را اعلام نکرده امتناع می‌کند. ML-DSA — طرح امضای شبکه‌ای که به‌عنوان FIPS 204 استاندارد شده، و دلیلی که مردم می‌گویند «PDF پسا-کوانتومی» — هنوز هیچ ثبت ISO 32000-2 ندارد. PDFای که یکی را حمل می‌کند از الگوریتمی استفاده می‌کند که استاندارد پایه نام نمی‌برد، و فایلی که خاموش از یک الگوریتم نام‌برده‌نشده استفاده می‌کند فایلی است که حکمش را هیچ‌کس دیگری نمی‌تواند بازتولید کند

پس HotPDF ادعا را صریح می‌کند. EnsureMLDSAExtensions سند را در صورت اجازه به PDF 2.0 ارتقا می‌دهد و /Extensions /HotPDF << /BaseVersion /2.0 /ExtensionLevel 1 >> را در کاتالوگ می‌نویسد. در طرف خواندن، LoadedDocumentDeclaresMLDSAExtension گزارش می‌دهد آیا آن اعلان دوام آورده، و VerifyLoadedSignatureWithOptions همان آزمون را قبل از اینکه Options.AllowMLDSA را اجابت کند اعمال می‌کند. flag را روی یک سند اعلام‌نشده تنظیم کنید و خاموش می‌ماند — گزینه می‌تواند خط‌مشی را شل کند، نه الزام ساختاری را

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'contract-pq.pdf';
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Arial', [], 11);
    Pdf.CurrentPage.TextOut(50, 720, 0, 'Supply agreement 2026-114');
    Pdf.EnsureMLDSAExtensions;   // declare before the signature is written
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

قبل از ذخیره فراخوانی‌اش کنید، نه بعد. اعلان بخشی از بازه بایت امضاشده است، و یک کاتالوگ که بعد وصله‌شود یا یک تغییر امضانشده به یک فایل امضاشده است یا یک revision دوم که یک اعتبارسنج آن را به‌عنوان یک تغییر گزارش می‌کند

سه خانواده الگوریتم، یک نقطه ورودی اعتبارسنجی

هر سه خانواده از طریق VerifyLoadedSignatureWithOptions می‌رسند، که یک اندیس امضا، stream منبع، یک رکورد THPDFCMSVerifyOptions و یک پارامتر out برای جزئیات امضا می‌گیرد. رکورد دقیقاً سه فیلد دارد، و هر یکی سؤالی را پاسخ می‌دهد که قبلاً نیاز به بازسازی داشت

SignatureProvider provider خودتان را به‌جای provider پلتفرمی داخلی جایگزین می‌کند. OpenSSLLibraryPath یک کتابخانه OpenSSL 3 را انتخاب می‌کند، که همان چیزی است اعتبارسنجی حالت‌خالص Ed25519 و Ed448 را فراهم می‌کند که Windows CNG همه‌جا ارائه نمی‌دهد. AllowMLDSA به الگوریتم‌های شبکه‌ای می‌پیوندد، مشروط به بررسی پسوند بالا. OID دقیق الگوریتمی که شناسایی شده در THPDFSignatureInfo.SignatureAlgorithmOID برمی‌گردد، پس یک گزارش ممیزی می‌تواند ثبت کند چه تأیید شده نه چه درخواست شده

var
  Opts: THPDFCMSVerifyOptions;
  Info: THPDFSignatureInfo;
  Status: THPDFSignatureVerifyStatus;
  Src: TFileStream;
begin
  Opts := THPDFCMSVerifyOptions.Default;
  Opts.OpenSSLLibraryPath := 'C:\openssl3\libcrypto-3-x64.dll';
  Opts.AllowMLDSA := Pdf.LoadedDocumentDeclaresMLDSAExtension;
  Src := TFileStream.Create('contract-pq.pdf', fmOpenRead or fmShareDenyWrite);
  try
    Status := Pdf.VerifyLoadedSignatureWithOptions(0, Src, Opts, Info);
    if Status = svValid then
      Memo1.Lines.Add('signed with OID ' + string(Info.SignatureAlgorithmOID));
  finally
    Src.Free;
  end;
end;

Ed25519 و Ed448 هیچ اعلان پسوندی نیاز ندارند، چون ISO 32000-2 از قبل آن‌ها را می‌پذیرد. آن‌ها به providerی نیاز دارند که آن‌ها را پیاده‌سازی کند، که در اکثر استقرارهای Windows یعنی نشانه‌گذاری OpenSSLLibraryPath به کتابخانه‌ای که شما حمل می‌کنید و کنترل می‌کنید به‌جای هر چیزی که اتفاقاً روی ماشین باشد

یک provider امضا واقعاً چه چیزی را قول می‌دهد؟

یک provider یک چیز را قول می‌دهد: با توجه به یک درخواست، یک وضعیت برگردان و، هنگام امضا، بایت‌ها. THPDFSignatureProviderRequest الگوریتم و OID آن، OID digest، طول salt برای PSS، اینکه آیا ورودی یک پیام است یا یک digest از پیش محاسبه‌شده، ورودی خودش، کلید عمومی یا گواهی، یک شناسه کلید و یک شناسه عملیات را حمل می‌کند. هیچ‌چیز در آن رکورد HotPDF-اختصاصی نیست — واژگانی است که یک درایور توکن یا یک سرویس امضا از قبل صحبت می‌کند

سه پیاده‌سازی با کتابخانه می‌آیند. THPDFCallbackSignatureProvider متدهای ناشناس را می‌پیچد، که کوتاه‌ترین مسیر از یک روتین امضای داخلی موجود به یک امضای PDF کارآمد است. THPDFRemoteSignatureProvider یک callback انتقال را با یک حد retry، یک ثبت لغو و کران‌هایی روی اندازه ورودی و امضا می‌پیچد، تا یک HSM هنگ‌شده نتواند به یک کاربرد هنگ‌شده تبدیل شود. THPDFPKCS11SignatureProvider عملیات RSA را در برابر یک نشست PKCS#11 متعلق به فراخواننده و از قبل تأیید‌شده و یک handle کلید خصوصی سریال می‌کند — HotPDF هرگز login نمی‌کند، هرگز PIN نمی‌بیند، و هرگز نشستی را که باز نکرده بسته نمی‌کند

var
  Provider: THPDFRemoteSignatureProvider;
begin
  Provider := THPDFRemoteSignatureProvider.Create(
    function(const Req: THPDFSignatureProviderRequest; Attempt: Integer;
      out Signature: TBytes): THPDFSignatureProviderStatus
    begin
      // POST Req.Input to the signing service; Req.KeyIdentifier selects the key
      if PostToSigningService(Req.KeyIdentifier, Req.Input, Signature) then
        Result := spsValid
      else
        Result := spsProviderError;
    end,
    3,          // RetryLimit
    1048576,    // MaxInputBytes
    65536);     // MaxSignatureBytes
  try
    // hand Provider to the signing call
  finally
    Provider.Free;
  end;
end;

چرا enum وضعیت شش مقدار دارد به‌جای یک boolean

THPDFSignatureProviderStatus میان spsValid، spsInvalid، spsUnsupported، spsMalformed، spsProviderError و spsCancelled تمایز قائل می‌شود، و فروپاشی آن‌ها توانایی شما را در عمل درست از دست می‌دهد. امضایی که از نظر رمزنگاری اشتباه است (spsInvalid) یک رویداد امنیتی است. الگوریتمی که provider پیاده‌سازی نمی‌کند (spsUnsupported) یک شکاف استقرار است. یک شکست انتقال (spsProviderError) ارزش retry دارد، و یک اعلان توکن لغو‌شده توسط کاربر (spsCancelled) اصلاً ارزش retry ندارد

قانون برای امضا باریک است: یک provider امضا تنها با یک امضای غیرخالی spsValid برمی‌گرداند. providerهای اعتبارسنجی spsValid یا spsInvalid برمی‌گردانند، و چهار مورد دیگر در هر دو مسیر متمایز باقی می‌مانند. اگر provider می‌نویسید، در برابر وسوسه مقاومت کنید که هر چیزی را که نمی‌شناسید به spsInvalid نگاشتید — این یک DLL مفقود را به گزارشی تبدیل می‌کند که امضای مشتری جعل شده

امضا واقعاً در فایل کجا فرود می‌آید

دو تابع providerها را به بایت‌های واقعی PDF وصل می‌کند. HPDFCMSBuildSignedDataWithProvider از یک digest SHA-256 سند CMS جداگانه می‌سازد، که نقطه ورودی درستی است وقتی workflow شما digest را جای دیگری محاسبه می‌کند. HPDFCMSSignPDFStreamWithProvider یک جایگاه‌دار امضای موجود در یک stream PDF را امضا می‌کند و pipeline استاندارد /ByteRange را حفظ می‌کند، که نقطه ورودی درستی است وقتی HotPDF خود جایگاه‌دار را چیده

حفظ آن pipeline بیشتر از آنچه به‌نظر می‌رسد اهمیت دارد. قرارداد /ByteRange — دو بازه که پنجره امضای hex را رد می‌کنند — همان چیزی است که هر اعتبارسنجی اول بررسی می‌کند، و یک مسیر مبتنی بر provider که آن را بازنویسی می‌کرد انطباق PAdES را بدون توجه به استواری رمزنگاری می‌شکست. HotPDF چیدمان را با مسیر امضای داخلی یکسان نگه می‌دارد، تا سندی که از طریق یک توکن PKCS#11 امضا شده با همان کد اعتبارسنجی امضا به‌عنوان یکی که از یک فایل PFX امضا شده تأیید می‌شود. برای قواعد profile که بالای انتخاب الگوریتم می‌نشینند، مرور امضاهای baseline پایدار PAdES در Delphi را ببینید، و برای تله‌های رمزگذاری خاص ECDSA که این مدل provider را پیش‌تر می‌نوشتند، یادداشت‌های اعتبارسنجی CMS برای ECDSA و فرمت‌های امضای P1363

ترتیبی برای مهاجرت که اسناد شما را رها نمی‌کند

آمادگی پسا-کوانتومی یک مسئله زمان‌بندی است، نه یک کلید. تقریباً هیچ viewer از PDF مستقر شده امروز ML-DSA را اعتبارسنجی نمی‌کند، پس سندی که تنها با آن امضا شده از دید خواننده سندی با امضای غیرقابل‌اعتبارسنجی است. ترتیبی که با آرشیوهای واقعی تماس پیدا می‌کند زنده می‌ماند این است: RSA یا ECDSA را به‌عنوان امضایی که یک اعتبارسنجی قضاوت می‌کند نگه دارید، اعلان پسوند و یک امضای ML-DSA دوم را جایی که یک خط‌مشی به شواهد مقاوم در برابر کوانتومی نیاز دارد اضافه کنید، و امضای اصلی را تنها زمانی حرکت دهید که سیستم‌های مصرف‌کننده به‌روز شده باشند

آنچه HotPDF امروز به شما می‌دهد توانایی نوشتن و اعتبارسنجی هر دو از همان کد است، با الگوریتمی که به‌صورت صادقانه در فایل و در نتیجه اعتبارسنجی ثبت شده. HotPDF یک جزء VCL بومی PDF برای Delphi و C++Builder بدون هیچ runtime خارجی PDF است، تا مسیرهای امضا و اعتبارسنجی درون اجرایی شما حرکت می‌کنند به‌جای کنار آن — صفحه جزء Delphi PDF برای HotPDF را برای لیست کامل ویژگی‌ها و دانلود آزمایشی ببینید