Python Crypt 暗号化 復号化

Python学習【365日チャレンジ!】235日目のマスターU(@Udemy11)です。

そこかしこで酷評されているマイナポイントですが、すでに最大5,000円の還元を受け取りました。

PayPayにするか、楽天カードにするか、メルペイにするか検討した結果、抽選で1,000万円が1名に当選する可能性があり、メルカリでの購入でさらに1,000円分のポイントバックがあるメルペイを選びました。

あと、よく使う釣具屋でメルペイが使えるということも、メルペイを選んだ決め手の一つです。

9月1日から還元が始まったのですが、PayPayが1ヶ月後のポイント還元に対して、メルペイは、使った次の日にポイントで還元されました。

すぐに還元されたポイントが商品の購入に使える上、使った還元ポイントがマイナポイント還元の対象となっています。

たとえば、最初の買い物で、10,000円使って2,500ポイントが還元されて、次の買い物で、7,500円と2,500ポイントで10,000円の買い物をすれば、2,500ポイントが還元されるということです。

普通に考えると、20,000円で5,000ポイントが還元されるので、20,000円で25,000円分使えます。

上記の例の場合は、17,500円使って22,500円分使えるということになります。

どちらも5,000ポイントが還元されるということに違いはないんですけどね。

それでは、今日もPython学習をはじめましょう。

昨日の復習

昨日は、バイト列、バイナリ列、Base64について学習しました。

バイト列とバイナリ列は、意味のない任意のビットパターンからなるデータの集合列のことで、バイトを単位で考えるか、ビットを単位で考えるかでどちらかの配列に分類されました。

Base64は、64種類の英数字のみを使った通信環境でマルチバイト文字やバイナリデータを扱うためのエンコード方式で、メールに添付する画像のエンコードなどで使われていました。

詳細については、昨日の記事を参考にしてください。

今日は、暗号化と復号化の基本について学習します。

16文字のランダムなコード

Pythonの暗号化ライブラリpycryptを使って暗号化処理をしていきます。

まずは、ターミナルからコマンドpip install pycryptを実行してpycryptをインストールします。

インストールが完了したら、アスキーコードからランダムに選んだ16文字のコードを作ります。

import string
import random

from Crypto.Cipher import AES


key = ''.join(
    random.choice(string.ascii_letters) for _ in range(AES.block_size)
)
print(AES.block_size)
print(string.ascii_letters)
print(key)

最初にstringrandomライブラリをインポートします。

次にインストールしたCrypt.Cipherパッケージから暗号規格のAESモジュールを読み込みます。

7行目から9行目でstring.ascii_lettersで文字列にしたアスキーコードの文字をランダムに選んで、16になるAES.block_sizeを使って、16文字のランダムな文字列をkeyに代入しています。

10行目でAES.block_size、11行目でstring.ascii_letters、13行目でkeyをそれぞれ出力しています。

出力結果

16
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
YDngexIsrJyqhAJf

2行目の英字大文字小文字の中から1行目の16個をランダムに選んだものが3行目のkeyになります。

コードの暗号化

次に任意のコードを暗号化してみます。

暗号化するには、16の倍数の文字でないとだめなので、16で割り切れないときは割り切れるように文字の数を調整します。

key = ''.join(
    random.choice(string.ascii_letters) for _ in range(AES.block_size)
)

iv = ''.join(
    random.choice(string.ascii_letters) for _ in range(AES.block_size)
)

plaintext = 'jdadfaagewgofiajoi'
cipher = AES.new(key, AES.MODE_CBC, iv)
padding_length = AES.block_size - len(plaintext) % AES.block_size
plaintext += chr(padding_length) * padding_length
cipher_text = cipher.encrypt(plaintext)
print(cipher_text)

インポートするライブラリは最初と同じです。

11行目〜13行目で16文字のランダムな英字を生成してivに代入しています。

15行目で暗号化するplaintextを作り、16行目のAES.newで、AES.MODE_CBCというアルゴリズムを使い、keyivを絡めた暗号化の準備をします。

暗号化は16文字の倍数でなければならないので、17行目と18行目でplaintextの長さを16で割った余りを16からひいいた数padding_lengthを求め、padding_lengthの値をpadding_lengthの数だけ16進数の値(chr(padding_length))を並べたものを暗号化するplaintextに加えます。

これで、暗号化するplaintextが16の倍数になり、暗号化ができるようになります。

続いて、19行目でplaintextを暗号化します。

最後に暗号化されたcipher_textを出力しています。

出力結果

b'Ps\xc8T\xf5\x1e\xdf]\xe58G\xb2\xb5\x97\x85\x0e)\x1a+\x8e\x9dbC\xed\x95\xa1\xc5Y\xe0:\xa0\x94'

復号化

次に、暗号化されたコードをもとのデータに戻す復号化をやってみます。

cipher2 = AES.new(key, AES.MODE_CBC, iv)
decrypted_text = cipher2.decrypt(cipher_text)
print(decrypted_text)
print(decrypted_text[-1])
print(decrypted_text[:-decrypted_text[-1]])

21行目までは上記の暗号化のコードと同じです。

22行目でcipher2を作り、23行目でcipher2を使って暗号化したcipher_textdecryptで復号化します。

あとは、decrypted_textと、付け足されたpadding_lengsの数と復号化したもとのplaintextを出力しています。

出力結果

b'V\x8fU\xdd\x10\xfcI\xbe\xeb\x87\xba\x97\xffv\xfe\xb77\xbf"[^\\`\xe0\x06\x8c\xb0\xedhL\x19\xe3'
b'jdadfaagewgofiajoi\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e'
14
b'jdadfaagewgofiajoi'

ちなみに、decrypted_text[-1]は、最後の文字を表しているので、decrypted_text[:-decrypted_text[-1]]は、decrypted_textから最後の文字である14を16進数にしたeを14個省いたデータとなるわけです

出力された値を見ると、2行目に\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0eという値があるのがわかります。

これは、16進数のe、つまり10進数の14になり、16進数のeが14個付け足されているということです。

プログラマーみたい

当たり前ですが、暗号化と復号化は非常に重要で、ネットワークが必要不可欠な現在のデータのやり取りには欠かすことのできない技術です。

ミッション・インポッシブルなどの工作員が出てくるような映画では、必ずといっていいほど、暗号化されたデータの解読が出てきますが、基本は今日学習したような仕組みでデータが暗号化されていると思うとワクワクしてきちゃいます。

実際に運用されているシステムなどはもっと複雑な暗号化が用いられているのだと思いますが、いよいよプログラマーに近づいて来たような感じがしています。

まずは暗号化と復号化の基本を理解して、コツコツと暗号化のスキルをマスターしていきましょう。

それでは明日も、Good Python!