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

التشفير: AES-256 (CBC) وAES-256-GCM

يشفّر ⁨Core⁩ ملفات ⁨PDF⁩ باستخدام ⁨AES-256⁩ (معيار التشفير المتقدم بمفاتيح طولها 256 بت) ضمن معالِج الأمان القياسي في ⁨ISO 32000-2⁩:2020 §7.6. الوضع الافتراضي هو ⁨V⁩=5 / ⁨R⁩=6 / ⁨AESV3⁩ (⁨AES-256-CBC⁩، تسلسل كتل التشفير). أما الوضع المُصادَق الاختياري فهو مسار ⁨ISO/TS 32003⁩:2023 ⁨V⁩=6 / ⁨R⁩=7 ⁨AES-256-GCM⁩ (وضع غالوا/العدّاد). توضّح هذه الصفحة اشتقاق المفتاح، وتنسيق النقل، وحدّ الأذونات، وحدود النشر.

Terminal window
composer require nextpdf/core:^3

يتطلّب المسار الافتراضي امتداد openssl. يستخدم مسار ⁨AES-256-GCM⁩ إمّا openssl أو ext-sodium. على المضيفين الذين لا يتوفر لديهم عتاد ⁨AES-NI⁩، يرفض ⁨libsodium⁩ وضع ⁨GCM⁩؛ فيعود ⁨Core⁩ إلى تنفيذ ⁨OpenSSL⁩ الأبطأ من دون تغيير الخوارزمية.

يستخدم المعالِج الافتراضي معالِج الأمان القياسي ⁨V⁩=5 / ⁨R⁩=6 مع مرشّح التشفير ⁨AESV3.⁩ عند استدعاء setEncryption()، يولّد ⁨Core⁩ مفتاح ملف عشوائيًا بطول 256 بت من مصدر عشوائية تعموي في المنصّة (random_bytes()). يبلغ طول المفتاح 32 بايت، بما يطابق طول مفتاح ⁨FIPS 197.⁩ يشفّر ⁨Core⁩ محتوى كل كائن على حدة باستخدام ⁨AES-256-CBC.⁩ ويسبق كل نص مشفَّر بمتجِّه تهيئة بطول 16 بايت، كما يوجّه ⁨ISO 32000-2⁩:2020 §7.6.4.

يتّبع اشتقاق المفتاح الخوارزمية 2.⁨B⁩ عند المراجعة 6. يطبّع ⁨Core⁩ كلمة المرور أولًا باستخدام ⁨SASLprep⁩ (⁨RFC 4013⁩)، ثم يقتطعها إلى 127 بايت من ⁨UTF-8⁩ عند حدّ محرف، كما يوجّه ⁨ISO 32000-2⁩:2020 §7.6.4.3.3. ويحسب التجزئة المشتقّة بروتين مكرَّر من ⁨SHA-256⁩ / ⁨SHA-384⁩ / ⁨SHA-512⁩ تقوده خطوة ⁨AES-128-CBC⁩، مما يرفع تكلفة تخمين كلمة المرور دون اتصال. يولّد ⁨Core⁩ أملاح المستخدم والمالك وكل مفتاح مرة واحدة لكل نسخة من المُشفِّر، فتُنتج النسخة الواحدة بايتات قاموس حتمية، وهو شرط مسبق للكاتب متعدّد التمريرات.

useAesGcm() يفعّل مسار ⁨AES-256-GCM⁩ الاختياري. ينفّذ هذا المسار مرشّح التشفير ⁨ISO/TS 32003⁩:2023 ⁨V⁩=6 / ⁨R⁩=7 ⁨AESV4.⁩ الخوارزمية هي ⁨AES-256-GCM⁩ بمعاملات من ⁨NIST SP 800-38D.⁩ لكل كائن مشفَّر، يتكوّن تخطيط النقل من متجِّه تهيئة بطول 12 بايت، ثم النص المشفَّر، ثم وسم مصادقة بطول 16 بايت. تكون البيانات المُصادَقة الإضافية فارغة، كما يوجّه ملف تعريف ⁨TS 32003⁩ §5.2. يتحقّق فكّ التشفير من الوسم ويثير TamperedDataException عند عدم التطابق؛ ولا يُرجع أبدًا نصًا صريحًا بعد فشل الوسم. يضيف هذا المسار كشف التعديل، وهو ما لا يوفّره مسار ⁨CBC⁩ الافتراضي من تلقاء نفسه.

