【PowerShell】iTextSharpでPDFを暗号化する

Code

はじまり

リサちゃん
リサちゃん

PDFを暗号化する?

135ml
135ml

それじゃあやってみようか。

リサちゃん
リサちゃん

あっ。

PDFにパスワードを付ける

PDFファイルには、正しいパスワードを入力しないと開けないようにすることが出来る暗号化機能があります。

しかし、その暗号化を施すためには、Adobe Acrobatのような高価なPDFエディタを使ったり、オンラインのPDFツールにアップロードする必要があったりします。暗号化するようなPDFファイルなのですから、中の情報が独り歩きしてしまうような懸念を減らしたいです。

そこで今回は、「iTextSharp」というパッケージをローカルにダウンロードして、そのパッケージとPowerShellのみでその暗号化を完結できるように、PDFにパスワードを付ける処理を実装していきたいと思います。それでは行ってみましょう。

ちなみに、PDFをちゃんと暗号化出来ると、Microsoft EdgeなどでPDFを開いた時にこのようなパスワード入力画面が表示されるようになります。

iTextSharpでPDFを暗号化する

それではまずは、iTextSharpをローカルにダウンロードします。このiTextSharpのパッケージのバージョンですが、今回は「5.5.13」を使います。

iTextSharp 5.5.13
iText is a PDF library that allows you to CREATE, ADAPT, INSPECT and MAINTAIN documents in the Portable Document Format ...

iTextSharpの新しいバージョンを利用すると、沼にハマる可能性があります。非推奨です。以下の記事でもそのことを紹介しています。

そして、PDFを暗号化するための処理を以下のように実装しました。

ChatGPT、NVIDIA NemotronやGeminiに聞きましたが、最も良い回答をくれたのはGeminiでしたね。iTextSharpを実際に使う部分でお世話になりました。(NVIDIA Nemotronは、iTextSharpの二重インポートを阻止するコードを書いてくれました。)

function Encrypt-PdfWithITextSharp {
  <#
  .SYNOPSIS
    Encrypts the specified PDF file.
  .DESCRIPTION
    Encrypts a PDF file using the iTextSharp library.
  .PARAMETER InputPath
    The path to the PDF file to encrypt.
  .PARAMETER OutputPath
    The output path for the encrypted PDF file.
  .PARAMETER Password
    The encryption password for the PDF file.
  .PARAMETER iTextSharpPath
    The iTextSharpPath library file.
  .EXAMPLE
    Encrypt-PdfWithITextSharp -InputPath "C:\Input.pdf" -OutputPath "C:\Encrypted_Output.pdf" -Password "YourStrongPassword"
  #>

  param (
    [string]$InputPath,
    [string]$OutputPath,
    [string]$Password,
    [string]$ITextSharpPath
  )

  # Load iTextSharp.dll
  if (-not ([System.AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.Location -eq $ITextSharpPath })) {
    Unblock-File -Path $iTextSharpPath;
    Add-Type -Path $ITextSharpPath
  }
  Write-Host ("{0}: InputPath:" -f $MyInvocation.MyCommand.Name);
  Write-Output $InputPath;
  Write-Host ("{0}: OutputPath:" -f $MyInvocation.MyCommand.Name);
  Write-Output $OutputPath;
  Write-Host ("{0}: ITextSharpPath:" -f $MyInvocation.MyCommand.Name);
  Write-Output $ITextSharpPath;

  $permissions = [iTextSharp.text.pdf.PdfWriter]::ALLOW_PRINTING -bor [iTextSharp.text.pdf.PdfWriter]::ALLOW_COPY -bor [iTextSharp.text.pdf.PdfWriter]::ALLOW_MODIFY_ANNOTATIONS # Set permissions
  Write-Host ("{0}: permission set" -f $MyInvocation.MyCommand.Name);

  try {
    # Create instances of PDFReader and PDFStamper
    $pdfReader = New-Object iTextSharp.text.pdf.PdfReader($InputPath)
    Write-Host ("{0}: PDF reader loaded.`n" -f $MyInvocation.MyCommand.Name);
    $pdfStamper = New-Object iTextSharp.text.pdf.PdfStamper($pdfReader, (New-Object System.IO.FileStream($OutputPath, [System.IO.FileMode]::Create)))
    Write-Host ("{0}: encrypting PDF.`n" -f $MyInvocation.MyCommand.Name);

    # Encrypt the PDF
    $ownerPassword = $Password # Set owner password
    $userPassword = $Password # Set user password
    Write-Host ("{0}: ownerPassword:" -f $MyInvocation.MyCommand.Name);
    Write-Output $ownerPassword;
    Write-Host ("{0}: userPassword:" -f $MyInvocation.MyCommand.Name);
    Write-Output $userPassword;
    $pdfStamper.Writer.SetEncryption(
      [System.Text.Encoding]::UTF8.GetBytes($userPassword),
      [System.Text.Encoding]::UTF8.GetBytes($ownerPassword),
      $permissions,
      [iTextSharp.text.pdf.PdfWriter]::ENCRYPTION_AES_256
    )

    # Close PDFStamper and release resources
    Write-Host ("{0}: closing PDF.`n" -f $MyInvocation.MyCommand.Name);
    $pdfStamper.Close()
    $pdfReader.Close()

    Write-Host ("{0}: PDF encryption complete.`n" -f $MyInvocation.MyCommand.Name);
  }
  catch {
    Write-Error "An error occurred during PDF encryption. Details: $($Error[0].Message)"
  }
}

