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

العقود / التوقيع

يغطي نطاق التوقيع ستة عقود. تحدّد هذه العقود كيفية إنتاج توقيع ببنية رسالة التشفير (⁨CMS⁩)، وتطبيق طابع زمني وفق طلب التعليقات (⁨RFC⁩) 3161، والتوقيع بمفتاح محفوظ في وحدة أمان الأجهزة (⁨HSM⁩)، وتمكين التحقق طويل الأمد (⁨LTV⁩). تنشر ⁨Core⁩ هذه العقود، ويوفّر إصدارا ⁨Pro⁩ و⁨Enterprise⁩ تطبيقاتها الإنتاجية.

Terminal window
composer require nextpdf/core:^3

التوقيع الرقمي في صيغة المستندات المحمولة (⁨PDF⁩) هو بنية ⁨CMS SignedData⁩ مخزّنة في قاموس التوقيع. يحمل المُدخل Contents البنيةَ المُرمّزة وفق قواعد الترميز المميّز (⁨DER⁩). يحدّد المُدخل ByteRange نطاقات البايتات التي يغطّيها التلخيص. يغطّي التلخيص الملف كاملًا ويستثني قيمة التوقيع نفسها؛ راجع المنظمة الدولية للتوحيد القياسي (⁨ISO⁩) 32000-2 §12.8.1. تتبع بنية ⁨CMS⁩ المعيار ⁨RFC 5652⁩ §5.1: الإصدار، وخوارزميات التلخيص، والمحتوى المُغلَّف، ومعلومات الموقّع.

SignerInterface هو العقد الأساسي. ينتج ⁨CMS SignedData⁩ لنطاق بايتات، ويطبّق طابعًا زمنيًا على قيمة توقيع، ويبلّغ عمّا إذا كان يدعم ⁨LTV.⁩ ويغطي المستويات الأساسية للتوقيعات الإلكترونية المتقدمة لصيغة ⁨PDF⁩ (⁨PAdES⁩) من ⁨B-B⁩ حتى ⁨B-LTA⁩ كما يحدّدها المعهد الأوروبي لمعايير الاتصالات (⁨ETSI⁩) ⁨EN 319 142.⁩ يضيف كل مستوى أعلى مادة تحقق إضافية. يحمل ⁨B-B⁩ التوقيع الأساسي. يضيف ⁨B-T⁩ طابعًا زمنيًا للتوقيع. يضيف ⁨B-LT⁩ بيانات الإبطال. يضيف ⁨B-LTA⁩ طابعًا زمنيًا أرشيفيًا.

HsmSignerInterface يوقّع بمفتاح مخزّن في وحدة أمان الأجهزة. لا يغادر المفتاح الخاص حدود الجهاز أبدًا. يعيد العقد شهادة الموقّع وسلسلة الشهادات بصيغة ⁨DER⁩، حتى تتمكّن طبقة ⁨CMS⁩ من بناء البنية. DeferredSignerInterface يوسّع SignerInterface للتوقيع غير المتزامن. يقدّم المستدعي البيانات، ويتلقّى معرّف مهمة، ويستطلع اكتمالها، ثم يسترجع النتيجة. استخدمه عندما لا تعيد وحدة ⁨HSM⁩ بعيدة أو خدمة مفاتيح سحابية نتيجة فورية.

TimestampProviderInterface يطلب رمزًا لطابع زمني وفق ⁨RFC 3161.⁩ البروتوكول تبادل طلب واستجابة مع سلطة الطوابع الزمنية (⁨TSA⁩)؛ راجع ⁨RFC 3161⁩ §2.4. تكون قيمة messageImprint في الرمز تجزئةً لقيمة التوقيع؛ راجع ⁨RFC 3161⁩ §2.4.2. LtvManagerInterface يفعّل ⁨LTV.⁩ وهو يجمع سلسلة الشهادات، ويجلب استجابات بروتوكول حالة الشهادة عبر الإنترنت (⁨OCSP⁩) وقائمة إبطال الشهادات (⁨CRL⁩)، ويبني متجر أمان المستند (⁨DSS⁩)، ويضيف طابعًا زمنيًا للمستند من أجل ⁨B-LTA.⁩ CryptoPolicyInterface يضبط خوارزميات التجزئة والتوقيع والتشفير المسموح بها، إضافةً إلى قوّة المفاتيح، قبل تنفيذ أي عملية تشفير.