يتّبع مسار ⁨GCM⁩ انضباط تفرّد متجِّه التهيئة في ⁨NIST SP 800-38D⁩ §8. تمثّل البايتات الأربعة العليا من متجِّه التهيئة حقلًا ثابتًا لكل نسخة، ويُضبط هذا الحقل من مصدر عشوائي أثناء الإنشاء. أما البايتات الثمانية السفلى فهي عدّاد كبير النهاية يتزايد بعد كل متجِّه تهيئة يُصدر. يتّبع ذلك نهج الإنشاء الحتمي في §8.2.1، إلا أن الحقل الثابت يُعشَّى لمنع التصادمات عبر المستندات بدلًا من تعداده. ويسجّل حارس ثانٍ كل متجِّه تهيئة مُصدَر في مجموعة تصادم، ويثير NonceReuseException إذا تكرّرت قيمة. كذلك يثير تجاوز سعة العدّاد NonceReuseException، لأنّ تجاوز السعة نمط فشل لإعادة استخدام متجِّه التهيئة الذي يحذّر منه §8.

ينطبق حدّان للطول على مسار ⁨GCM.⁩ سقف النص الصريح لكل كائن هو 2^39 − 256 بايت، وهو الحدّ المشتق لكل استدعاء في ⁨NIST SP 800-38D⁩ §5.2.1.1. يثير الإدخال الأكبر استثناء طول مع إرشاد إلى تقسيم البيانات عبر الكائنات. حدّ أمان الاستدعاء هو 2^32 استدعاء لكل مفتاح. assertWithinSafetyBound() فحص اختياري يثير GcmInvocationLimitExceededException، مما يتيح للمستدعي تدوير مفتاح المستند قبل عتبة §8.3. يعدّ ⁨NIST SP 800-57⁩ الجزء 1 §4 قرار عمر المفتاح هذا مسؤولية نشر.

أعلام الأذونات إرشادية. يكتب ⁨Core⁩ قناع البتات في مدخل /Perms المشفَّر وقيمة /P، ثم يسترده عبر validatePerms() عند القراءة، ويفشل مغلقًا عند وجود علامة تالفة. يُتوقّع من القارئ المتوافق أن يحترم الأعلام. الأعلام غير مفروضة بالتعمية: فالمعالِج الذي يملك مفتاح فكّ التشفير ويتجاهل البتات يمكنه قراءة المحتوى أو نسخه أو تعديله. صِف أعلام الأذونات باعتبارها عُرفًا للقارئ، لا تحكّمًا في الوصول.

النوعالصنفالأعضاء الرئيسيونالاستقرارمنذ
Aes256Encryptorصنفencrypt(), decrypt(), encryptForObject(), buildEncryptionDictionary(), verifyUserPassword(), verifyOwnerPassword(), validatePerms(), getEncryptionKey()مستقر1.0.0
Aes256GcmEncryptorصنفencrypt(), decrypt(), encryptStream(), assertWithinSafetyBound(), invocationCount(), isAvailable()مستقر2.18.0
KeyMaterialصنف نهائي للقراءة فقطgenerate(), exposeKey(), fingerprint()مستقر2.18.0
EncryptedPayloadSpecصنف نهائي للقراءة فقطtoDict()مستقر2.18.0
CryptoCapabilitiesصنف نهائيhasAesGcm(), detectFipsMode(), assertFipsAvailableForProfile()مستقر2.0.0
NonceReuseExceptionاستثناءمستقر2.18.0
TamperedDataExceptionاستثناءمستقر2.18.0
DecryptionFailedExceptionاستثناءمستقر2.18.0
GcmInvocationLimitExceededExceptionاستثناءمستقر3.0.0
examples/22-protection.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
// AES-256-CBC, V=5/R=6. Call before addPage().
$doc->setEncryption(
userPassword: 'demo',
ownerPassword: 'admin',
permissions: 4, // printing only; copy/modify denied for a conforming reader
);
$doc->addPage();
$doc->setFont('helvetica', '', 12);
$doc->cell(0, 8, 'Confidential', newLine: true);
$doc->save(__DIR__ . '/output/22-protection.pdf');
examples/security/gcm-authenticated-encryption.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Security\CryptoCapabilities;
use NextPDF\Security\Encryption\Aes256GcmEncryptor;
use NextPDF\Security\Exception\TamperedDataException;
use NextPDF\Security\KeyMaterial;
use Psr\Log\LoggerInterface;
final readonly class AuthenticatedBlobCipher
{
public function __construct(private LoggerInterface $logger) {}
/**
* Seal a payload with AES-256-GCM and return the wire-format bytes.
*
* @param non-empty-string $plaintext The payload to protect.
*
* @return non-empty-string IV(12) || ciphertext || tag(16).
*/
public function seal(string $plaintext, KeyMaterial $key): string
{
if (!CryptoCapabilities::hasAesGcm()) {
throw new \RuntimeException('Host cannot perform AES-256-GCM.');
}
$cipher = new Aes256GcmEncryptor($key);
// Opt-in NIST SP 800-38D §8.3 key-rotation guard.
$cipher->assertWithinSafetyBound();
$wire = $cipher->encrypt($plaintext);
$this->logger->info('Payload sealed', [
'key_fingerprint' => $key->fingerprint(),
'invocations' => $cipher->invocationCount(),
]);
return $wire;
}
/**
* Open a sealed payload; a modified payload raises, never returns plaintext.
*
* @param non-empty-string $wire IV(12) || ciphertext || tag(16).
*/
public function open(string $wire, KeyMaterial $key): string
{
try {
return (new Aes256GcmEncryptor($key))->decrypt($wire);
} catch (TamperedDataException $e) {
$this->logger->warning('Tampered payload rejected', [
'key_fingerprint' => $key->fingerprint(),
]);
throw $e;
}
}
}

