Cum să lucrați cu motorul de criptare PDF în Python
Aspose.PDF FOSS pentru Python (import aspose_pdf) implementează protecția prin parolă PDF la nivel de octet printr-o mică clasă internă a motorului, EncryptionUtils, în aspose_pdf.engine.encryption. Aici se găsesc cifrul AES-CBC și algoritmii de derivare a cheilor ai PDF Standard Security Handler (Revizii ISO 32000 2 până la 6), independent de Document API de nivel înalt. Acest ghid instalează pachetul și parcurge trei sarcini ale motorului: criptarea și decriptarea octeților brut cu AES-CBC, derivarea cheilor Standard Security Handler pentru o parolă (Reviziile 2-4) și derivarea cheilor AES-256 pentru documente cu Revizia 5/6.
Ghid pas cu pas
Pasul 2: Importați clasele necesare
EncryptionUtils nu este reexportat din pachetul de nivel superior aspose_pdf — importați-l direct din submodulul engine.encryption:
import os
from aspose_pdf.engine.encryption import EncryptionUtilsPasul 3: Criptați și decriptați datele cu AES-CBC
encrypt_aes_cbc și decrypt_aes_cbc lucrează direct pe orice cheie de 16, 24 sau 32 de octeți (AES-128, AES-192 sau AES-256) fără a trece prin derivarea cheii bazată pe parolă. encrypt_aes_cbc aplică padding PKCS7 intern, iar decrypt_aes_cbc îl elimină.
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))Pasul 4: Derivați cheile standard ale gestionării securității pentru o parolă (Reviziile 2-4)
compute_owner_key_v4 calculează valoarea parolei Owner (O) conform Algoritmului 3.3, iar compute_user_key_v4 calculează valoarea parolei User (U) și cheia de criptare a fișierului conform Algoritmului 3.4/3.5. verify_password_v4 rederivează cheia dintr-o parolă candidată astfel încât să puteți confirma că deschide documentul înainte de a avea încredere în el.
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 și u_value au întotdeauna 32 de octeți, indiferent de key_length. verify_password_v4 returnează None — nu o excepție — când parola nu se potrivește.
Pasul 5: Derivați cheile AES-256 pentru documentele cu revizia 5/6
Gestionarele de securitate pentru reviziile 5 și 6 (AES-256) nu utilizează compute_owner_key_v4/compute_user_key_v4 — ele hash-uiesc parola cu compute_hash_v5, care implementează „Algorithm 2.B” din ISO 32000-2. Folosiți generate_file_id pentru un identificator de fișier nou, și derive_object_key pentru a transforma o cheie de criptare a fișierului într-o cheie pentru un obiect indirect specific.
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) == 16derive_object_key amestecă numărul obiectului și numărul de generație în cheia fișierului, astfel încât fiecare obiect indirect primește propria cheie, chiar dacă cheia fișierului este partajată.
Probleme comune și remedieri
encrypt_aes_cbc/decrypt_aes_cbc ridică „AES key must be 16, 24, or 32 bytes”. Argumentul key trebuie să fie o cheie AES reală, nu un șir de parolă brut. Generați una cu os.urandom(16 | 24 | 32), sau derivați una mai întâi cu compute_user_key_v4 (Revizii 2-4) sau compute_hash_v5 (Revizii 5/6).
verify_password_v4 returns None în loc de a ridica. Aceasta înseamnă că parola nu a corespuns — nu este o condiție de eroare de prins, ci doar un rezultat de verificat. De asemenea, confirmați că permissions, file_id, key_length, și revision sunt exact aceleași valori utilizate când documentul o_value and u_value au fost calculate inițial; o neconcordanță în oricare dintre ele produce în tăcere cheia greșită în loc să eșueze zgomotos.
compute_owner_key_v4 cu o parolă de proprietar goală. Passing "" for owner_password este un comportament definit de specificație, nu un bug — algoritmul revine la utilizarea parolei de utilizator, astfel încât parolele de proprietar și de utilizator devin echivalente.
Chei generate pentru revizia greșită sau lungimea cheii incorectă. Revizia 2 folosește chei RC4 de 40-bit (5-byte), revizia 3/4 folosește chei RC4/AES de 128-bit (16-byte), iar revizia 5/6 folosește compute_hash_v5 în loc de compute_owner_key_v4/compute_user_key_v4 în totalitate. Amestecarea acestora nu declanșează o excepție — produce doar o cheie care nu va deschide documentul.
compute_hash_v5 ieșirea se modifică neașteptat. Al treilea argument (user_key) este amestecat în hash. Transmite b"" explicit când dorești un hash simplu de validare a parolei, deoarece omiterea lui nu este același lucru cu transmiterea unei valori goale în fiecare punct de apelare.
Întrebări frecvente
De ce nu poate fi EncryptionUtils importat direct din aspose_pdf?
Se află în subpachetul engine deoarece este un detaliu de implementare intern al protecției cu parolă la nivel de document, nu face parte din suprafața publică de nivel înalt API. Importați-l explicit din aspose_pdf.engine.encryption.
Este compute_hash_v5 același cu un singur apel SHA-256?
Nu. Implementă Algoritmul 2.B al ISO 32000-2, care efectuează multiple runde folosind diferite algoritmi de hash selectați prin valori intermediare — deliberat mai costisitor decât un singur apel de hash.
Returnează generate_file_id() aceiași octeți de fiecare dată?
Nu — returnează o valoare proaspătă, aleatoare de 16 octeți la fiecare apel. Un identificator de fișier diferit modifică fiecare cheie derivată din acesta, chiar și atunci când parola este identică.
Toate obiectele indirecte dintr-un PDF criptat împărtășesc aceeași cheie?
Nu. derive_object_key combină numărul obiectului și numărul de generație cu cheia de criptare a fișierului, astfel încât fiecare obiect indirect primește o cheie distinctă per-object.
Ar trebui codul aplicației să apeleze EncryptionUtils direct pentru a proteja cu parolă un PDF?
De obicei nu. EncryptionUtils este motorul de nivel jos pe care se bazează API-urile de parolă la nivel de document. Majoritatea aplicațiilor ar trebui să protejeze PDF-urile prin API la nivel de document și să apeleze la EncryptionUtils doar pentru unelte personalizate sau inspecție forensic a fișierelor criptate.