النوعالصنفالأعضاء الرئيسيونالاستقرارمنذ
SignerInterfaceواجهةsign(string): SignatureResult، timestamp(string): string، supportsLtv(): boolمستقر1.0.0
HsmSignerInterfaceواجهةsign(string, string): string، getCertificateDer()، getCertificateChainDer()، getPublicKeyAlgorithm()مستقر1.0.0
DeferredSignerInterfaceواجهةsubmitForSigning(string): string، retrieveSignature(string)، isComplete(string) (يوسّع SignerInterface)تجريبي3.0.0
TimestampProviderInterfaceواجهةgetTimestamp(string): stringتجريبي3.0.0
LtvManagerInterfaceواجهةenableLtv(...)، addDocumentTimestamp(...)مستقر1.10.0
CryptoPolicyInterfaceواجهةisHashAlgorithmAllowed()، isSignatureAlgorithmAllowed()، isEncryptionAlgorithmAllowed()، isKeyStrengthAllowed()، getPreferredHashAlgorithm()، getName()مستقر1.9.0

SignerInterface::sign() يعيد NextPDF\Security\Signature\SignatureResult. تحتوي الخاصية العامة $cmsSignedData على بنية ⁨CMS⁩ المُرمّزة بصيغة ⁨DER.⁩ وتحتوي الخاصية العامة $digestHex على تلخيص ⁨SHA-256.⁩ وتحتوي الخاصية العامة $timestampToken على رمز ⁨TSA⁩ الاختياري. توابع الوصول هي toHex() / toHexPadded(int) / getSize() / hasTimestamp(). HsmSignerInterface::sign() يعيد بايتات مُرمّزة بصيغة ⁨DER⁩ لخوارزمية ريفست-شامير-أدلمان (⁨RSA⁩) وبايتات r‖s الخام لخوارزمية التوقيع الرقمي بالمنحنى الإهليلجي (⁨ECDSA⁩). يستخدم وسيط الخوارزمية معرّفات ⁨OpenSSL.⁩

examples/contracts/signing-quickstart.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\SignerInterface;
/**
* Sign a byte range with any SignerInterface implementation.
*
* @param SignerInterface $signer A core or Premium signer.
* @param string $byteRange The PDF byte range to sign.
*
* @return string Hex-encoded CMS SignedData for the PDF /Contents field.
*/
function signByteRange(SignerInterface $signer, string $byteRange): string
{
$result = $signer->sign($byteRange);
return $result->toHex();
}

SignerInterface::sign() يعيد SignatureResult. toHex() يُنتج السلسلة السداسية عشرية التي يضعها الكاتب في حقل /Contents؛ أما بايتات ⁨DER⁩ الخام فمتاحة على الخاصية العامة $cmsSignedData. تعتمد الدالة على العقد، لا على صنف محدّد. ينفّذ هذا العقدَ كلٌّ من موقّع اختبار أساسي وموقّع ⁨HSM⁩ في ⁨Premium.⁩

examples/contracts/signing-production.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\CryptoPolicyInterface;
use NextPDF\Contracts\SignerInterface;
use NextPDF\Contracts\TimestampProviderInterface;
use NextPDF\Exception\NextPdfException;
use Psr\Log\LoggerInterface;
final readonly class TimestampedSigningService
{
public function __construct(
private SignerInterface $signer,
private TimestampProviderInterface $timestamps,
private CryptoPolicyInterface $policy,
private LoggerInterface $logger,
) {}
/**
* Sign a byte range, then timestamp the CMS structure.
*
* @param string $byteRange The PDF byte range to sign.
*
* @return array{cms: string, digest: string, tst: string}
*/
public function sign(string $byteRange): array
{
if (!$this->policy->isHashAlgorithmAllowed($this->policy->getPreferredHashAlgorithm())) {
throw new \LogicException('Preferred hash rejected by crypto policy.');
}
try {
$signature = $this->signer->sign($byteRange);
$token = $this->timestamps->getTimestamp($signature->cmsSignedData);
return [
'cms' => $signature->toHex(),
'digest' => $signature->digestHex,
'tst' => $token,
];
} catch (NextPdfException $e) {
$this->logger->error('Signing failed', [
'policy' => $this->policy->getName(),
'error' => $e->getMessage(),
]);
throw $e;
}
}
}

