تخطَّ إلى المحتوى

استكشاف أخطاء التوقيع والطابع الزمني وإصلاحها

تغطي هذه الصفحة إخفاقات التوقيع التي يُطلقها NextPDF\Exception\SignatureException وNextPDF\Security\Signature\Exception\SignatureLevelUnreachableException. يذكر كل مدخل طريقة المصنع أو الفئة باسمها الدقيق، حتى تتمكن من تأكيد السبب من الرسالة ومن getContext() بدلًا من استنتاجه.

ملاحظة عن الصياغة: لا يؤكد المحرك أن التوقيع صالح أو أن المستند محمي. بل يُبلّغ عن الإخفاق الذي اكتشفه. تعامَل مع كل حلّ بوصفه خطوة لإزالة سبب مُبلَّغ عنه.

مدخل: تعذّر إنتاج مستوى ⁨PAdES⁩ “⁨B-LT⁩” أو “⁨B-LTA⁩”

قسم بعنوان «مدخل: تعذّر إنتاج مستوى ⁨PAdES⁩ “⁨B-LT⁩” أو “⁨B-LTA⁩”»
  • العَرَض. SignatureException وتنتهي الرسالة بالجزء nextpdf/enterprise package is required for B-LT/B-LTA signatures.
  • السبب المُرجَّح. مزوّد قدرة التحقق طويل الأمد غير متوفر. يتضمن مستويا ⁨B-LT⁩ و⁨B-LTA⁩ مادة الإلغاء وطابعًا زمنيًا للأرشفة، ويأتي ذلك المزوّد ضمن nextpdf/enterprise.
  • الدليل / التشخيص. يُنتج المصنع SignatureException::ltvCapabilityMissing() هذه الرسالة بالضبط. يُعيد getContext() الحقل signature_level مضبوطًا على المستوى الذي حاولته.
  • الحلّ.
    1. ثبّت المزوّد بتشغيل composer require nextpdf/enterprise.
    2. شغّل استدعاء التوقيع مرة أخرى.
    3. إذا تعذّر عليك تثبيت المزوّد، فاطلب B-B أو B-T بدلًا من ذلك؛ فحزمة ⁨core⁩ تستطيع إنتاجهما.
  • ذات صلة. مرجع الاستثناءات.

مدخل: مستوى التوقيع غير قابل للبلوغ ويُرفض الاستدعاء

قسم بعنوان «مدخل: مستوى التوقيع غير قابل للبلوغ ويُرفض الاستدعاء»
  • العَرَض. SignatureLevelUnreachableException مع رسالة بالصيغة PAdES level "<x>" is unreachable (highest achievable: "<y>").
  • السبب المُرجَّح. يتطلب مستوى المطابقة المطلوب بنية تحتية غير متاحة وقت التوقيع، وغالبًا ما تكون هيئة طابع زمني للمستوى ⁨B-T⁩ وما فوقه. يُخفق المحرك بأمان: فهو لا يخفض المستوى بصمت ثم يُعلن عن مستوى أعلى.
  • الدليل / التشخيص. يُعيد getContext() الحقول requestedLevel وhighestAchievableLevel وreason. يحدّد الحقل reason الفجوة في البنية التحتية. وهذا هو السلوك الافتراضي للإخفاق الآمن الذي أُدخِل لمنع مستند يدّعي مستوى لا يستوفيه.
  • الحلّ.
    1. اقرأ الحقل reason لتحديد البنية التحتية المفقودة.
    2. وفّر المكوّن المفقود. على سبيل المثال، اضبط هيئة طابع زمني، ثم شغّل الاستدعاء مرة أخرى.
    3. لقبول مستوى أدنى عن قصد، مرّر allowDegradation: true إلى PadesOrchestrator. سينتج الاستدعاء عندئذٍ highestAchievableLevel ويُبلّغ عن المستوى الذي أنتجه.
  • ذات صلة. التشفير والأذونات.

مدخل: عميل هيئة الطابع الزمني مطلوب لكنه غير متوفر