يتحقّق المُشفِّر من قدرة المضيف، ويطبّق حارس الاستدعاء الاختياري، ولا يسجّل إلا بصمة المفتاح غير القابلة للعكس، ويعيد إثارة رفض العبث بدلًا من إرجاع بايتات مشبوهة.

  • يوفّر مسار ⁨AES-256-CBC⁩ الافتراضي السرّية فقط. ولا يكشف النص المشفَّر المعدَّل من تلقاء نفسه. استخدم مسار ⁨AES-256-GCM⁩ عندما تحتاج إلى كشف التعديل.
  • يثير useAesGcm() استثناءً عندما يكون وضع ⁨PDF/A⁩ نشطًا، وعندما لا يوفّر أيٌّ من openssl ولا ext-sodium وضع ⁨AES-256-GCM.⁩ التقط كلتا الحالتين واعرض رسالة قابلة للتنفيذ من قِبل المشغّل.
  • على المضيفين الذين لا يتوفر لديهم ⁨AES-NI⁩، يرفض ⁨libsodium⁩ وضع ⁨GCM.⁩ يعود ⁨Core⁩ إلى ⁨OpenSSL GCM⁩، وهو صحيح لكنه أبطأ؛ فتنخفض الإنتاجية لا الأمان.
  • سقف النص الصريح لكل كائن في ⁨GCM⁩ هو 2^39 − 256 بايت. يثير الإدخال الأكبر استثناء طول؛ فقسّم المحتوى عبر كائنات متعدّدة باستخدام encryptStream().
  • يجب أن تكون نسخة KeyMaterial بطول 32 بايت بالضبط. يرفض الإنشاء الطول الخاطئ بدلًا من اقتطاعه.
  • مسار القراءة (verifyUserPassword()، verifyOwnerPassword()، validatePerms()) يستخدم مقارنة بزمن ثابت للمواد التعموية ويفشل مغلقًا عند علامة أذونات تالفة.

تشفير ⁨AES-256-CBC⁩ لكل كائن هو استدعاء ⁨OpenSSL⁩ واحد، وزمنه ⁨O⁩(⁨n⁩) في جسم الكائن. يشغّل اشتقاق المفتاح روتين الخوارزمية 2.⁨B⁩ المكرَّر مرة واحدة لكل نسخة من المُشفِّر؛ والتكلفة محدودة وثابتة لكل مستند. يقسّم مسار بثّ ⁨AES-256-GCM⁩ الإدخال إلى أجزاء بحجم 16 ميبي بايت، فيحدّ استخدام الكومة الحيّة إلى نحو 64 ميغابايت بصرف النظر عن الحجم الكلّي للإدخال، ويبقى دون ميزانية الذروة الموثّقة البالغة 64 ميغابايت. يضيف كل كائن ⁨GCM⁩ عبئًا قدره 28 بايت (متجِّه تهيئة بطول 12 بايت زائد وسم بطول 16 بايت). يحسّن عتاد ⁨AES-NI⁩ إنتاجية ⁨GCM⁩ تحسينًا ملموسًا؛ وبدونه تنخفض الإنتاجية فقط.

