Hur du arbetar med PDF-krypteringsmotorn i Python
Aspose.PDF FOSS för Python (import aspose_pdf) implementerar PDF-lösenordsskydd på byte-nivå via en liten intern motor-klass, EncryptionUtils, i aspose_pdf.engine.encryption. Här finns AES-CBC-chiffret och PDF Standard Security Handlers nyckelderiveringsalgoritmer (ISO 32000 Revisioner 2 till 6) faktiskt, oberoende av den högre nivåns Document API. Denna guide installerar paketet och går igenom tre motoruppgifter: kryptera och dekryptera råa byte med AES-CBC, härleda Standard Security Handler-nycklar för ett lösenord (Revisioner 2-4) och härleda AES-256-nycklar för Revision 5/6-dokument.
Steg-för-steg-guide
Steg 2: Importera nödvändiga klasser
EncryptionUtils exporteras inte om från top-nivå aspose_pdf-paketet — importera den direkt från engine.encryption-undermodulen:
import os
from aspose_pdf.engine.encryption import EncryptionUtilsSteg 3: Kryptera och dekryptera data med AES-CBC
encrypt_aes_cbc och decrypt_aes_cbc arbetar direkt på vilken 16-, 24- eller 32-byte nyckel (AES-128, AES-192 eller AES-256) som helst utan att gå via lösenordsbaserad nyckelderivering. encrypt_aes_cbc applicerar PKCS7-padding internt, och decrypt_aes_cbc tar bort den.
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))Steg 4: Härleda standardnycklar för säkerhetshanterare för ett lösenord (Revisioner 2-4)
compute_owner_key_v4 beräknar ägarlösenordsvärdet (O) enligt Algoritm 3.3, och compute_user_key_v4 beräknar användarlösenordsvärdet (U) och filkrypteringsnyckeln enligt Algoritm 3.4/3.5. verify_password_v4 härleder nyckeln på nytt från ett kandidatlösenord så att du kan bekräfta att det öppnar dokumentet innan du litar på det.
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 och u_value är alltid 32 byte oavsett key_length. verify_password_v4 returnerar None — inte ett undantag — när lösenordet inte matchar.
Steg 5: Härleda AES-256-nycklar för dokument med revision 5/6
Säkerhetshanterare för revision 5 och 6 (AES-256) använder inte compute_owner_key_v4/compute_user_key_v4 — de hashar lösenordet med compute_hash_v5, vilket implementerar ISO 32000-2:s “Algorithm 2.B”. Använd generate_file_id för ett nytt filidentifierare, och derive_object_key för att omvandla en filkrypteringsnyckel till en nyckel för ett specifikt indirekt objekt.
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 blandar objektnumret och generationsnumret i filnyckeln, så att varje indirekt objekt får sin egen nyckel även om filnyckeln delas.
Vanliga problem och lösningar
encrypt_aes_cbc/decrypt_aes_cbc kastar “AES key must be 16, 24, or 32 bytes”. Argumentet key måste vara en riktig AES-nyckel, inte en rå lösenordssträng. Generera en med os.urandom(16 | 24 | 32), eller härleda en först med compute_user_key_v4 (Revisioner 2-4) eller compute_hash_v5 (Revisioner 5/6).
verify_password_v4 returns None istället för att kasta. Detta betyder att lösenordet inte matchade — det är inte ett felvillkor att fånga, bara ett resultat att kontrollera. Bekräfta också att permissions, file_id, key_length, och revision är exakt samma värden som användes när dokumentets o_value and u_value beräknades ursprungligen; en avvikelse i någon av dem producerar tyst fel nyckel istället för att misslyckas med ett tydligt fel.
compute_owner_key_v4 med ett tomt ägarlösenord. Passing "" for owner_password är specifikationsdefinierat beteende, inte en bugg — algoritmen faller tillbaka på att använda användarlösenordet, så ägarlösenordet och användarlösenordet blir ekvivalenta.
Nycklar härledda för fel revision eller nyckellängd. Revision 2 använder 40-bit (5-byte) RC4-nycklar, Revision 3/4 använder 128-bit (16-byte) RC4/AES-nycklar, och Revision 5/6 använder compute_hash_v5 istället för compute_owner_key_v4/compute_user_key_v4 helt. Att blanda dessa ger inte ett undantag — det producerar bara en nyckel som inte kan öppna dokumentet.
compute_hash_v5 utdata förändras oväntat. Det tredje argumentet (user_key) blandas in i hashvärdet. Skicka b"" explicitt när du vill ha en enkel lösenordsvalideringshash, eftersom utelämnande av den inte är detsamma som att skicka ett tomt värde i varje anropsställe.
Vanliga frågor
Varför går det inte att importera EncryptionUtils direkt från aspose_pdf?
Den finns i engine subpackage eftersom den är en intern implementeringsdetalj för dokumentnivåns lösenordsskydd, inte en del av det högnivå-publika API-gränssnittet. Importera den explicit från aspose_pdf.engine.encryption.
Är compute_hash_v5 detsamma som ett enkelt SHA-256-anrop?
Nej. Det implementerar ISO 32000-2:s Algoritm 2.B, som utför flera rundor med olika hash-algoritmer som väljs av mellanliggande värden — avsiktligt mer kostsamt än ett enskilt hash-anrop.
Returnerar generate_file_id() samma byte varje gång?
Nej — den returnerar ett nytt, slumpmässigt 16-byte-värde vid varje anrop. En annan filidentifierare ändrar varje nyckel som härleds från den, även när lösenordet är identiskt.
Delar alla indirekta objekt i en krypterad PDF samma nyckel?
Nej. derive_object_key kombinerar objektnumret och generationsnumret med filens krypteringsnyckel, så varje indirekt objekt får en unik per-objekt-nyckel.
Bör applikationskod anropa EncryptionUtils direkt för att lösenordsskydda en PDF?
Vanligtvis inte. EncryptionUtils är den lågnivå-motor som dokument-nivå-lösenords-API:er är byggda på. De flesta applikationer bör skydda PDF-filer via dokument-nivå-API och endast använda EncryptionUtils för anpassade verktyg eller forensisk inspektion av krypterade filer.