AES暗号化をPythonとJavascriptでやってみる話（備忘録）

# この記事の大まかな内容
PythonのFlaskを使用してWEBアプリ開発をしていたのだが、ローカル環境で動かす専門のWEBアプリのため、SSL証明書が取得できないし、
OpenSSLを使用してもいいが、chromeブラウザくんは警告を出しまくるし、何より、常に危険マークがつくのは気に食わないということでせめてAES暗号化をして通信をしてあげたいと思ったわけです。

# 注意点
- 個人の備忘録なので、あまり参考にならないと思います。
- この記事がきっかけになるいかなる損害も責任を負いません
- セキュリティ上危険と言われる可能性があるので、公式のドキュメントを参照してください
- WebCryptoAPIを最初使っていましたが、非SSL時に使用できないです！

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">WebCryptoAPIのドキュメント読んでたらド正論書いてあって笑ったｗ<br><br>”自分のしていることが分からない場合は、おそらくこの API を使用すべきではないでしょう。” <a href="https://t.co/XifECVOaM6">pic.twitter.com/XifECVOaM6</a></p>&mdash; まぎらわしぃ (@magirawashili) <a href="https://twitter.com/magirawashili/status/1752216608511304094?ref_src=twsrc%5Etfw">January 30, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


# PythonでAESキーを作成する
今回私のプロジェクトではPythonをサーバーで動かして処理をしているので、AESキーとIVをサーバーで作成します。
今回はFlaskのプロジェクトなので```app.config```に保存しました。
今回は、cryptographyライブラリを使用しますので、pipダウンロードしておきます。
### 必要なライブラリのインポート
```python
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
```
### AES鍵の作成
```python
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を使用して暗号化＆復号する
### 暗号化
```python
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
```
### 復号
```python 
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を使用してライブラリを読み込む
```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
```
```javascript
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()
```
### 復号
```javascript
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鍵の交換をするのがいいと思います。

多分もっとほかのやりかたもあるとは思いますが