[Python][PyCrypto] AES暗号でパスワード使ってデータを暗号化

 今回はパスワードを使って文字列をAES暗号で暗号化する処理を書いていきます。

 暗号化で使ったパスワードと復号化で使うパスワードが同じなら元のデータに復元できるという処理です。イメージはこんな感じ。

 暗号の使い方についての内容なのでセキュリティや仕組みについては考慮しません。

 PythonのPycryptoというライブラリを使います。

暗号とは


 ざっくり言うとデータをぐちゃぐちゃにして他人に内容を分からなくする方法です。

 ぐちゃぐちゃにすることを暗号化、それを元に戻すことを復号化と言います。

AES暗号について


 データを暗号化・復号化するときに同じ鍵を使う共通鍵方式です。ブロック(16Byteなどの単位)ごとに暗号化します。

 今回は使い方について書くので仕組みについては触れません。AES暗号とは

ライブラリの用意


 PyCryptoという暗号関連のライブラリをインストールします。

pip install pycrypto

実装


暗号化

 暗号化したいデータとパスワードを引数とします。バイト値を扱うため、文字列は全てUTF-8でエンコーディングしています。

 まずはbase64でエンコードしてバイト配列にします。次にブロックごと(16byte)に暗号化するのでバイト数が16の倍数になるように値(今回は”_”)を追加します。

 次にパスワードをハッシュしたもの、生成したIVを用いてpycryptoの暗号化処理をするオブジェクトを生成し、それを使って暗号化します。

 IV(初期化ベクトル)とは暗号化で使用されるパラメータで、この値を変えることで同じ暗号鍵でも違う暗号化データを生成することができます。

今回はパスワードを使って暗号化するのでIVは固定します。

import base64, hashlib
   
def encrypt(raw_data, password):
    # base64にエンコードし、バイト数を16の倍数にする
    raw_data_base64 = _trans_multiple_of_16byte(base64.b64encode(raw_data.encode("utf8")))
    # パスワードをエンコード、ハッシュして32バイトの鍵を生成
    secret_key = hashlib.sha256(password.encode("utf8")).digest()
    # 16バイトのIVを生成
    iv = hashlib.md5("default iv".encode("utf8")).digest()
    # 暗号化オブジェクトの生成
    crypto = AES.new(secret_key, AES.MODE_CBC, iv)
    # 暗号化
    return crypto.encrypt(raw_data_base64)


def _trans_multiple_of_16byte(data):
    # _を付け加えて16バイトの倍数にする
    surplus = len(data) % 16
    if surplus != 0:
        for i in range(16 - surplus):
            data += "_".encode("utf8")
    return data

復号化

 基本的に暗号化と逆の処理です。

 まずパスワードをハッシュしたもの、IVを使って暗号化処理をするオブジェクトを生成し、それを使ってデータを復号化します。

 次に復号化した値から、バイト数を16の倍数にするために付け加えた値(今回は”_”)を除去します。

 最後にbase64でデコードし、文字列なので暗号化の時と同じUTF-8でデコーディングします。

def decrypt(enc_data, password, is_str):
    # パスワードをエンコード、ハッシュして32バイトの鍵を生成
    secret_key = hashlib.sha256(password.encode("utf8")).digest()
    # 16バイトのIVを生成
    iv = hashlib.md5("default iv".encode("utf8")).digest()
    crypto = AES.new(secret_key, AES.MODE_CBC, iv)

    # 復号化して、バイト数を16の倍数にするために付け加えた_を除去
    raw_data_base64 = _reverse_multiple_of_16byte(crypto.decrypt(enc_data))
    # base64デコードを行う
    return base64.b64decode(raw_data_base64).decode("utf8")
def _reverse_multiple_of_16byte(data):
    # 末尾から追加された文字を全て取り除く
    while FILL_CHAR.encode(ENCODE_TYPE) == data[-1]:
        data.pop()
    return data

コメントを残す

メールアドレスが公開されることはありません。