この記事の大まかな内容
PythonのFlaskを使用してWEBアプリ開発をしていたのだが、ローカル環境で動かす専門のWEBアプリのため、SSL証明書が取得できないし、
OpenSSLを使用してもいいが、chromeブラウザくんは警告を出しまくるし、何より、常に危険マークがつくのは気に食わないということでせめてAES暗号化をして通信をしてあげたいと思ったわけです。
注意点
- 個人の備忘録なので、あまり参考にならないと思います。
- この記事がきっかけになるいかなる損害も責任を負いません
- セキュリティ上危険と言われる可能性があるので、公式のドキュメントを参照してください
- WebCryptoAPIを最初使っていましたが、非SSL時に使用できないです!
WebCryptoAPIのドキュメント読んでたらド正論書いてあって笑ったw
— まぎらわしぃ (@magirawashili) January 30, 2024
”自分のしていることが分からない場合は、おそらくこの API を使用すべきではないでしょう。” pic.twitter.com/XifECVOaM6
PythonでAESキーを作成する
今回私のプロジェクトではPythonをサーバーで動かして処理をしているので、AESキーとIVをサーバーで作成します。
今回はFlaskのプロジェクトなのでapp.config
に保存しました。
今回は、cryptographyライブラリを使用しますので、pipダウンロードしておきます。
必要なライブラリのインポート
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
AES鍵の作成
def generate_AES_key():
key = os.urandom(32)
iv = os.urandom(16)
return key, iv
app.config["AES_KEY"], app.config["AES_IV"] = generate_AES_key()
PythonでAESを使用して暗号化&復号する
暗号化
def Encrypt(aes_key, aes_iv, encrypt_text):
padder = padding.PKCS7(algorithms.AES.block_size).padder()
padded_data = padder.update(encrypt_text.encode()) + padder.finalize()
cipher = Cipher(algorithms.AES(aes_key), modes.CBC(aes_iv), backend=default_backend())
encryptor = cipher.encryptor()
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
return ciphertext
復号
def Decryption(aes_key, aes_iv, decrypt_text):
cipher = Cipher(algorithms.AES(aes_key), modes.CBC(aes_iv), backend=default_backend())
decryptor = cipher.decryptor()
padded_data = decryptor.update(decrypt_text) + decryptor.finalize()
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
plaintext = unpadder.update(padded_data) + unpadder.finalize()
return plaintext.decode()
Javascriptで暗号化&復号する
暗号化
※非同期関数で実行
まずは、CDNを使用してライブラリを読み込む
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
const key = CryptoJS.enc.Base64.parse(responseData['aes_key']);
const iv = CryptoJS.enc.Base64.parse(responseData['aes_iv']);
const ciphertext = CryptoJS.AES.encrypt(data, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return ciphertext.toString()
復号
const key = CryptoJS.enc.Base64.parse(responseData['aes_key']);
const iv = CryptoJS.enc.Base64.parse(responseData['aes_iv']);
const decrypted = CryptoJS.AES.decrypt(data, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
const plaintext = decrypted.toString(CryptoJS.enc.Utf8);
return plaintext
おわり
これ以上話すことはないし、正直言ってあまり良くない というか最悪だから、ちゃんと使うときは、皆さん正しく書きましょうね。。。
ここでは、AESキーの交換を裸の状態でやっているので最初の鍵交のときには、クライアント側で公開鍵暗号方式の公開鍵をサーバーに送りつけて、サーバー側でAES鍵をクライアント公開鍵で暗号化して、クライアント側に送り返して、クライアント側で秘密鍵を使用して、複合するような形で、AES鍵の交換をするのがいいと思います。
多分もっとほかのやりかたもあるとは思いますが