法務、金融、ヘルスケアの分野では、大量のPDFドキュメントを生成することが標準的な慣行です。しかし、ドキュメントの作成は戦いの半分にすぎず、それらを保護することも同様に重要です。毎日数百ギガバイトのPDFを処理するアーカイブパイプラインがある場合、AES-256暗号化の適用はすぐにパフォーマンスのボトルネックになる可能性があります。
この記事では、メモリの枯渇を回避し、暗号化ループを最適化することで、Delphiで高速なAES-256 PDF暗号化を実現する方法を探ります。
PDF暗号化仕様
PDFのセキュリティは大きく進化しました。初期のバージョンでは40ビットのRC4が使用されていましたが、現在では簡単に破られてしまいます。現在の標準(PDF 1.7拡張レベル3およびPDF 2.0)では、AES-256暗号化が義務付けられています。
PDFでは、ファイルブロック全体を暗号化するわけではありません。ドキュメント構造(XRefテーブルや辞書の構造)は平文のままです。代わりに、ストリーム(画像やページコンテンツの生データ)と文字列(メタデータテキストなど)を暗号化します。これには、パーサーがデータを抽出し、AES CBC(暗号ブロック連鎖)を適用して、書き戻す必要があります。
ボトルネック: メモリへのロード
巨大なPDF(たとえば、2GBのスキャン済みアーカイブ)を暗号化する際によくある間違いは、暗号化エンジンに渡す前にストリーム全体を`TMemoryStream`にロードすることです。これにより、32ビットプロセスではメモリ不足(OOM)例外が発生し、64ビットプロセスでは大量のページフォールトが発生します。
Delphiでのストリーミング暗号化
解決策は、チャンク化されたストリーミングアプローチを使用することです。Windows Cryptography API: Next Generation (CNG) またはOpenSSLのようなライブラリを使用して、PDFストリームを64KBのブロックで読み取り、ブロックを暗号化し、出力ディスクストリームに直接書き込むことができます。
ストリームのバッファリングされた暗号化ループを示す概念的なDelphiの例を次に示します。
uses
System.Classes, System.SysUtils;
const
BUFFER_SIZE = 65536; // 64KB chunks
// This represents your AES-256 encryption routine
procedure EncryptStreamChunk(const InBuffer; var OutBuffer; BytesRead: Integer; const Key: TBytes; const IV: TBytes);
begin
// Call to Windows CNG (BCryptEncrypt) or OpenSSL (EVP_EncryptUpdate)
// ...
end;
procedure EncryptLargePDFStream(InputStream, OutputStream: TStream; const Key, IV: TBytes);
var
InBuffer, OutBuffer: array of Byte;
BytesRead: Integer;
begin
SetLength(InBuffer, BUFFER_SIZE);
// AES CBC requires padding, so the output buffer must be slightly larger
SetLength(OutBuffer, BUFFER_SIZE + 16);
InputStream.Position := 0;
OutputStream.Position := 0;
repeat
BytesRead := InputStream.Read(InBuffer[0], BUFFER_SIZE);
if BytesRead > 0 then
begin
EncryptStreamChunk(InBuffer[0], OutBuffer[0], BytesRead, Key, IV);
// Write the encrypted cipher text directly to disk
OutputStream.Write(OutBuffer[0], BytesRead); // Note: padding logic omitted for brevity
end;
until BytesRead < BUFFER_SIZE;
end;
暗号化バックエンドの最適化
Delphi開発者には、AESバックエンドとしていくつかの選択肢があります。
- ネイティブのDelphi実装: デプロイは簡単ですが、純粋にソフトウェアで実行されるため、遅くなることがよくあります。
- Windows CNG (BCrypt): 高度に最適化されており、ハードウェアアクセラレーション(最新のIntel/AMD CPUのAES-NI命令)を利用できます。
- OpenSSL (libcrypto): 業界標準であり、信じられないほど高速ですが、外部DLLを同梱する必要があります。
高スループットのサーバーアプリケーションでは、ハードウェアで高速化されたAES-NIが必須です。DelphiでWindows CNGを使用する場合、BCryptEncrypt関数をマッピングすることで、アプリケーションは重い処理をCPUの専用暗号化シリコンにオフロードでき、暗号化のオーバーヘッドを実質的にほぼゼロに減らすことができます。
結論
ギガバイト規模のPDFを暗号化する場合は、完全なメモリバッファリングではなくストリームのチャンク化に依存し、暗号化バックエンドがハードウェアAES-NIアクセラレーションを利用していることを確認してください。この組み合わせにより、アーカイブパイプラインがCPUバウンドにならずに、NVMeドライブの速度で確実に実行されるようになります。
注: チャンク化されたストリーミングを利用した高速AES-256暗号化は、HotPDF VCL Componentでネイティブにサポートされています。