قسم بعنوان «مدخل: عميل هيئة الطابع الزمني مطلوب لكنه غير متوفر»
  • العَرَض. SignatureException وتنتهي الرسالة بالجزء TSA client is required for level <x> but none was provided.
  • السبب المُرجَّح. يحتاج طلب ⁨B-T⁩ أو ⁨B-LT⁩ أو ⁨B-LTA⁩ إلى عميل هيئة طابع زمني، وليس لدى المنسّق أي عميل منها.
  • الدليل / التشخيص. يُنتج المصنع SignatureException::tsaRequired() هذه الرسالة؛ ويحمل getContext() الحقل signature_level الذي جرت محاولته.
  • الحلّ.
    1. اضبط عميل هيئة طابع زمني ومرّره إلى المنسّق.
    2. أعد تشغيل الاستدعاء.
    3. لإنتاج مستوى لا يحتاج إلى طابع زمني، اطلب B-B.
  • ذات صلة. مرجع الاستثناءات.

مدخل: عنوان ⁨URL⁩ لنقطة نهاية هيئة الطابع الزمني فارغ

قسم بعنوان «مدخل: عنوان ⁨URL⁩ لنقطة نهاية هيئة الطابع الزمني فارغ»
  • العَرَض. SignatureException وتنتهي الرسالة بالجزء TSA endpoint URL is empty.
  • السبب المُرجَّح. أُنشئ عميل هيئة الطابع الزمني بعنوان ⁨URL⁩ فارغ لنقطة النهاية.
  • الدليل / التشخيص. يُنتج المصنع SignatureException::tsaUrlEmpty() هذه الرسالة. هذا عيب في الإعداد، وليس إخفاقًا في الشبكة.
  • الحلّ.
    1. عيّن عنوان ⁨URL⁩ غير فارغ لنقطة النهاية في عميل هيئة الطابع الزمني، مثل https://timestamp.example.com/tsa.
    2. إذا كان المستوى المطلوب لا يتطلب الطابع الزمني، فأزِل ربط عميل هيئة الطابع الزمني بدلًا من ذلك.
    3. شغّل الاستدعاء مرة أخرى.
  • ذات صلة. مرجع الاستثناءات.

مدخل: العنصر النائب للتوقيع مفقود من المخزن المؤقت

قسم بعنوان «مدخل: العنصر النائب للتوقيع مفقود من المخزن المؤقت»
  • العَرَض. SignatureException وتنتهي الرسالة بالجزء no /Contents <…> field found in PDF buffer (signature placeholder missing).
  • السبب المُرجَّح. تلقّت مرحلة التوقيع مخزنًا مؤقتًا من دون حاوية توقيع محجوزة، فلم يعد لديها مكان لكتابة التوقيع.
  • الدليل / التشخيص. يُنتج المصنع SignatureException::signatureContentsNotFound() هذه الرسالة.
  • الحلّ.
    1. تأكّد من كتابة حقل التوقيع وعنصره النائب قبل تشغيل مرحلة التوقيع.
    2. أعد تشغيل خط الأنابيب حتى يكون العنصر النائب موجودًا عند بدء التوقيع.
  • ذات صلة. مرجع الاستثناءات.

مدخل: حالة الإلغاء غير معروفة (رفض مُجيب ⁨OCSP⁩)

