pythonで共通鍵暗号を試してみた
しばらく前から暗号化に興味を持ちました。python用のライブラリーpycryptodomeの存在を知りましたので、暗号化を試してみました。
今回はpycryptodomeのドキュメントにある例に手を加えて試しました。
なお、pycryptodomeのドキュメントは残念ながら英語ですが、google翻訳で十分読めます。 Examples(例)とAPI documentationのCrypto.Cipherの中のModern modes of operation for symmetric block ciphers(対称ブロック暗号の最新の動作モード)を読めば何とかなります。
PyCryptodomeのインストール
PyCryptodomeはPyCryptoのforkで、PyCryptoと互換性があります。PyCryptoがインストールされていない場合、次の形でインストールできます。
$ pip3 install pycryptodome
試した暗号方式
Examples(例)の最初の例として挙げられていた共通鍵方式のAESで、EAXモードです。
例には公開鍵方式もありましました。公開鍵方式の利用例としてはマイナンバーカードなが挙げられます。
お試しスクリプトについて
今回、試しに書いたスクリプトは暗号化と復号化をし、暗号化されたものと暗号キーをそれぞれ別のファイルに保存します。暗号化されたデータと暗号キーはバイナリーなので、可読文字に変換するためにBase64、JSONも使っています。
暗号化すると、暗号データとnonce、認証タグができます。復号する時には暗号キーの他にこのnonceと認証タグも必要で、これらを用いて正しく暗号化/復号化できたことが確認されるようです。
暗号化されたデータとnonce、認証タグはセットにして一つのファイルとして出力してみました。暗号キーは一緒に保管すると意味ないので別ファイルにしてみました。今回はnonce、認証タグを暗号化されたデータとセットにしましたが、これが適切かどうかわかりません。
今回は試すだけなので簡単に端末画面でキーボードで操作するようにしました。
- '0' をEnterすると暗号キーを作成し、ファイル'testkey'に保存します。
- '1' をEnterすると暗号化します。暗号化前のデータはキーボードから適当に入力します。
暗号化されたデータはファイル'testdata'に保存します。 - '3' をEnterすると暗号化されデータファイル'testdata'と暗号キーファイル'testkey'を読み込んで復号します。
復号結果を表示します。
復号化してできた復号文が原文と同じであれば成功です。暗号キーを変えると当然復号で失敗します。
お試しスクリプト
#!/usr/bin/python3 """ AES(EAXモード)での暗号化とbase64、JSON処理 動作確認 """ from Crypto.Cipher import AES # pycryptodome from Crypto.Random import get_random_bytes # pycryptodome from base64 import b64encode from base64 import b64decode import json """ 暗号化するためキー(バイト型)を生成する。 """ def create_key(): key = get_random_bytes(16) # 乱数でキー(byte型)を作成 return(key) """ 与えられて原文(str型)を暗号化キー(バイト型)で暗号化し、結果を返す """ def encrypt(text_b, key): cipher = AES.new(key, AES.MODE_EAX) # キーを用いてEAXモードで暗号インスタンス化 ciphertext, tag = cipher.encrypt_and_digest(text_b) # 暗号化結果と最終認証タグができる nonce = cipher.nonce ''' header、暗号化された結果、認証タグ、nonceをバイナリーから可読文字のリストに変換''' b64text = [b64encode(x).decode('utf-8') for x in [ciphertext, tag, nonce]] json_text = json.dumps(b64text) # 可読文字のリストをjson形式にする return(json_text) # 結果を返す。 """ 与えられた暗号文(json)を暗号化キーで復号する。結果をstr型で返す。 """ def decrypt(text, key): b64text = json.loads(text) # json形式から可読文字のリストに戻す cipherdata = [ b64decode(x) for x in b64text] ciphertext = cipherdata[0] # 各バイナリーに戻す tag = cipherdata[1] nonce = cipherdata[2] cipher = AES.new(key, AES.MODE_EAX, nonce=nonce) # 同じキーてEAXモードでAESをインスタンス化、nonceも同じにする try: plaintext = cipher.decrypt_and_verify(ciphertext, tag) #復号化と認証タグの有効を確認 except ValueError: print('MACタグが無効の為メッセージを信頼できない') plaintext = b'' return(plaintext) """ 以下 テスト用 """ if __name__ == '__main__': sw = input('選択して下さい 0 or 1 or 3 --> ') if sw == '0': key = create_key() # 暗号化キーの作成 with open('testkey', 'wb') as f: f.write(key) # キーを保存 print('keyの作成 ', key) elif sw == '1': # 暗号化する with open('testkey', 'rb') as f: key = f.read() text = input('暗号化するテキストを入力 --> ') print('原 文:', text) text_b = text.encode('utf-8') # 原文をstr型からbyte型に変換 json_text = encrypt(text_b, key) with open('testdata', 'w') as f: f.write(json_text) print('暗号文:',json_text) elif sw == '3': with open('testkey', 'rb') as f: key = f.read() with open('testdata', 'r') as f: json_text = f.read() result = decrypt(json_text, key) result_s = result.decode('utf-8') # 復号化成功 print ('復号文:', result_s)
暗号の詳しいことは理解できていませんが、pycryptodomeを簡単に使えることがわかりました。pycryptodomeは多くの方式をサポートしているので、他の方式も同様に簡単に使えるものと思います。
今、このpycryptodomeを使って、パスワード管理ソフトの自作を考えています。