So arbeiten Sie mit der PDF-Verschlüsselungs-Engine in Python
Aspose.PDF FOSS für Python (import aspose_pdf) implementiert den PDF-Passwortschutz auf Byte-Ebene durch eine kleine interne Engine-Klasse, EncryptionUtils, in aspose_pdf.engine.encryption. Hier befinden sich der AES-CBC-Kipher und die Schlüsselableitungs-Algorithmen des PDF Standard Security Handlers (ISO 32000 Revisionen 2 bis 6), unabhängig von der High-Level Document API. Diese Anleitung installiert das Paket und führt durch drei Engine-Aufgaben: Verschlüsseln und Entschlüsseln von Rohbytes mit AES-CBC, Ableiten von Standard-Security-Handler-Schlüsseln für ein Passwort (Revisionen 2-4) und Ableiten von AES-256-Schlüsseln für Dokumente der Revision 5/6.
Schritt-für-Schritt-Anleitung
Schritt 2: Erforderliche Klassen importieren
EncryptionUtils wird nicht vom Top-Level-aspose_pdf-Paket re-exportiert – importieren Sie es direkt aus dem engine.encryption-Untermodul:
import os
from aspose_pdf.engine.encryption import EncryptionUtilsSchritt 3: Daten mit AES-CBC verschlüsseln und entschlüsseln
encrypt_aes_cbc und decrypt_aes_cbc arbeiten direkt mit jedem 16-, 24- oder 32-Byte-Schlüssel (AES-128, AES-192 oder AES-256), ohne die passwortbasierte Schlüsselableitung zu durchlaufen. encrypt_aes_cbc wendet intern PKCS7-Padding an, und decrypt_aes_cbc entfernt es.
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))Schritt 4: Ableiten der Standard-Security-Handler-Schlüssel für ein Passwort (Revisionen 2-4)
compute_owner_key_v4 berechnet den Owner-Passwortwert (O) gemäß Algorithmus 3.3, und compute_user_key_v4 berechnet den User-Passwortwert (U) und den Datei-Verschlüsselungsschlüssel gemäß Algorithmus 3.4/3.5. verify_password_v4 leitet den Schlüssel aus einem Kandidaten-Passwort neu ab, sodass Sie prüfen können, ob es das Dokument öffnet, bevor Sie ihm vertrauen.
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 und u_value haben immer 32 Byte, unabhängig von key_length. verify_password_v4 gibt None zurück — keine Ausnahme — wenn das Passwort nicht übereinstimmt.
Schritt 5: Ableiten von AES-256-Schlüsseln für Dokumente der Revision 5/6
Die Sicherheits-Handler der Revision 5 und 6 (AES-256) verwenden nicht compute_owner_key_v4/compute_user_key_v4 — sie hash-en das Passwort mit compute_hash_v5, das den ISO 32000-2-„Algorithmus 2.B“ implementiert. Verwenden Sie generate_file_id für einen neuen Datei-Identifikator und derive_object_key, um einen Datei-Verschlüsselungsschlüssel in einen Schlüssel für ein bestimmtes indirektes Objekt umzuwandeln.
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 mischt die Objekt-Nummer und die Generation-Nummer in den Datei-Schlüssel ein, sodass jedes indirekte Objekt seinen eigenen Schlüssel erhält, obwohl der Datei-Schlüssel gemeinsam genutzt wird.
Häufige Probleme und Lösungen
encrypt_aes_cbc/decrypt_aes_cbc wirft “AES key must be 16, 24, or 32 bytes”. Der Schlüsselparameter muss ein echter AES-Schlüssel sein, nicht ein roher Passwort-String. Erzeugen Sie einen mit os.urandom(16 | 24 | 32), oder leiten Sie zuerst einen ab mit compute_user_key_v4 (Revisionen 2-4) oder compute_hash_v5 (Revisionen 5/6).
verify_password_v4 returns None anstatt zu werfen. Das bedeutet, dass das Passwort nicht übereinstimmt — es ist kein zu fangender Fehlzustand, sondern nur ein zu prüfendes Ergebnis. Bestätigen Sie außerdem, dass permissions, file_id, key_length, und revision sind exakt dieselben Werte, die verwendet wurden, als das Dokument o_value and u_value ursprünglich berechnet wurden; eine Abweichung bei einem von ihnen führt stillschweigend zum falschen Schlüssel, anstatt laut zu fehlschlagen.
compute_owner_key_v4 mit einem leeren Eigentümer-Passwort. Passing "" for owner_password ist spezifikationsdefiniertes Verhalten, kein Bug — der Algorithmus greift auf das Benutzerpasswort zurück, sodass das Eigentümer- und das Benutzerpasswort gleichwertig werden.
Schlüssel, die für die falsche Revision oder Schlüssellänge abgeleitet wurden. Revision 2 verwendet 40-Bit-(5-Byte-)RC4-Schlüssel, Revision 3/4 verwendet 128-Bit-(16-Byte-)RC4/AES-Schlüssel, und Revision 5/6 verwendet compute_hash_v5 statt compute_owner_key_v4/compute_user_key_v4 vollständig. Das Mischen dieser führt nicht zu einer Ausnahme — es erzeugt lediglich einen Schlüssel, der das Dokument nicht öffnen kann.
compute_hash_v5 Ausgabe ändert sich unerwartet. Das dritte Argument (user_key) wird in den Hash gemischt. Übergeben b"" explizit, wenn Sie einen einfachen Passwortvalidierungs-Hash wünschen, da das Weglassen nicht dasselbe ist wie das Übergeben eines leeren Werts an jeder Aufrufstelle.
Häufig gestellte Fragen
Warum ist EncryptionUtils nicht direkt aus aspose_pdf importierbar?
Es befindet sich im engine Subpaket, weil es ein internes Implementierungsdetail des dokumentenbezogenen Passwortschutzes ist und nicht zum hochrangigen öffentlichen API Interface gehört. Importieren Sie es explizit aus aspose_pdf.engine.encryption.
Ist compute_hash_v5 dasselbe wie ein einzelner SHA-256-Aufruf?
Nein. Es implementiert Algorithmus 2.B aus ISO 32000-2, der mehrere Runden mit verschiedenen Hash-Algorithmen durchführt, die anhand von Zwischenwerten ausgewählt werden — bewusst teurer als ein einzelner Hash-Aufruf.
Gibt generate_file_id() jedes Mal dieselben Bytes zurück?
Nein — es liefert bei jedem Aufruf einen neuen, zufälligen 16-Byte-Wert. Ein anderer Dateikennzeichner ändert jeden daraus abgeleiteten Schlüssel, selbst wenn das Passwort identisch ist.
Verwenden alle indirekten Objekte in einem verschlüsselten PDF denselben Schlüssel?
Nein. derive_object_key kombiniert die Objekt-Nummer und die Generations-Nummer mit dem Datei-Verschlüsselungs-Schlüssel, sodass jedes indirekte Objekt einen eigenen, objektspezifischen Schlüssel erhält.
Soll Anwendungs-Code EncryptionUtils direkt aufrufen, um ein PDF mit einem Passwort zu schützen?
In der Regel nicht. EncryptionUtils ist die Low-Level-Engine, auf der die dokumenten-bezogenen Passwort-APIs aufbauen. Die meisten Anwendungen sollten PDFs über das dokumenten-bezogene API schützen und nur bei benutzerdefinierten Werkzeugen oder forensischer Untersuchung verschlüsselter Dateien zu EncryptionUtils greifen.