قسم بعنوان «مدخل: حالة الإلغاء غير معروفة (رفض مُجيب ⁨OCSP⁩)»
  • العَرَض. SignatureException وتنتهي الرسالة بالجزء OCSP responder returned non-successful OCSPResponseStatus "<status>".
  • السبب المُرجَّح. لم يُعِد مُجيب بروتوكول حالة الشهادة عبر الإنترنت (⁨OCSP⁩) حالة successful، فلم يُنتِج أي تأكيد إلغاء. يتّبع المحرك ⁨RFC 6960⁩ §4.2.1، كما يرد في المصدر: لا يُسمح بجسم استجابة مملوء إلا لحالة successful (0). يرفض المحرك التعامل مع استجابة مرفوضة بوصفها نتيجة ثقة موجبة.
  • الدليل / التشخيص. يُنتج المصنع SignatureException::nonSuccessfulOcspResponseStatus() هذه الرسالة ويُسمّي الحالة المُبلَّغ عنها، مثل tryLater أو internalError. أما بايت حالة محجوز أو غير معروف، فيُنتج بدلًا من ذلك SignatureException::reservedOcspResponseStatus().
  • الحلّ.
    1. حدّد الحالة في الرسالة. بالنسبة إلى حالة عابرة مثل tryLater، أعد محاولة جلب الإلغاء لاحقًا.
    2. بالنسبة إلى unauthorized أو malformedRequest، تحقّق من عنوان ⁨URL⁩ لطلب ⁨OCSP⁩ ومن الشهادة التي يتوقّعها المُجيب.
    3. لا تُخفِ إخفاق الحصول على مُنتَج ⁨B-LT⁩ أو ⁨B-LTA⁩؛ فتأكيد الإلغاء جزء من ذلك المستوى.
  • ذات صلة. مرجع الاستثناءات.

مدخل: تعذّر فكّ ترميز عنصر في سلسلة الشهادات

قسم بعنوان «مدخل: تعذّر فكّ ترميز عنصر في سلسلة الشهادات»
  • العَرَض. SignatureException وتنتهي الرسالة بالجزء failed to base64-decode PEM body — input is not valid PEM.
  • السبب المُرجَّح. عنصر في سلسلة الشهادات ليس بريدًا مُعزَّز الخصوصية (⁨PEM⁩) صالحًا، وغالبًا يكون ذلك بسبب الاقتطاع، أو محرف دخيل، أو كتلة قواعد الترميز المميِّز (⁨DER⁩) ثنائية قُدّمت في موضع كان يُتوقَّع فيه ⁨PEM.⁩
  • الدليل / التشخيص. يُنتج المصنع SignatureException::pemDecodingFailed() هذه الرسالة أثناء تجميع السلسلة.
  • الحلّ.
    1. افحص كل شهادة في السلسلة بحثًا عن محارف دخيلة أو اقتطاع.
    2. أعد تصدير الشهادة المتأثرة بصيغة ⁨PEM.⁩
    3. أعد تشغيل استدعاء التوقيع.
  • ذات صلة. التشفير والأذونات.

مدخل: نوع المفتاح الخاص لا يطابق الخوارزمية

قسم بعنوان «مدخل: نوع المفتاح الخاص لا يطابق الخوارزمية»
  • العَرَض. SignatureException وتنتهي الرسالة بالجزء expected private key of type "<x>" for the configured algorithm but got "<y>".
  • السبب المُرجَّح. المفتاح الخاص المُحمَّل لا يطابق خوارزمية التوقيع المُهيّأة، مثل مفتاح ريفست-شامير-أدلمان (⁨RSA⁩) عند تحديد خوارزمية التوقيع الرقمي بالمنحنى الإهليلجي (⁨ECDSA⁩).
  • الدليل / التشخيص. يُنتج المصنع SignatureException::unexpectedKeyType() هذه الرسالة ويُسمّي كلًّا من فئة المفتاح المتوقَّعة والفعلية.
  • الحلّ.
    1. تحقّق من أن الشهادة وزوج المفتاح يطابقان الخوارزمية التي اخترتها.
    2. غيّر تحديد الخوارزمية ليتطابق مع المفتاح، أو حمّل المفتاح الذي يطابق الخوارزمية.
    3. شغّل استدعاء التوقيع مرة أخرى.
  • ذات صلة. مرجع الاستثناءات.

مدخل: مادة مفتاح أو توقيع ⁨Ed25519⁩ مُشوَّهة