تستقبل الخدمة ثلاثة عقود بالحقن. وتتحقق من سياسة التشفير قبل التوقيع. يكشف SignatureResult بايتات ⁨CMS⁩ على الخاصية العامة $cmsSignedData، وتلخيص ⁨SHA-256⁩ على $digestHex، والسلسلة السداسية عشرية لحقل /Contents عبر toHex(). يتلقّى مزوّد الطوابع الزمنية بايتات ⁨CMS⁩ ويعيد رمزًا مُرمّزًا بصيغة ⁨DER.⁩ تسجّل كتلة ⁨catch⁩ اسم السياسة وتعيد رمي الاستثناء. ولا تخفي الإخفاق أبدًا.

  • يجب أن يستثني تلخيص نطاق البايتات قيمة التوقيع. ينتج عن التلخيص الذي يغطّي المُدخل Contents توقيعٌ لا يمكن التحقق منه أبدًا؛ راجع ⁨ISO 32000-2⁩ §12.8.1.
  • SignerInterface::supportsLtv() يبلّغ عن القدرة، لا عن الحالة. يمكن للموقّع أن يدعم ⁨LTV⁩ ومع ذلك ينتج توقيع ⁨B-B⁩ عندما لا تكون أي خدمة طوابع زمنية مهيّأة.
  • DeferredSignerInterface::retrieveSignature() يعيد null حتى تكتمل المهمة. استطلع isComplete() أولًا حتى لا تنقل الحمولة في كل فحص. تصبح عملية الاسترجاع متماثلة الإجراء بمجرد توفر نتيجة.
  • LtvManagerInterface::addDocumentTimestamp() يجب أن يُشغَّل بعد كتابة متجر أمان المستند. ينتج عن استدعائه أولًا بنية ⁨B-LTA⁩ غير صالحة.
  • CryptoPolicyInterface يعيد true لكل خوارزمية عند عدم ضبط أي سياسة. في بيئة خاضعة للتنظيم، اضبط سياسة صريحة، ولا تعتمد على الافتراضي المفتوح.
  • HsmSignerInterface::getCertificateChainDer() يستثني شهادة الموقّع. استخدم getCertificateDer() لورقة الموقّع، وطريقة السلسلة للشهادات الوسيطة.

تأتي تكلفة التوقيع أساسًا من عملية التشفير وأي رحلة شبكة ذهابًا وإيابًا، لا من العقد. يستغرق التوقيع البرمجي المحلي عادةً بضعة مللي ثوانٍ، غالبًا بعدد أحادي الرقم. يضيف توقيع ⁨HSM⁩ رحلة إلى الجهاز ذهابًا وإيابًا. يضيف الطابع الزمني رحلة شبكة ذهابًا وإيابًا إلى سلطة الطوابع الزمنية. يضيف التحقق طويل الأمد جلب ⁨OCSP⁩ أو ⁨CRL⁩ واحدًا لكل شهادة في السلسلة. يغطّي performance_budget البالغ 1500 ⁨ms⁩ من الزمن الفعلي توقيعًا واحدًا مزوّدًا بطابع زمني مع ⁨TSA⁩ بعيدة على اتصال مُهيّأ مسبقًا. يتجاوز التحقق طويل الأمد مقابل نقطة نهاية إبطال بطيئة هذا الحدّ، وينبغي أن يُشغَّل خارج مسار الطلب. ملف إعادة الإنتاجية هو structural، لا bitwise. يضمّن الطابع الزمني لحظة التوقيع، لذلك يختلف تشغيلان في بايتات الطابع الزمني بينما تبقى بنية المستند مطابقة.

