كيفية العمل مع محرك تشفير PDF في Python

كيفية العمل مع محرك تشفير PDF في Python

Aspose.PDF FOSS لـ Python (import aspose_pdf) يطبق حماية كلمة مرور PDF على مستوى البايت عبر فئة محرك داخلية صغيرة، EncryptionUtils، في aspose_pdf.engine.encryption. هنا توجد خوارزمية التشفير AES-CBC ومقابض المفتاح لتقنيات اشتقاق مفاتيح معالج الأمان القياسي للـ PDF (ISO 32000 الإصدارات 2 إلى 6) فعليًا، مستقلة عن Document API عالي المستوى. يثبت هذا الدليل الحزمة ويستعرض ثلاث مهام للمحرك: تشفير وفك تشفير البايتات الخام باستخدام AES-CBC، اشتقاق مفاتيح معالج الأمان القياسي لكلمة مرور (الإصدارات 2-4)، واشتقاق مفاتيح AES-256 لمستندات الإصدار 5/6.

دليل خطوة بخطوة

الخطوة 2: استيراد الفئات المطلوبة

EncryptionUtils غير مُعاد تصديره من حزمة aspose_pdf العليا — استورده مباشرةً من الوحدة الفرعية engine.encryption:

import os
from aspose_pdf.engine.encryption import EncryptionUtils

الخطوة 3: تشفير وفك تشفير البيانات باستخدام AES-CBC

encrypt_aes_cbc و decrypt_aes_cbc يعملان مباشرةً على أي مفتاح بطول 16 أو 24 أو 32 بايت (AES-128، AES-192، أو AES-256) دون المرور باشتقاق المفتاح القائم على كلمة المرور. encrypt_aes_cbc يطبق حشو PKCS7 داخليًا، و decrypt_aes_cbc يزيله.

key = os.urandom(32)  # AES-256; 16- and 24-byte keys are also accepted
plaintext = b"Confidential PDF content"

ciphertext = EncryptionUtils.encrypt_aes_cbc(key, plaintext)
decrypted = EncryptionUtils.decrypt_aes_cbc(key, ciphertext)

assert decrypted == plaintext
print("Roundtrip OK, ciphertext length:", len(ciphertext))

الخطوة 4: استخراج مفاتيح معالج الأمان القياسي لكلمة المرور (الإصدارات 2-4)

compute_owner_key_v4 يحسب قيمة كلمة مرور المالك (O) وفقًا للخوارزمية 3.3، وcompute_user_key_v4 يحسب قيمة كلمة مرور المستخدم (U) ومفتاح تشفير الملف وفقًا للخوارزمية 3.4/3.5. verify_password_v4 يعيد استخراج المفتاح من كلمة مرور مرشحة بحيث يمكنك التأكد من أنها تفتح المستند قبل الوثوق به.

owner_password = "owner-secret"
user_password = "user-secret"
key_length = 16   # 128-bit (AES/RC4); Revision 2 uses 5 (40-bit)
revision = 4
file_id = os.urandom(16)
permissions = -4  # standard PDF permissions value

o_value = EncryptionUtils.compute_owner_key_v4(
    owner_password, user_password, key_length, revision
)

u_value, encryption_key = EncryptionUtils.compute_user_key_v4(
    user_password, o_value, permissions, file_id, key_length, revision
)

verified_key = EncryptionUtils.verify_password_v4(
    user_password, u_value, o_value, permissions, file_id, key_length, revision
)
assert verified_key == encryption_key

ciphertext = EncryptionUtils.encrypt_aes_cbc(verified_key, b"Confidential PDF content")
decrypted = EncryptionUtils.decrypt_aes_cbc(verified_key, ciphertext)
assert decrypted == b"Confidential PDF content"

o_value وu_value دائمًا 32 بايت بغض النظر عن key_length. verify_password_v4 يُعيد None — وليس استثناءً — عندما لا تتطابق كلمة المرور.


الخطوة 5: استخراج مفاتيح AES-256 للمستندات بالإصدار 5/6

معالجات الأمان للإصدارين 5 و6 (AES-256) لا تستخدم compute_owner_key_v4/compute_user_key_v4 — بل تقوم بتجزئة كلمة المرور باستخدام compute_hash_v5، التي تنفّذ “الخوارزمية 2.B” في ISO 32000-2. استخدم generate_file_id للحصول على معرف ملف جديد، وderive_object_key لتحويل مفتاح تشفير الملف إلى مفتاح لكائن غير مباشر محدد.

password = b"SecurePassword123"
salt = os.urandom(8)

password_hash = EncryptionUtils.compute_hash_v5(password, salt)
assert len(password_hash) == 32