قسم بعنوان «مدخل: مادة مفتاح أو توقيع ⁨Ed25519⁩ مُشوَّهة»
  • العَرَض. SignatureException وتتضمن نهاية الرسالة عدم تطابق في طول ⁨Ed25519⁩ — على سبيل المثال Ed25519 signature length <n> ≠ expected 64 bytes، أو Ed25519 round-trip self-verify failed.
  • السبب المُرجَّح. أرجعت بنية التشفير في وقت التشغيل مادة مفتاح أو توقيعًا بطول خاطئ، أو فشل التحقق من توقيع مُنتَج حديثًا مقابل مفتاحه العام نفسه. يستشهد المحرك بـ⁨RFC 8032⁩ §3.4 في المصدر، الذي يحدد توقيع ⁨Ed25519⁩ المنفصل عند 64 بايت. يُجهض المحرك بدلًا من إصدار مادة لا يستطيع التحقق منها ذاتيًا.
  • الدليل / التشخيص. المصانع ذات الصلة هي SignatureException::ed25519SignatureMalformed() و::ed25519RoundTripVerifyFailed() و::ed25519KeyParseFailed() و::ed25519SeedInvalid() و::ed25519SecretKeyMalformed() و::ed25519PublicKeyInvalid(). ويُسمّي كلٌّ منها الطول المُلاحَظ.
  • الحلّ.
    1. أعد تثبيت امتداد ⁨libsodium⁩ لـ⁨PHP⁩؛ فالبناء المُجرَّد أو التالف هو السبب المُوثَّق لمادة الطول الخاطئ.
    2. تأكّد من أن المفتاح من نوع ⁨Ed25519⁩ وأن ⁨OpenSSL⁩ بإصدار 1.1.1 أو أحدث.
    3. أعد تشغيل استدعاء التوقيع.
  • ذات صلة. مرجع الاستثناءات.

مدخل: لم يُصدَر قاموس الطابع الزمني للأرشفة

قسم بعنوان «مدخل: لم يُصدَر قاموس الطابع الزمني للأرشفة»
  • العَرَض. SignatureException وتنتهي الرسالة بالجزء no /Type /DocTimeStamp dictionary was emitted into the PDF buffer.
  • السبب المُرجَّح. اشتغلت حلقة الأرشفة ⁨B-LTA⁩، لكن قاموس طابع المستند الزمني لم يصل قطّ إلى المخزن المؤقت. سيكون مُنتَج ⁨B-LTA⁩ مكتوبًا جزئيًا، لذا يرفض المحرك إعادته.
  • الدليل / التشخيص. يُنتج المصنع SignatureException::documentTimestampNotEmitted() هذه الرسالة. ينشأ هذا الإخفاق من شرط لاحق وقت الإنهاء.
  • الحلّ.
    1. تعامَل مع الخرج بوصفه مُلغًى؛ لا تسلّم المُنتَج الجزئي.
    2. أعد تشغيل خط أنابيب ⁨B-LTA⁩ مع هيئة طابع زمني قابلة للبلوغ.
    3. إذا تكرّر الإخفاق، فالتقط getContext() والاستثناء السابق المُتسلسل لإعداد تقرير عيب.
  • ذات صلة. مرجع الاستثناءات.
  • تضبط هذه المصانع cert_info على الموضوع أو البصمة عند توفّر أحدهما فقط؛ ويُتوقَّع أن يكون cert_info فارغًا لإخفاقات القدرة والإعداد.
  • يُطلق طلب ⁨B-LT⁩ أو ⁨B-LTA⁩ من دون عميل بروتوكول نقل النص التشعّبي (⁨HTTP⁩) مُهيّأ الاستثناء SignatureException::httpClientMissing()؛ فجلب الإلغاء يحتاج إلى عميل متوافق مع توصية معيار ⁨PHP⁩ (⁨PSR⁩)-18 ومُمرَّر إلى المنسّق.
  • تؤدي شهادة مدعومة بوحدة أمان عتادية (⁨HSM⁩) من دون تنفيذ مُوقِّع إلى إطلاق الاستثناء SignatureException::hsmSignerMissing()؛ صِل المُوقِّع بالشهادة قبل التوقيع.

المسرد: مستوى ⁨PAdES⁩ · تأكيد الإلغاء