Come lavorare con il motore di crittografia PDF in Python
Aspose.PDF FOSS per Python (import aspose_pdf) implementa la protezione con password PDF a livello di byte attraverso una piccola classe engine interna, EncryptionUtils, in aspose_pdf.engine.encryption. Qui risiedono il cifrario AES-CBC e gli algoritmi di derivazione delle chiavi del PDF Standard Security Handler (ISO 32000 Revisioni 2-6), indipendentemente dall’alta-livello Document API. Questa guida installa il pacchetto e descrive tre attività del motore: crittografare e decrittografare byte grezzi con AES-CBC, derivare le chiavi del Standard Security Handler per una password (Revisioni 2-4) e derivare le chiavi AES-256 per documenti di Revisione 5/6.
Guida passo-passo
Passo 2: Importa le classi richieste
EncryptionUtils non è re-esportato dal pacchetto di livello superiore aspose_pdf — importalo direttamente dal sotto-modulo engine.encryption:
import os
from aspose_pdf.engine.encryption import EncryptionUtilsPasso 3: Crittografa e decrittografa i dati con AES-CBC
encrypt_aes_cbc e decrypt_aes_cbc operano direttamente su qualsiasi chiave da 16, 24 o 32 byte (AES-128, AES-192 o AES-256) senza passare per la derivazione della chiave basata su password. encrypt_aes_cbc applica internamente il padding PKCS7, e decrypt_aes_cbc lo rimuove.
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))Passo 4: Derivare le chiavi del gestore di sicurezza standard per una password (Revisioni 2-4)
compute_owner_key_v4 calcola il valore della password Owner (O) secondo l’Algoritmo 3.3, e compute_user_key_v4 calcola il valore della password User (U) e la chiave di cifratura del file secondo l’Algoritmo 3.4/3.5. verify_password_v4 ricalcola la chiave da una password candidata così puoi confermare che apre il documento prima di fidarti di esso.
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 e u_value sono sempre 32 byte indipendentemente da key_length. verify_password_v4 restituisce None — non un’eccezione — quando la password non corrisponde.
Passo 5: Derivare le chiavi AES-256 per i documenti della Revisione 5/6
I gestori di sicurezza delle revisioni 5 e 6 (AES-256) non usano compute_owner_key_v4/compute_user_key_v4 — hashano la password con compute_hash_v5, che implementa l’“Algoritmo 2.B” di ISO 32000-2. Usa generate_file_id per un nuovo identificatore di file, e derive_object_key per trasformare una chiave di cifratura del file in una chiave per un oggetto indiretto specifico.
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 mescola il numero dell’oggetto e il numero di generazione nella chiave del file, così ogni oggetto indiretto ottiene la propria chiave anche se la chiave del file è condivisa.
Problemi comuni e soluzioni
encrypt_aes_cbc/decrypt_aes_cbc solleva “AES key must be 16, 24, or 32 bytes”. L’argomento key deve essere una vera chiave AES, non una stringa password grezza. Generane una con os.urandom(16 | 24 | 32), oppure derivane una prima con compute_user_key_v4 (Revisioni 2-4) oppure compute_hash_v5 (Revisioni 5/6).
verify_password_v4 returns None invece di sollevare. Ciò significa che la password non corrisponde — non è una condizione di errore da gestire, ma solo un risultato da verificare. Conferma anche che permissions, file_id, key_length, e revision sono gli stessi valori esatti usati quando il documento’s o_value and u_value were originally computed; una discrepanza su uno qualsiasi di essi produce silenziosamente la chiave sbagliata invece di fallire apertamente.
compute_owner_key_v4 con una password del proprietario vuota. Passing "" for owner_password è un comportamento definito dalla specifica, non un bug — l’algoritmo ricade sull’uso della password utente, così le password del proprietario e dell’utente diventano equivalenti.
Chiavi derivate per la revisione o la lunghezza della chiave errate. La Revisione 2 utilizza chiavi RC4 a 40-bit (5 byte), la Revisione 3/4 utilizza chiavi RC4/AES a 128-bit (16 byte), e la Revisione 5/6 utilizza compute_hash_v5 invece di compute_owner_key_v4/compute_user_key_v4 completamente. Mescolare questi non genera un’eccezione — produce semplicemente una chiave che non aprirà il documento.
compute_hash_v5 l’output cambia in modo imprevisto. Il terzo argomento (user_key) è mescolato nell’hash. Passa b"" esplicitamente quando vuoi un hash di convalida della password semplice, poiché ometterlo non è lo stesso di passare un valore vuoto in ogni punto di chiamata.
Domande Frequenti
Perché EncryptionUtils non è importabile direttamente da aspose_pdf?
Vive nel sottopacchetto engine perché è un dettaglio di implementazione interno della protezione con password a livello di documento, non fa parte della superficie pubblica di alto livello API. Importalo esplicitamente da aspose_pdf.engine.encryption.
È compute_hash_v5 lo stesso di una singola chiamata SHA-256?
No. Implementa l’Algoritmo 2.B di ISO 32000-2, che esegue più round utilizzando diversi algoritmi di hash selezionati da valori intermedi — deliberatamente più costoso di una singola chiamata di hash.
generate_file_id() restituisce gli stessi byte ogni volta?
No — restituisce un nuovo valore casuale di 16 byte ad ogni chiamata. Un identificatore di file diverso modifica ogni chiave derivata da esso, anche quando la password è identica.
Tutti gli oggetti indiretti in un PDF crittografato condividono una sola chiave?
No. derive_object_key combina il numero dell’oggetto e il numero di generazione con la chiave di crittografia del file, quindi ogni oggetto indiretto ottiene una chiave distinta per oggetto.
Il codice dell’applicazione dovrebbe chiamare EncryptionUtils direttamente per proteggere con password un PDF?
Di solito no. EncryptionUtils è il motore di basso livello su cui sono costruite le API di password a livello di documento. La maggior parte delle applicazioni dovrebbe proteggere i PDF tramite il API a livello di documento e ricorrere a EncryptionUtils solo per strumenti personalizzati o per l’ispezione forense di file crittografati.