pycryptoでAES暗号化を試す

認証機能はWebサービスを作るときに重要ですが、パスワードの保管って厄介ですよね。Cookieやらスクリプトに平文を直書きするのは抵抗があります。
スクリプト実行時に入力させたり(getpassモジュール)、OS依存のCredential管理機能を借用するという方法もあるようですが、Python以外と連携させるときなど場合によってはちょっと使いにくい。

そこで、デファクトな暗号化方式を扱えるようにしておくと、様々な言語でライブラリが提供されていたり、ネットで参考情報が見つかったりと、ところどころ便利です。

もくじ:

はじめに

万能というわけではないのですが、ここではPythonでAESを使うやり方をメモしておきます。

AESはいわゆる共通鍵暗号なので、平文をキー(暗号鍵)で暗号化し、キーを知っていれば、同じキーで暗号を平文へ復号できます。
例えば最初に書いたように、平文で秘密の情報を保存しておく必要はありませんし、他にもクライアントサーバ間通信などに使えると思います。送り手と受け手で同じキーを使う仕組みさえあれば、送り手のクライアントがデータを暗号化し、受け取ったサーバで復号することで、途中の伝送路には平文が流れることがありません。
当たり前ですが、キーがバレるとまずいです。

主要な暗号化方式はライブラリ化されており、Pythonではpycryptoが使えます。

標準ではないので、入っていない場合はいつものごとくpipでインストール。なお検証した環境はWindows10 + Python3.6です。

参考にさせて頂いたページ
あとStackOverflowもちょいちょい。

【追記】
Windows環境の場合、Visual Cランタイム関連のビルドエラーがあるためpycryptoよりもpycryptodomeのほうが推奨されています。モジュール名などは(ほぼ)同じです。

PythonでのAES暗号化

こだわり始めると設定やら方式やらが色々あるのですが、まあパスワード程度の文字列が暗号化・復号できればいいので、決め打ちでハードコーディングしてしまいます。
ここではpycryptoを使ってAES暗号化を実装します(cryptoutil.py)、というかうすーいラッパーですね。臆面もなくコードは参考サイトの丸パクリですが、アンパディング周りなど必要に応じて展開しているのと、キー生成用にクラスを定義しています。

まず、サンプル全体を記載しておきます。ふたつのクラスを定義しています。

実装は見たままなので、以下主に使い方の説明です。

キーの作成と管理

KeyUtilはキーを管理するクラス。
インスタンス作成時に自動生成させるか、任意の文字列を渡してキーを作ります。キーは32文字(鍵長256bit)に固定ですので、任意の文字列を渡した場合には32文字より多い場合は余りを切り捨て、少ない場合はパディング(PKCS#7,暗号化のときも同じ)します。

私はモノグサなのでキーをファイルに保存・読み込みする機能も付けていますが、もちろんキーと暗号は別に管理しないと意味がありません。
ファイル名を指定するか、指定しない場合はデフォルト(_key.pkl)が適用されます。

暗号化と復号

上で作成したキーを読み込んで、暗号化を試します。暗号化した結果はBase64でエンコードしたものになり、復号メソッドに渡せばそのまま復号してくれます。
キーと暗号、それぞれを保存したファイルから復号するときは、下の4行で元の平文を取得できます。

今回作成したクラスには暗号化・復号の両方のメソッドがありますが、もちろん役割が明確に決まっているスクリプトならどちらかに絞ってしまって構いません。

PowerShellでの暗号化

ここからは補足的な話。送り手(暗号化する側)がPowerShellだとどうでしょうか。
PowerShellスクリプトで何らかの処理をして、結果を暗号化してサーバに送信、サーバ側で復号する場合。サーバ側をPythonで実装すると、上の復号機能の部分がそのまま使えそうですね。

PowerShellでAES暗号化

AES暗号化を.NETの機能(System.Security)を使って実装します。
ここでは、キー(文字列)と平文、ファイル名を渡すと暗号化した文字列を指定されたファイルに書き出す関数aes_encryptを定義しています。上で書いたように、本来はWeb API経由で送信するのがいいのですが、面倒なのでローカルのファイル経由にします。

なお、出力するファイルを簡単に識別させるため、拡張子を適当にpskにしました。

Pythonでの復号

同じスクリプトで復号するので特に変更点はありません。ここでは暗号化された文字列をPowerShellが出力したファイル(拡張子psk)から読み込みます。

復号とは特に関係ありませんが、ファイルの読み方には若干注意が必要です。
対象のファイルをBOM付きUTF-8テキストファイルとして文字列を読み出し、念のためCRLFを除去します。

簡単化のため以降はPowerShellコンソールで検証しています。

まずPythonのシェルでキーを作成し、保存します。ここまでは上と同じですが、キーを表示させてクリップボードにコピーしておきます。

PowerShellコンソールで平文を暗号化します。関数をロードし、先の実行結果からコピペしたキーと、平文を指定して実行。ファイル名は指定せず、デフォルトの名前(ps_pass.psk)で出力させます。念のためcat(Get-Content)すると、暗号が書き込まれているのがわかりますね。

保存しておいたキーで初期化し、復号してみましょう。PowerShellが出力したファイル名を指定すると、暗号をロードしてくれます。期待通りならPowerShellで指定した平文が返ってくると思います。