التوقيع على نطاق واسع، دون تنازل
Spec: ISO 32000-2, §12.8ISO 32000-2 §12.8Spec: ETSI EN 319 142-1ETSI EN 319 142-1Spec: RFC 5652, §5.1RFC 5652 §5.1
لمحة سريعة
قسم بعنوان «لمحة سريعة»توقيع مستند واحد عملية تشفيرية. أما توقيع مئة ألف مستند في موعد نهائي فهو العملية نفسها، مُكرَّرة، حيث لم يعد الإخفاق الخطير “كان بطيئًا” بل “خرج أحدها غير موقَّع ولم يلاحظ أحد”. تدور هذه الصفحة حول فعل الأمر الثاني دون التخلّي عن الأول: توقيع كميّ ومتزامن يبقى فيه كل توقيع صحيحًا، ويرفض فيه التشغيل إصدار ملف تعذّر عليه توقيعه، وتستأنف فيه الدفعة الكبيرة بدلًا من البدء من جديد.
لماذا يهمّ هذا
قسم بعنوان «لماذا يهمّ هذا»التوقيع حقيقة خاصّة بكل مستند. يُحسَب ملخّصه على نطاق بايتات مُعلَن يتخطّى قيمة التوقيع نفسها (Spec: ISO 32000-2, §12.8ISO 32000-2 §12.8)، فلا توجد طريقة صادقة لتوقيع ألف مستند “كدفعة” بضربة واحدة — إذ يحمل كلٌّ منها كائن CMS SignedData خاصًّا به على بايتاته الخاصّة (Spec: RFC 5652, §5.1RFC 5652 §5.1). لذا يضاعف الحجم فرص أن يسير شيء واحد بالضبط على نحو خاطئ بصمت: مَقبِض مفتاح أخفق لوهلة، أو سلطة طوابع زمنية انتهت مهلتها، أو عامل مات وهو يحمل ملفًا مكتوبًا نصفه.
النتيجة الباهظة ليست انهيارًا. الانهيار صاخب وتعيد المحاولة عليه. أما النتيجة الباهظة فهي نتيجة صامتة — ملف PDF غير موقَّع يبدو منجَزًا قابعًا في أرشيف، يكتشفه بعد أشهر مدقّق تابع لمراجِع. على نطاق واسع، يتعذّر تمييز “موقَّع في معظمه” عن “موقَّع” إلى أن يُفحَص الملف المهمّ. والغاية كلّها من التوقيع على نطاق واسع جعل تلك النتيجة مستحيلة بنيويًّا، لا نادرة إحصائيًّا.
النسخة المختصرة
قسم بعنوان «النسخة المختصرة»- كل مستند يُوقَّع منفردًا، على نطاق بايتاته الخاصّ. الدفعة كلمة جدولة، لا كلمة تشفيرية. لا يوجد توقيع مشترك.
- المستوى عقد، لا تلميح. تُسمّي مستوى خط أساس PAdES وينتج المحرّك ذلك المستوى بالضبط لكل مستند، أو يُخفق ذلك المستند بصوت عالٍ (Spec: ETSI EN 319 142-1ETSI EN 319 142-1).
- خطّ المعالجة يفشل مغلقًا. المستند الذي يتعذّر توقيعه بصورة صحيحة لا يمرّ بوصفه بايتات عادية. بل يُحتجَز، ولا يُمرَّر.
- التزامن خاصّ بكل مستند، وآمن بالبناء. وحدات التوقيع لا تتشارك حالة قابلة للتغيير، فلا يستطيع عاملان إفساد مخرج أحدهما الآخر.
- التشغيلات الكبيرة دائمة. المخرَج المُلتزَم لا يُعاد إصداره عند الاستئناف؛ والتشغيل المنهار يواصل من آخر نقطة تفتيش بدلًا من إعادة توقيع كل شيء.
كيف يتعامل NextPDF مع ذلك
قسم بعنوان «كيف يتعامل NextPDF مع ذلك»يقوم التصميم على فصل واحد: إنتاج التوقيع خطوة صغيرة حتمية خاصّة بكل مستند؛ وتشغيل آلاف منها بأمان خطوة تنسيق. والإبقاء على هاتين منفصلتين هو ما يجعل كلًّا منهما تبقى بسيطة.
خطوة التوقيع هي التي يجب ألا تتنازل أبدًا. تطلب مستوى —
حالة تعداد SignatureLevel، لا سلسلة نصية على المحرّك تفسيرها — ويُعامَل
ذلك المستوى بوصفه عقدًا لـذلك المستند. ينتج المحرّك المستوى
المطلوب أو يتوقّف بخطأ قابل للمعالجة؛ ولا يوقّع بصمت
عند مستوى أدنى ثم يدع سجلًّا يدّعي مستوى أعلى. والصحّة لا تتراخى
لأن وراء هذا المستند مستندات أكثر. فالتوقيع المئة ألف يُحسَب
بالعناية نفسها التي يُحسَب بها الأول تمامًا.
قاعدة الفشل المغلق هي ما يجعل ذلك جديرًا بالثقة على نطاق واسع. يرفض مسار
التوقيع في NextPDF إصدار قطعة تبدو معقولة لكنها غير موقَّعة بدلًا من
القطعة التي طلبتها. والمسار التطبيقي المدعوم هو واجهة Document
البرمجية عالية المستوى: تهيّئ التوقيع بـDocument::setSignature() ثم تطلب
البايتات بـDocument::getPdfData() (أو save() / output())، وذلك
المرور الكتابي الواحد إمّا يُصدِر ملف PDF موقَّعًا بصورة صحيحة أو يطرح قبل إعادة
البايتات — لا ملفًا غير موقَّع يظنّه المستدعي موقَّعًا. ومطبَّقة عبر
دفعة، هذه هي القاعدة التي تحوّل “أفلت أحدها غير موقَّع” من
عيب كامن صامت إلى مهمّة واحدة مُخفِقة قابلة لإعادة المحاولة.
- Warm the signing material onceعند إقلاع العامل، افتح مصدر المفتاح/الشهادة وعميل الطابع الزمني. تُدفَع هذه الكلفة مرّة واحدة لكل عامل، لا مرّة لكل مستند.
- Enqueue the documentsيحتجز طابور المهامّ الخاصّة بكل مستند. الطابور هو مِقبَض الإنتاجية — يتوسّع عمّال التوقيع أفقيًّا خلفه.
- Render and sign one documentتعرض وحدة قابلة للتخلّص المستند، ثم توقّعه على نطاق بايتاته الخاصّ عند مستوى PAdES المطلوب. لا شيء يُتشارَك مع المستند التالي.
- Commit on success, hold on failureيُلتزَم الملف الموقَّع بصورة صحيحة مرّة واحدة. أما المستند الذي تعذّر توقيعه فيُخفَق ويُعاد — ولا يُصدَر قطّ بوصفه بايتات غير موقَّعة.
- Checkpoint, and resume on crashيسجّل التشغيل الدائم ما الذي التُزِم. وبعد الانهيار يواصل من آخر نقطة تفتيش بدلًا من إعادة توقيع الدفعة بأكملها.
تمنحك Core الصحّة التشفيرية: توقيع CMS برمجيًّا وPAdES B-B (مع B-T عبر عميل الطابع الزمني) حيث يُوقَّع كل مستند منفردًا ويفشل مغلقًا. أما التنسيق الذي يجعل التشغيل الكبير دائمًا ومتزامنًا ومرّة واحدة بالضبط — محرّك العرض الخالي من الأثر الجانبي إضافة إلى المُلتزِم، ومخازن نقاط التفتيش والتعطيل المتكرّر والرسائل المهملة — فهو وحدة Stream في الإصدارات المتقدّمة؛ والتوقيع المدعوم بالعتاد عبر HSM أو KMS سحابي كذلك حدّ من حدود الإصدارات المتقدّمة. تثبت Core أن كل توقيع صحيح؛ والإصدارات المتقدّمة تجعل مليونًا منها قابلًا للبقاء.
مثال عملي
قسم بعنوان «مثال عملي»الشكل أدناه هو وحدة التوقيع الخاصّة بكل مستند داخل حلقة دفعة. توقّع كل تكرارة مستندًا واحدًا عند مستوى مُسمّى وتُنتج إمّا نتيجة موقَّعة بصورة صحيحة أو تُخفِق تلك المهمّة الواحدة — ولا تُعيد قطّ بايتات غير موقَّعة متنكّرة في صورة نتيجة.
<?php
declare(strict_types=1);
use NextPDF\Contracts\DocumentFactoryInterface;use NextPDF\Security\Signature\CertificateInfo;use NextPDF\Security\Signature\SignatureLevel;use NextPDF\Exception\SignatureException;use Psr\Log\LoggerInterface;
/** * One signing-batch iteration: render, sign at a named level, commit or fail. * * The factory and the certificate source ($certInfo, the warmed signing * material) are process-lifetime singletons; the document is disposable. A * document that cannot be signed at the requested level fails this job loudly — * it is never committed unsigned. * * @param iterable<int, callable(\NextPDF\Core\Document): \NextPDF\Core\Document> $jobs */function signBatch( DocumentFactoryInterface $factory, CertificateInfo $certInfo, LoggerInterface $logger, iterable $jobs,): void { // The level is an explicit, ordered contract — not a flag we hope is honoured. $level = SignatureLevel::PAdES_B_T;
foreach ($jobs as $jobId => $build) { // Fresh, disposable unit — shares the warmed signing material only. $doc = $factory->create(); $doc = $build($doc);
try { // Sign over this document's own byte range, at exactly $level, // or throw. There is no "signed lower, reported higher" path. $doc->setSignature(certInfo: $certInfo, level: $level); $signed = $doc->getPdfData(); } catch (SignatureException $e) { // Fail-closed: this document does NOT continue as unsigned bytes. // The job is failed and left for retry / dead-letter handling. $logger->error('pdf.sign.failed', ['job_id' => $jobId, 'reason' => $e->getMessage()]); continue; }
// Only a correctly-signed result reaches the commit step. commitSignedOutput($jobId, $signed); unset($doc, $signed); // release per-document state before the next iteration
$logger->info('pdf.sign.committed', ['job_id' => $jobId, 'level' => $level->value]); }}catch هو السطر الحامل للحِمل. إنه الفرق بين تشغيل
يحتجز المستندات التي تعذّر توقيعها وتشغيل يُصدِرها رغم ذلك.
وcontinue لا يطمس الإخفاق — إذ تُسجَّل المهمّة وتُترَك لإعادة
المحاولة، فتنتهي الدفعة بقائمة معروفة كاملة بما وُقِّع وما لم
يُوقَّع، لا بفجوة صامتة قطّ.
سوء فهم شائع
قسم بعنوان «سوء فهم شائع»سوء الفهم الأول هو أن “التوقيع الدفعي” يعني توقيعًا واحدًا مطبَّقًا على ملفات كثيرة. وليس كذلك، وأيّ نظام يدّعي ذلك لا ينتج توقيعات PAdES صحيحة — إذ يُربَط ملخّص كل مستند ببايتاته الخاصّة (Spec: ISO 32000-2, §12.8ISO 32000-2 §12.8). الدفعة تتعلّق محضًا بـكم العدد ومدى السرعة، لا بمشاركة الوحدة التشفيرية قطّ.
والثاني هو أن التزامن يعني تخفيف الصحّة من أجل السرعة — أن الموقِّع السريع لا بدّ أن يقطع زاوية لا يقطعها الموقِّع المتأنّي. وليس كذلك. لأن وحدات التوقيع لا تتشارك حالة قابلة للتغيير، فتشغيلها بالتوازي يغيّر الجدولة، لا البايتات. ويُحسَب كل توقيع متوازٍ بالدقّة نفسها التي يُحسَب بها التوقيع المفرد؛ والتوازي في التنسيق المحيط بها.
والثالث هو أن الديمومة شيء تلحقه بعد أول تشغيل ليلي مُخفِق. فعندئذٍ تكون قد خسرت التشغيل أصلًا. خطّ معالجة قابل للاستئناف عليه أن يعرف، لكل مستند، ما الذي التُزِم وما الذي لم يُلتزَم قبل الانهيار — وهو بالضبط ما توجد مخازن نقاط التفتيش والتعطيل المتكرّر لتسجيله.
الحدود والقيود
قسم بعنوان «الحدود والقيود»- كل توقيع خاصّ بمستند ومرتبط بالمعايير؛ لا اختصار للدفعة. الحجم يغيّر الجدولة، لا الوحدة التشفيرية. يوقّع NextPDF كل مستند على نطاق بايتاته الخاصّ.
- تؤدّي Core توقيع CMS برمجيًّا وPAdES B-B (B-T عبر عميل طابع زمني). أما محرّك العرض والتوقيع الدائم المتزامن مرّة واحدة بالضبط فهو وحدة Stream في الإصدارات المتقدّمة؛ وحفظ المفاتيح المدعوم بـHSM/KMS حدٌّ من حدود الإصدارات المتقدّمة. لا تدّعي هذه الصفحة ذلك التنسيق ضمن Core.
- الفشل المغلق سلوك المحرّك، لا ضمان بشأن توصيلك.
يرفض NextPDF إصدار ملف غير موقَّع لكن يُظَنّ موقَّعًا ويُبرز
مسار التوقيع المدعوم. أما خطّ معالجة يلتقط الخطأ الناتج
ويلتزم رغم ذلك فقد اختار هزيمة الضمان — وهو التأطير الذي يوجد
catch/continueفي المثال لمنعه. - يُفرَض مستوى PAdES لكل مستند، لا يُصدَّق للتشغيل. ينتج المحرّك مستوى خط الأساس المطلوب أو يُخفِق؛ وذلك فرض بنيوي، لا حُكم مطابقة من طرف ثالث للملفات المنتَجة. أما تدرّج المستوى نفسه فمُغطّى في أنماط PAdES الأساسية.
- الطابور، وحفظ المفتاح، وسلطة الطوابع الزمنية، ومخزن الكائنات ملكك. يوفّر NextPDF صحّة التوقيع الخاصّة بكل مستند، وفي الإصدارات المتقدّمة، أوّليات التنسيق الدائم. وهو لا يشغّل بنيتك التحتية ولا يضمن سلطة طوابعك الزمنية.
| Edition | Availability |
|---|---|
| Core | توقيع CMS برمجي خاصّ بكل مستند، وPAdES B-B (B-T مع عميل طابع زمني)، مُوقَّع منفردًا على نطاق بايتات كل مستند الخاصّ، يفشل مغلقًا ضدّ المخرَج غير الموقَّع بصمت. لا يحتاج التوقيع العادي الخاصّ بكل مستند إلى أيّ فئة تجارية. |
| Pro | يضيف وحدة Stream: محرّك عرض خالٍ من الأثر الجانبي إضافة إلى مُلتزِم دائم، ومخازن نقاط تفتيش وتعطيل متكرّر ورسائل مهملة — تشغيلات دفعة متزامنة، آمنة عند الانهيار، مرّة واحدة بالضبط، تستأنف بدلًا من إعادة البدء. |
| Enterprise | يضيف حفظ المفاتيح المدعوم بالعتاد (HSM عبر PKCS#11، أو KMS سحابي) بحيث لا يغادر المفتاح الخاصّ الجهاز قطّ، ومستويات PAdES طويلة الأمد (B-LT، B-LTA) التي تُبقي أرشيفًا عالي الحجم قابلًا للتحقّق لعقود. |
مستندات ذات صلة
قسم بعنوان «مستندات ذات صلة»- توليد المستندات عالي الحجم — نموذج الدفعة المُطابِر بذاكرة محدودة الذي توقّع هذه الصفحة فوقه؛ اقرأه أولًا لانضباط الإنتاجية والقياس.
- أنماط PAdES الأساسية — ما الذي يضيفه كل مستوى (B-B إلى B-LTA)، كي توقّع عند المستوى الذي يحتاجه الالتزام.
- كيف تتموضع التوقيعات داخل ملف PDF — أساس نطاق البايتات والقاموس الذي يجعل التوقيع خاصًّا بكل مستند.
- التوقيع المدعوم بـHSM — أين يقع حدّ المفتاح الخاصّ حين تقيم مادة التوقيع في العتاد.
- Stream (Pro) — محرّك العرض الدائم المتزامن مرّة واحدة بالضبط الذي يحوّل وحدة توقيع واحدة إلى تشغيل قابل للاستئناف.
مسرد المصطلحات
قسم بعنوان «مسرد المصطلحات»- التوقيع الدفعي — توقيع مستندات كثيرة وفق جدولة. مفهوم جدولة؛ ومع ذلك يُوقَّع كل مستند منفردًا على بايتاته الخاصّة.
- الفشل المغلق — عند إخفاق كان سيُنتج مخرجًا غير موقَّع أو خاطئًا، يحتجز خطّ المعالجة المستند ويُبلِّغ، بدلًا من تمريره بوصفه بايتات عادية.
- الالتزام مرّة واحدة بالضبط — خاصّة خطّ معالجة دائم حيث يُنشَر المخرَج الموقَّع بصورة صحيحة مرّة واحدة ولا يُعاد إصداره عند استئناف تشغيل منهار.
- نقطة التفتيش — سجلّ دائم خاصّ بكل مستند لما الذي التُزِم، كي يستطيع تشغيل أن يواصل من حيث توقّف بدلًا من إعادة توقيع كل شيء.
- CMS
SignedData— الحاوية التشفيرية للتوقيعات على المحتوى (يمكنها حمل عدّة موقِّعين)؛ ينتج هذا الخطّ توقيع PDF لموقِّع واحد لكل مستند، الوحدة الخاصّة بكل مستند التي تنتجها الدفعة. - PAdES — التوقيعات الإلكترونية المتقدّمة لـPDF (PDF Advanced Electronic Signatures)، عائلة نمط ETSI EN 319 142 لتوقيع PDF؛ وتمتدّ مستويات خط أساسها من B-B إلى B-LTA.