Hur du arbetar med PDF-krypteringsmotorn i Python

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 EncryptionUtils

Steg 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) == 16

derive_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.


Se även

 Svenska