Як працювати з механізмом шифрування PDF у Python

Як працювати з механізмом шифрування PDF у Python

Aspose.PDF FOSS для Python (import aspose_pdf) реалізує захист PDF паролем на рівні байтів за допомогою невеликого внутрішнього класу механізму, EncryptionUtils, у aspose_pdf.engine.encryption. Саме тут розташовані шифр AES-CBC та алгоритми виведення ключів PDF Standard Security Handler (ISO 32000 Ревізії 2-6), незалежно від високорівневого Document API. Цей посібник встановлює пакет і покроково розглядає три завдання механізму: шифрування та розшифрування сирих байтів за допомогою AES-CBC, виведення ключів Standard Security Handler для пароля (Ревізії 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, що реалізує “Algorithm 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”. Аргумент key має бути справжнім AES-ключем, а не необробленим рядком пароля. Створіть його за допомогою os.urandom(16 | 24 | 32), або спочатку отримайте його за допомогою compute_user_key_v4 (Revisions 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 використовує 40-бітові (5-байтові) RC4-ключі, ревізії 3/4 — 128-бітові (16-байтові) RC4/AES-ключі, а ревізії 5/6 — compute_hash_v5 замість compute_owner_key_v4/compute_user_key_v4 повністю. Поєднання цих параметрів не викликає винятку — воно просто створює ключ, який не відкриє документ.

compute_hash_v5 вихід змінюється несподівано. Третій аргумент (user_key) змішується у хеш. Передайте b"" явно, коли вам потрібен простий хеш перевірки пароля, оскільки його пропуск не те саме, що передача порожнього значення у всіх місцях виклику.


Поширені запитання

Чому 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 — це низькорівневий движок, на якому базуються API паролів рівня документа. Більшість застосунків мають захищати PDF через API рівня документа і звертатися до EncryptionUtils лише для спеціальних інструментів або судово-експертної перевірки зашифрованих файлів.


Дивіться також

 Українська