لهذا السطح التشفيري نموذج تهديد صريح. يرفع تطبيع ⁨SASLprep⁩ مع اشتقاق المفتاح المكرَّر بالمراجعة 6 تكلفة تخمين كلمة المرور دون اتصال، لكن كلمة المرور الضعيفة تظلّ الخطر المتبقّي المهيمن. لا يزيل أي اشتقاق ذلك الخطر. يكشف مسار ⁨GCM⁩ تعديل النص المشفَّر عبر التحقّق من الوسم؛ أما مسار ⁨CBC⁩ الافتراضي فلا يفعل. في مسار ⁨GCM⁩، يمنع عدّاد مع مجموعة تصادم إعادة استخدام متجِّه التهيئة، بما يتّسق مع انضباط متجِّه التهيئة في ⁨NIST SP 800-38D⁩ §8.1. يرفض تجاوز سعة العدّاد بدلًا من الالتفاف. يخفّف حجب KeyMaterial وسمة #[\SensitiveParameter] على كلمات المرور كشفَ المفتاح عبر السجلّات. تُصفَّر مواد المفتاح المشتقّة بعد الاستخدام حيث تسمح المنصّة بذلك.

الحدّ صريح أيضًا. يطبّق ⁨Core⁩ تشفير ⁨AES-256⁩ كما هو معرَّف في ⁨ISO 32000-2⁩:2020 §7.6، ويطبّق للمسار الاختياري ⁨ISO/TS 32003⁩:2023 §5.2. تعتمد الحماية الفعّالة على قوّة كلمة المرور، وإدارة المفاتيح، وبيئة النشر، والقارئ المستهلِك. يحترم القرّاء المتوافقون أعلام الأذونات، لكن التعمية لا تفرضها. يفرض ⁨ISO 32000-2⁩:2020 §7.6.4.4.10 خطوة ⁨AES-ECB⁩ المستخدمة لقيمة /Perms لكتلة واحدة بطول 16 بايت. وهي ليست وضعًا عامّ الأغراض. يكشف ⁨Core⁩ فحصًا لتدوير المفتاح قبل حدّ الاستدعاء 2^32، لكنه لا يفرضه افتراضيًا؛ وذلك التدوير مسؤولية نشر.

مقرّ البيانات وتخفيفات معلومات التعريف الشخصية

قسم بعنوان «مقرّ البيانات وتخفيفات معلومات التعريف الشخصية»

يجري التشفير وفكّ التشفير داخل العملية؛ ولا تغادر المضيف عبر هذا السطح أي بايتات للمستند أو كلمة مرور أو قيمة مفتاح. تستند مجموعة تصادم متجِّه التهيئة في ⁨GCM⁩ إلى بصمة مفتاح غير قابلة للعكس، لا إلى بايتات المفتاح. إذا وضع نشرٌ ما المفتاح خلف نظام خارجي لإدارة المفاتيح أو رمز ⁨PKCS⁩#11، فإنّ تلك الخلفية مسؤولة عن المقرّ؛ ويكون ⁨OASIS PKCS⁩#11 ⁨v3.1⁩ C_GenerateKey نقطة الربط لتوليد مفتاح مقيم في الرمز.

القياس الآمن عن بُعد وتنقية السجلّات

قسم بعنوان «القياس الآمن عن بُعد وتنقية السجلّات»

سجّل اسم السياسة وبصمة المفتاح المكوّنة من 8 محارف، ولا تسجّل المفتاح أو كلمة المرور أبدًا. يُرجع KeyMaterial::__toString() و__debugInfo() عنصرًا نائبًا محجوبًا. تتضمّن الاستثناءات من هذا السطح تسمية العملية والبصمة، لا بايتات المفتاح. عدّاد استدعاء ⁨GCM⁩ قياسٌ آمن عن بُعد للوحات معلومات تدوير المفتاح.

