PythonでPDF暗号化エンジンを使用する方法

PythonでPDF暗号化エンジンを使用する方法

Aspose.PDF FOSS for Python (import aspose_pdf) は、aspose_pdf.engine.encryption にある小さな内部エンジン・クラス EncryptionUtils を通じて、バイトレベルで PDF パスワード保護を実装します。ここに、AES-CBC 暗号と PDF 標準セキュリティハンドラの鍵導出アルゴリズム(ISO 32000 リビジョン 2〜6)が実装されており、上位レベルの Document API とは独立しています。本ガイドではパッケージのインストール方法と、3 つのエンジンタスク(AES-CBC を使用した生バイトの暗号化・復号、パスワード用の標準セキュリティハンドラ鍵の導出(リビジョン 2-4)、リビジョン 5/6 文書用の AES-256 鍵の導出)を順に説明します。

ステップバイステップガイド

ステップ 2: 必要なクラスをインポートする

EncryptionUtils はトップレベルの aspose_pdf パッケージから再エクスポートされていません — engine.encryption サブモジュールから直接インポートしてください:

import os
from aspose_pdf.engine.encryption import EncryptionUtils

ステップ 3: AES-CBC を使用してデータを暗号化および復号する

encrypt_aes_cbc と decrypt_aes_cbc は、パスワードベースの鍵導出を経ずに、任意の 16、24、または 32 バイト鍵(AES-128、AES-192、または AES-256)で直接動作します。encrypt_aes_cbc は内部で PKCS7 パディングを適用し、decrypt_aes_cbc がそれを除去します。

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))

ステップ 4: パスワード用の標準セキュリティハンドラキーを導出する(リビジョン 2-4)

compute_owner_key_v4 はアルゴリズム 3.3 に従ってオーナーパスワード値 (O) を計算し、compute_user_key_v4 はアルゴリズム 3.4/3.5 に従ってユーザーパスワード値 (U) とファイル暗号化キーを計算します。verify_password_v4 は候補パスワードからキーを再導出し、信頼する前にドキュメントが開くことを確認できます。

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 と u_value は、key_length に関係なく常に 32 バイトです。パスワードが一致しない場合、verify_password_v4 は None を返します — 例外ではありません —。


ステップ 5: リビジョン 5/6 ドキュメント用の AES-256 キーを導出する

リビジョン 5 および 6 のセキュリティハンドラ (AES-256) は compute_owner_key_v4/compute_user_key_v4 を使用せず、compute_hash_v5 でパスワードをハッシュします。これは ISO 32000-2 の「アルゴリズム 2.B」を実装しています。新しいファイル識別子には generate_file_id を使用し、ファイル暗号化キーを特定の間接オブジェクト用のキーに変換するには derive_object_key を使用します。

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 はオブジェクト番号と世代番号をファイルキーに混ぜ込むため、ファイルキーは共有されていても各間接オブジェクトが独自のキーを持ちます。


一般的な問題と修正方法

encrypt_aes_cbc/decrypt_aes_cbc “AES key must be 16, 24, or 32 bytes"というエラーが発生します。 キー引数は生のパスワード文字列ではなく、実際のAESキーである必要があります。次の方法で生成してください os.urandom(16 | 24 | 32), または先に次を使用して導出してください compute_user_key_v4 (Revisions 2-4)または compute_hash_v5 (Revisions 5/6)。

verify_password_v4 returns None 例外を送出する代わりに. これはパスワードが一致しなかったことを意味します — 例外として捕捉すべきエラーではなく、単なる結果としてチェックすべきです。また、次を確認してください permissions, file_id, key_length、そして revision は、ドキュメントが最初に計算されたときに使用された正確に同じ値です o_value and u_value は元々計算されたものです; それらのいずれかが不一致になると、エラーを大声で出す代わりに黙って間違ったキーが生成されます。

compute_owner_key_v4 空の所有者パスワードで。 Passing "" for owner_password は仕様で定義された動作であり、バグではありません — アルゴリズムはユーザーパスワードの使用にフォールバックするため、所有者パスワードとユーザーパスワードは同等になります。

誤ったリビジョンまたはキー長で導出されたキーです。 リビジョン2は40ビット(5バイト)のRC4キーを使用し、リビジョン3/4は128ビット(16バイト)のRC4/AESキーを使用し、リビジョン5/6は compute_hash_v5 の代わりに compute_owner_key_v4/compute_user_key_v4 完全に。これらを混在させても例外は発生せず、単にドキュメントを開くことができないキーが生成されるだけです。

compute_hash_v5 出力が予期せず変わります。 第3引数 (user_key) がハッシュに混入します。渡してください b"" プレーンなパスワード検証ハッシュが必要な場合は、明示的に指定してください。省略することは、すべての呼び出し箇所で空の値を渡すのと同じではありません。


よくある質問

なぜ EncryptionUtils は aspose_pdf から直接インポートできないのですか?

それは engine サブパッケージに存在します。これはドキュメントレベルのパスワード保護の内部実装詳細であり、上位レベルの公開 API インターフェースの一部ではありません。aspose_pdf.engine.encryption から明示的にインポートしてください。

compute_hash_v5 は単一の SHA-256 呼び出しと同じですか?

いいえ。ISO 32000-2 のアルゴリズム 2.B を実装しており、途中の値で選択される異なるハッシュアルゴリズムを使用して複数ラウンドを実行します—単一のハッシュ呼び出しより意図的にコストが高くなるよう設計されています。

generate_file_id() は毎回同じバイトを返しますか?

いいえ — 毎回の呼び出しで新しいランダムな 16 バイトの値を返します。異なるファイル識別子は、それに由来するすべてのキーを変更します。パスワードが同一でもです。

暗号化された PDF のすべての間接オブジェクトは 1 つのキーを共有しますか?

いいえ。derive_object_key はオブジェクト番号と世代番号をファイル暗号化キーと組み合わせるため、各間接オブジェクトは個別のオブジェクト固有キーを取得します。

アプリケーションコードは PDF をパスワード保護するために EncryptionUtils を直接呼び出すべきですか?

通常はそうではありません。EncryptionUtils は文書レベルのパスワード API が構築されている低レベルエンジンです。ほとんどのアプリケーションは文書レベルの API を通じて PDF を保護すべきで、EncryptionUtils はカスタムツールや暗号化ファイルのフォレンジック検査が必要な場合にのみ使用してください。


参照

 日本語