function Encrypt-MultiPdfWithITextSharp(){
  <#
  .SYNOPSIS
    Encrypts multiple PDF files.
  .DESCRIPTION
    Encrypts multiple PDF files using the iTextSharp library.  The output files are named with "_encrypted" appended before the .pdf extension.
  .PARAMETER ImgPathList
    An array of paths to the PDF files to encrypt.
  .PARAMETER Password
    The encryption password for the PDF files.
  .PARAMETER iTextSharpPath
    The path to the iTextSharp library DLL.
  .EXAMPLE
    Encrypt-MultiPdfWithITextSharp -ImgPathList $pdfFiles -Password "SecurePassword" -ITextSharpPath "C:\path\to\itextsharp.dll"
  #>

  param (
    [array]$ImgPathList,
    [string]$Password,
    [string]$ITextSharpPath
  )

  # Get image files to add into PDF.
  foreach ($path in $imgPathList) {
    Write-Output $path;
    Write-Output $path.GetType().FullName;
    Write-Output $path.FullName.GetType().FullName;

    $ext = ".pdf"
    $outputPath = $path.FullName.Replace($ext, "_encrypted{0}" -f $ext)
    Encrypt-PdfWithITextSharp -InputPath $path -OutputPath $outputPath -Password $Password -ITextSharpPath $iTextFilePath;
  }
}


$targetFolder = (Get-Location).Path;
$imgPathList = Get-ChildItem $targetFolder -Recurse -File -Include *.pdf -Exclude PDFsharp*;
$password = Read-Host "Input password for encrypted PDF......";
$iTextFilePath = "{0}\lib\itextsharp.dll" -f $targetFolder;

Encrypt-MultiPdfWithITextSharp -ImgPathList $imgPathList -Password $password -ITextSharpPath $iTextFilePath;

$tmp = Read-Host "Input 'y' to terminate this process......";

Encrypt-MultiPdfWithITextSharpに一つのディレクトリ内にあるPDFの配列を渡して、Encrypt-PdfWithITextSharpで一つずつ暗号化していきます。

iTextSharpのDLLを読み込む際には、既に読み込まれているかどうかを判断してから読み込み処理に入るようにしています。

  if (-not ([System.AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.Location -eq $ITextSharpPath })) {
    Unblock-File -Path $iTextSharpPath;
    Add-Type -Path $ITextSharpPath
  }

$pdfReaderにPDFを開かせて、$pdfStamperに開いているPDFを暗号化してもらいます。

    # Create instances of PDFReader and PDFStamper
    $pdfReader = New-Object iTextSharp.text.pdf.PdfReader($InputPath)
    Write-Host ("{0}: PDF reader loaded.`n" -f $MyInvocation.MyCommand.Name);
    $pdfStamper = New-Object iTextSharp.text.pdf.PdfStamper($pdfReader, (New-Object System.IO.FileStream($OutputPath, [System.IO.FileMode]::Create)))
    Write-Host ("{0}: encrypting PDF.`n" -f $MyInvocation.MyCommand.Name);

$pdfStamperに暗号化してもらう時の処理はこんな感じで引数を指定をします。PDFのパスワードは2種類付けることが可能です。PDFのオーナー用と閲覧者用のパスワードです。$permissionsで、PDFのオーナーじゃないと行えなくなるアクションを設定します。(Adobe Acrobatでパスワードを付ける時は、確かオーナー用じゃないと印刷が出来なかった気がします。こんな風に仕分けられていたんですね。)

    $permissions = [iTextSharp.text.pdf.PdfWriter]::ALLOW_PRINTING -bor [iTextSharp.text.pdf.PdfWriter]::ALLOW_COPY -bor [iTextSharp.text.pdf.PdfWriter]::ALLOW_MODIFY_ANNOTATIONS # Set permissions

    $pdfStamper.Writer.SetEncryption(
      [System.Text.Encoding]::UTF8.GetBytes($userPassword),
      [System.Text.Encoding]::UTF8.GetBytes($ownerPassword),
      $permissions,
      [iTextSharp.text.pdf.PdfWriter]::ENCRYPTION_AES_256
    )

New-Objectをしたので、オブジェクトを解放することを忘れない。

    $pdfStamper.Close()
    $pdfReader.Close()

まとめ

今回は、iTextSharpをPowerShellで叩いて、PDFを暗号化する処理を実装する試みでした。

以下が、本記事のまとめです。

  • iTextSharpは、「5.5.13」をダウンロードした。おそらくこれが最新の安定版である。
  • iTextSharpのDLLを読み込む際には、既に読み込まれているかどうかを判断することが出来る。
  • PDFのパスワードは、オーナー用と閲覧者用の2種類を付けることが可能である。
  • New-Objectをしたので、オブジェクトを解放することを忘れない。

その他のPowerShell関連の記事

おしまい

リサちゃん
リサちゃん

マジで開けなくなったわ

135ml
135ml

これで安全だ。

以上になります!

コメント

タイトルとURLをコピーしました