التهديدالتخفيف في ⁨Core⁩الحدّ المتبقّي
تخمين كلمة المرور دون اتصال⁨SASLprep⁩ مع الاشتقاق المكرَّر بالمراجعة 6كلمة المرور الضعيفة لا تزال الخطر المهيمن
تعديل النص المشفَّرالتحقّق من وسم ⁨GCM⁩ (المسار الاختياري)مسار ⁨CBC⁩ للسرّية فقط
إعادة استخدام متجِّه التهيئة (⁨GCM⁩)حقل ثابت عشوائي مع عدّاد مع مجموعة تصادم؛ تجاوز السعة يرفض
نص صريح مفرط الطول في ⁨GCM⁩فحص الطول عند 2^39 − 256؛ إرشاد التقسيمعلى المستدعي بثّ الإدخال الكبير
الإفراط في استخدام المفتاح (⁨GCM⁩)assertWithinSafetyBound() عند 2^32اختياري؛ غير مفروض افتراضيًّا
تجاوز أعلام الأذوناتلا شيء — الأعلام إرشاديةالقارئ غير المتوافق يتجاهل الأعلام
كشف المفتاح عبر السجلّاتحجب KeyMaterial؛ #[\SensitiveParameter]المستدعي الذي يسجّل exposeKey() يُبطل هذا

⁨Core⁩ ليس وحدة تشفير مُصدَّقة وفق ⁨FIPS⁩ وليس معتمدًا وفق ⁨FIPS.⁩ CryptoCapabilities::detectFipsMode() فحصٌ بأفضل جهد يُبلّغ بأنّ الوضع نشط أو غائب أو غير محدَّد. assertFipsAvailableForProfile() يفشل مغلقًا عند تحديد ملف تعريف ⁨FIPS⁩ على مضيف لا يثبت وجود مزوّد ⁨FIPS.⁩ يعمل السطح التشفيري في وضع متوافق مع ⁨FIPS⁩ عندما يُشغَّل على بنية ⁨OpenSSL⁩ مضيفة حمّلت مزوّدًا مُصدَّقًا وفق ⁨FIPS.⁩ الوضعية المُصدَّقة والمعتمدة شأن خاص بإصدار ⁨Enterprise.⁩

الادّعاءالمعيارالبندالدليل
كل متجِّه تهيئة في ⁨GCM⁩ فريد لكل استدعاء عبر إنشاء حتمي من حقل ثابت مع عدّاد.⁨NIST SP 800-38D⁩§8.2.1
انضباط إنشاء متجِّه التهيئة يمنع إعادة الاستخدام عبر الاستدعاءات على مفتاح واحد.⁨NIST SP 800-38D⁩§8.1
سقف النص الصريح لكل كائن يطابق حدّ الطول لكل استدعاء.⁨NIST SP 800-38D⁩§5.2.1.1
مدّة صلاحية المفتاح وتدويره مسؤولية النشر.⁨NIST SP 800-57⁩ الجزء 1 المراجعة 5§4
مفتاح ملف ⁨AES⁩ بطول 256 بت، مطابقًا لطول مفتاح المعيار.⁨FIPS 197⁩§4.2.1
توليد مفتاح مقيم في الرمز هو نقطة تكامل مخزن المفاتيح الخارجي.⁨OASIS PKCS⁩#11 ⁨v3.1⁩⁨C_GenerateKey⁩

⁨ISO 32000-2⁩:2020 §7.6 و⁨ISO/TS 32003⁩:2023 §5.2 هما الأساس المعياري للمعالِجات الموثّقة هنا. نصّهما مقيَّد بالترخيص. تعيد هذه الصفحة صياغة تلك المعايير، وتستشهد بالبنود بالأرقام، ولا تقتبس أيًا منها. يوفّر اختبار معايير الخوارزمية 2.⁨B⁩ وأداة الوحي الخارجي في ذيل دليل الصفحة الدليل الزمني المتحقَّق منه للاشتقاق الدقيق للمفتاح بايتًا ببايت.

يوفّر ⁨Core⁩ مسار ⁨AES-256-CBC⁩ الافتراضي، ومسار ⁨AES-256-GCM⁩ الاختياري، وسطح المفتاح المحلّي، وبوابة سياسة التشفير. يضيف إصدار ⁨Enterprise⁩ خلفية حفظ مفاتيح ⁨HSM/PKCS⁩#11 وملف تعريف سياسة تشفير بوضع ⁨FIPS⁩ خلف العقود نفسها. واجهة برمجة التطبيقات العامة (⁨API⁩) متطابقة؛ وتختلف خلفية حفظ المفاتيح وتنفيذ السياسة.