PyPDF2でpdfを暗号化・復号する


# 動作環境
```sh
python -V

#　実行結果
Python 3.11.0
```

# PyPDF2をインストールします
```sh
pip install PyPDF2
```

# pdfファイルを暗号化する
イメージとしてはpdfファイルを新規で作成し、元データを読み込んで、暗号化して保存するような流れになります。
実行すると元データと同じ場所に`{ファイル名}_enc.pdf`が作成されます。
```python
# モジュールのインポート
from PyPDF2 import PdfFileWriter,PdfFileReader

# 対象のpdfファイルの指定
filename = "./sample.pdf"

#PdfFileWriterオブジェクトを作成
out = PdfFileWriter()

#指定したPDFファイルをPdfFileReaderで開く
file = PdfFileReader(filename)

#PDFファイルのページ数を取得
num = file.numPages

#PDFファイルの全てのページを読み込んで新しいファイルにページを追加する
for idx in range(num):
    page = file.getPage(idx)
    out.addPage(page)

#パスワードを指定
password = 'hogehoge'

#入力したパスワードで新しいファイルを暗号化
out.encrypt(password)

#暗号化された情報をファイルに書き込み
with open(filename.replace('.pdf','') + r'_enc.pdf','wb') as f:
    out.write(f)
```

# pdfファイルにパスワードが設定されているか確認する
`isEncrypted`を使用してpdfファイルにパスワードが設定されているかを確認します。
パスワードが設定されていると`True`が返され、設定されていないと`Flase`が返されます。
```python
# モジュールのインポート
from PyPDF2 import PdfFileReader

# 対象のpdfファイルの指定
enc_file_path = "./sample_enc.pdf"
pdf_file_path = "./sample.pdf"

# pdfファイルのパスワードの有無を出力
print(PdfFileReader(enc_file_path).isEncrypted)
print(PdfFileReader(pdf_file_path).isEncrypted)

#　実行結果
True
False
```

# pdfファイルを復号する
イメージは暗号化の際と同じで、暗号化されていないファイルを新規で作る流れになります。
実行すると元データと同じ場所に`{ファイル名}_dec.pdf`が作成されます。
```python
# モジュールのインポート
from PyPDF2 import PdfFileReader,PdfFileWriter

# 対象のpdfファイルの指定
file_path = "./sample_enc.pdf"

#PdfFileWriterオブジェクトを作成
out = PdfFileWriter()

#暗号化されたPDFファイルを指定しPdfFileReaderで開く
file = PdfFileReader(file_path)

#PDFファイルに設定したパスワード
password = 'hogehoge'

#パスワードで復号する
file.decrypt(password)

#新しいファイルにページを追加する
for idx in range(file.numPages):
    page =file.getPage(idx)
    out.addPage(page)

#復号された情報をファイルに書き込み   
with open(file_path.replace(r'.pdf','') + r'_dec.pdf','wb') as f:
    out.write(f)
```

# 応用
`enc.py`にて設定するパスワードを自動で生成して設定します。設定されたパスワードはcsvファイルで吐き出されます。
対象ファイルを実行時の引数から受け取っています。
`enc.py`と`check_enc.py`では第一引数に対象ファイル。
`dec.py`では第一引数に対象ファイルと第二引数にパスワードとなっています。

下記のようにファイルを配置します。
```
.
├── check_enc.py
├── dec.py
├── enc.py
└── pdf
    └── sample.pdf
```

```python
# check_enc.py

# モジュールのインポート
from PyPDF2 import PdfFileReader
from sys import argv

print(PdfFileReader(argv[1]).isEncrypted)
```

```python
# enc.py

# モジュールのインポート
from PyPDF2 import PdfFileWriter,PdfFileReader
from os import path
from sys import argv
from re import sub
from string import ascii_uppercase,ascii_lowercase,digits
from secrets import choice

# パスワードを生成する関数を作成
def pwgen(size=20):
   chars = ascii_uppercase + ascii_lowercase + digits + '%&$#()'
   return ''.join(choice(chars) for x in range(size))

#コマンドからの引数を取得
filename = argv[1]

#PdfFileWriterオブジェクトを作成
out = PdfFileWriter()

#指定したPDFファイルをPdfFileReaderで開く
file = PdfFileReader(filename)

#PDFファイルのページ数を取得
num = file.numPages

#PDFファイルの全てのページを読み込んで新しいファイルにページを追加する
for idx in range(num):
    page = file.getPage(idx)
    out.addPage(page)

#パスワードを作成
password = pwgen()

#入力したパスワードで新しいファイルを暗号化
out.encrypt(password)

#暗号化された情報をファイルに書き込み
with open(filename.replace('.pdf','') + r'_enc.pdf','wb') as f:
    out.write(f)

#設定したパスワードをテキストに書き出す
pw_file = lambda x: sub(r'^.*/','',x.replace(r'.pdf','')) + r'_pw.csv'
filecheck = lambda x: 'a' if path.isfile(pw_file(x)) else 'x'

with open(pw_file(filename), filecheck(filename)) as f:
    print("\"" + filename + "\"" + r',' + "\"" + password + "\"", file=f)
```

```python
# dec.py

# モジュールのインポート
from PyPDF2 import PdfFileReader,PdfFileWriter
from sys import argv

#PdfFileWriterオブジェクトを作成
out = PdfFileWriter()

#暗号化されたPDFファイルを指定しPdfFileReaderで開く
file = PdfFileReader(argv[1])

#PDFファイルに設定したパスワード
password = argv[2]

#パスワードで復号する
file.decrypt(password)

#新しいファイルにページを追加する
for idx in range(file.numPages):
    page =file.getPage(idx)
    out.addPage(page)

#復号された情報をファイルに書き込み   
with open(argv[1].replace(r'.pdf','') + r'_dec.pdf','wb') as f:
    out.write(f)
```

# まとめ
あとはfor文なりでフォルダ内のpdfファイル全てを暗号化・復号するといったことも可能になるので、効率化の参考になれば幸いです。