file_id = EncryptionUtils.generate_file_id()
assert len(file_id) == 16

file_key = os.urandom(32)
object_key = EncryptionUtils.derive_object_key(file_key, 12, 0, use_aes=True)
assert len(object_key) == 16

derive_object_key يخلط رقم الكائن ورقم الجيل في مفتاح الملف، بحيث يحصل كل كائن غير مباشر على مفتاح خاص به رغم أن مفتاح الملف مشترك.


المشكلات الشائعة والحلول

encrypt_aes_cbc/decrypt_aes_cbc يثير “AES key must be 16, 24, or 32 bytes”. يجب أن يكون معامل المفتاح مفتاح AES حقيقي، وليس سلسلة كلمة مرور خام. أنشئ واحدًا باستخدام os.urandom(16 | 24 | 32)، أو استخرج واحدًا أولاً باستخدام compute_user_key_v4 (الإصدارات 2-4) أو compute_hash_v5 (الإصدارات 5/6).

verify_password_v4 returns None بدلاً من رفع الاستثناء. هذا يعني أن كلمة المرور لم تتطابق — ليست حالة خطأ يجب التقاطها، بل نتيجة للتحقق فقط. أيضًا تأكد من أن permissions, file_id, key_length، و revision هي القيم نفسها تمامًا المستخدمة عندما يكون المستند o_value and u_value تم حسابها أصلاً؛ أي عدم تطابق في أيٍّ منها ينتج المفتاح الخطأ بصمت بدلاً من الفشل بصوت عالٍ.

compute_owner_key_v4 مع كلمة مرور مالك فارغة. Passing "" for owner_password سلوك معرف بالمواصفة، ليس خطأً — تعيد الخوارزمية استعمال كلمة مرور المستخدم، وبالتالي تصبح كلمة مرور المالك وكلمة مرور المستخدم متكافئتين.

المفاتيح المستخرجة لإصدار غير صحيح أو طول مفتاح غير صحيح. الإصدار 2 يستخدم مفاتيح RC4 بطول 40-بت (5 بايت)، والإصدار 3/4 يستخدم مفاتيح RC4/AES بطول 128-بت (16 بايت)، والإصدار 5/6 يستخدم compute_hash_v5 بدلاً من compute_owner_key_v4/compute_user_key_v4 تمامًا. خلط هذه لا يثير استثناءً — بل ينتج مفتاحًا لا يفتح المستند.

compute_hash_v5 يتغير الإخراج بشكل غير متوقع. الحجة الثالثة (user_key) يتم دمجها في التجزئة. مرّر b"" بشكل صريح عندما تريد hash للتحقق من كلمة المرور العادي، لأن حذفها لا يعادل تمرير قيمة فارغة في كل موقع استدعاء.


الأسئلة المتكررة

لماذا لا يمكن استيراد EncryptionUtils مباشرةً من aspose_pdf؟

يُوجد في الحزمة الفرعية engine لأنه تفاصيل تنفيذية داخلية لحماية كلمة مرور على مستوى المستند، وليس جزءًا من الواجهة العامة عالية المستوى لـ API. استوردها صراحةً من aspose_pdf.engine.encryption.

هل compute_hash_v5 هو نفسه استدعاء SHA-256 واحد؟

لا. إنه يطبق خوارزمية 2.B من ISO 32000-2، التي تُجري عدة جولات باستخدام خوارزميات تجزئة مختلفة تُختار بناءً على القيم المتوسطة — مما يجعلها أكثر تكلفة عن استدعاء تجزئة واحد.

هل تُعيد generate_file_id() نفس البايتات في كل مرة؟

لا — تُعيد قيمة عشوائية جديدة بطول 16 بايت في كل استدعاء. معرف ملف مختلف يغيّر كل مفتاح مشتق منه، حتى عندما تكون كلمة المرور متطابقة.

هل تشترك جميع الكائنات غير المباشرة في ملف PDF مشفر في مفتاح واحد؟

لا. derive_object_key يجمع رقم الكائن ورقم الجيل مع مفتاح تشفير الملف، لذا يحصل كل كائن غير مباشر على مفتاح مميز خاص به.

هل ينبغي لشفرة التطبيق استدعاء EncryptionUtils مباشرةً لحماية PDF بكلمة مرور؟

عادةً لا. EncryptionUtils هو المحرك منخفض المستوى الذي تُبنى عليه واجهات برمجة التطبيقات لكلمات المرور على مستوى المستند. يجب على معظم التطبيقات حماية ملفات PDF عبر API على مستوى المستند ولا يلجأون إلى EncryptionUtils إلا لأدوات مخصصة أو فحص جنائي للملفات المشفرة.


انظر أيضاً

 العربية