عقود التوقيع هي الحدّ التشفيري الأساسي للمحرّك، لذلك يكون نموذج التهديد صريحًا. حفظ المفاتيح هو الشاغل الأول: HsmSignerInterface يبقي المفتاح الخاص داخل حدود الجهاز، والعقد لا يكشف مادة المفتاح أبدًا. خفض مستوى الخوارزمية هو الشاغل الثاني: CryptoPolicyInterface يحجب التجزئات الضعيفة والمفاتيح القصيرة قبل العملية، بما يتيح لعملية النشر فرض ملف معايير المعالجة الفيدرالية للمعلومات (⁨FIPS⁩) 140-3 أو ملف التعريف الإلكتروني والاستيثاق وخدمات الثقة (⁨eIDAS⁩) دون تشعيب المحرّك. الثقة بالطابع الزمني هي الشاغل الثالث: رمز ⁨RFC 3161⁩ جدير بالثقة بقدر جدارة سلطة الطوابع الزمنية فقط، لذلك يكون عقد المزوّد قابلًا للحقن وتثبّت عملية النشر سلطتها الخاصة. التحقق طويل الأمد هو الشاغل الرابع: تُجلب مادة الإبطال وقت التوقيع وتُخزّن في متجر أمان المستند بحيث يصمد التحقق بعد انتهاء صلاحية الشهادة. عامِل كل مُدخل للموقّع باعتباره غير موثوق. يحسب المحرّك نطاق البايتات؛ ولا يُقبل أبدًا من المستدعي. هذه الصفحة مُعلّمة بـexport_control_class: legal-review-required لأن العقود تحكم التوقيع التشفيري. يعيد النص صياغة جميع المصادر المعيارية ولا يقتبس أيًّا منها، حفاظًا على سلامة الاستشهاد.

الادّعاءالمعيارالبندالدليل
يخزّن المُدخل Contents في قاموس التوقيع قيمةَ التوقيع بصيغة ⁨CMS SignedData⁩ مُرمّزة بصيغة ⁨DER⁩ أو رمز طابع زمني (⁨TimeStampToken⁩).⁨ISO 32000-2⁩§12.8.1
يُحسب التلخيص على نطاق البايتات الذي تحدّده مصفوفة ByteRange ويستثني قيمة التوقيع.⁨ISO 32000-2⁩§12.8.1,
يحمل متجر أمان المستند مادة التحقق طويل الأمد ضمن مُدخلات المعلومات المتعلقة بالتحقق (⁨VRI⁩) و⁨OCSP⁩ و⁨CRL⁩ ومُدخلات الشهادات.⁨ISO 32000-2⁩§12.8.4.3,
يضع التحقق طويل الأمد وفق ⁨PAdES⁩ بيانات التحقق في قاموسي ⁨DSS⁩ و⁨VRI.⁩⁨ETSI EN 319 142-2⁩§6.3,
يربط رمز الطابع الزمني وفق ⁨RFC 3161⁩ تجزئةً لقيمة التوقيع عبر messageImprint في تبادل طلب واستجابة مع ⁨TSA.⁩⁨RFC 3161⁩§2.4.2، §2.4,
يحمل تسلسل ⁨CMS SignedData⁩ الإصدار، وخوارزميات التلخيص، والمحتوى المُغلَّف، ومعلومات الموقّع.⁨RFC 5652⁩§5.1

جميع البنود مُعاد صياغتها. لا يستنسخ ⁨NextPDF⁩ النص المعياري. راجع المعايير المنشورة للحصول على الصياغة المعتمدة.

تنشر ⁨Core⁩ عقود التوقيع وتثبّت استقرارها. يوفّر إصدارا ⁨Pro⁩ و⁨Enterprise⁩ التطبيقات الإنتاجية خلف HsmSignerInterface وLtvManagerInterface والموقّع المؤجّل، بما في ذلك تكامل أجهزة معيار تشفير المفتاح العام #11 (⁨PKCS⁩#11) و⁨PAdES B-LT⁩ و⁨B-LTA.⁩ تربط ⁨Core⁩ هذه التطبيقات في وقت التشغيل عبر class_exists() وتحوّلها إلى هذه العقود، بحيث لا يحمل المحرّك مفتوح المصدر أي اعتمادية تجارية ولا تتغيّر واجهة برمجة التطبيقات عند الترقية.