はじめに
Pythonでのシステム開発において、パスワードやトークン、個人情報などを暗号化・復号する機会は多くあります。過去のプロジェクトでは pyCrypto(Crypto モジュール)がよく採用されていましたが、皆さんのプロジェクトでは現在どのライブラリを使用しているでしょうか?
もし pyCrypto をそのまま使い続けている場合、セキュリティ上の脆弱性や、最新のPython環境で動かなくなるリスクが潜んでいます。
本記事では、レガシーな pyCrypto から、完全な上位互換として機能する PyCryptodome への移行手順と、実践的な AesCipher クラスの実装方法を解説します。
目次
- PyCryptoとPyCryptodomeとは?
- なぜPyCryptodomeへ移行すべきなのか?
- 移行手順(アンインストールとインストール)
- AES暗号化の実装例
- 動作確認
- 移行時の注意点・ハマりポイント
- まとめ
- 参考文献
🧷 PyCryptoとPyCryptodomeとは?
✅ PyCrypto
Python向けの強力な暗号化ライブラリとして長く使われてきたパッケージです。ソースコード内では import Crypto として呼び出されます。
✅ PyCryptodome
PyCrypto のフォーク(派生)として誕生したライブラリです。PyCrypto とAPIの互換性を保ちながら、新機能の追加や脆弱性の修正が行われています。既存のコードベース(import Crypto)を書き換えることなく、安全な環境へ移行できるのが最大の特徴です。
🧷 なぜPyCryptodomeへ移行すべきなのか?
移行を強く推奨する理由は主に以下の3点です。
- 開発の停止:
pyCryptoは2013年を最後にリリースが止まっており、実質的に開発が終了(Dead Project)しています。 - 脆弱性の放置:
pyCryptoにはヒープベースのバッファオーバーフローの脆弱性(CVE-2013-7459)などが報告されていますが、修正パッチが提供されていません。 - ビルドエラーの発生: C言語の拡張モジュールを含んでいるため、Python 3.8以降などの最新環境や特定のOSで
pip installする際にコンパイルエラーが頻発します。
🧷 移行手順(アンインストールとインストール)
APIの互換性があるため、コードの書き換えよりも「環境の入れ替え」がメインの作業になります。
✅ ① 既存のPyCryptoを削除する
競合を防ぐため、必ず先に古いライブラリをアンインストールします。
pip uninstall pycrypto
✅ ② PyCryptodomeをインストールする
pip install pycryptodome
これだけでパッケージの入れ替えは完了です。ソースコード側の import Crypto という記述は一切変更する必要がありません。
🧷 AES暗号化の実装例
PyCryptodome への移行を機に、AES暗号化(CBCモード)のラッパークラスを実装し直す例を紹介します。
PyCryptodome からは便利なパディング用モジュール(Crypto.Util.Padding)が標準提供されているため、自前でパディングロジックを書く必要がなくなりました。
import base64
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
class AesCipher:
"""
AES暗号化 (AesCipher) /復号を行うラッパークラス (CBCモード)
"""
def __init__(self, key: str):
# AES-256の場合は32バイト、AES-128の場合は16バイトのキーを指定
self.key = key.encode('utf-8')
self.block_size = AES.block_size
def encrypt(self, plain_text: str) -> str:
"""文字列を暗号化してBase64で返す"""
plain_bytes = plain_text.encode('utf-8')
# PyCryptodome標準のpad関数を使用
padded_data = pad(plain_bytes, self.block_size)
# セキュリティを高めるため、初期化ベクトル(IV)を毎回ランダムに生成
iv = get_random_bytes(self.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
encrypted_bytes = cipher.encrypt(padded_data)
# IVと暗号文を結合してBase64エンコード
return base64.b64encode(iv + encrypted_bytes).decode('utf-8')
def decrypt(self, encrypted_text: str) -> str:
"""Base64の暗号文を復号して文字列を返す"""
encrypted_bytes = base64.b64decode(encrypted_text)
# 先頭からブロックサイズ分をIVとして抽出
iv = encrypted_bytes[:self.block_size]
actual_ciphertext = encrypted_bytes[self.block_size:]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
decrypted_padded = cipher.decrypt(actual_ciphertext)
# PyCryptodome標準のunpad関数を使用
decrypted_bytes = unpad(decrypted_padded, self.block_size)
return decrypted_bytes.decode('utf-8')
🧷 動作確認
実装した AesCipher クラスを使って、実際に暗号化と復号ができるかテストします。
if __name__ == "__main__":
# 32バイト(256bit)のシークレットキーを用意
SECRET_KEY = "12345678901234567890123456789012"
cipher = AesCipher(SECRET_KEY)
original_text = "PyCryptodomeへの移行完了!"
print(f"平文: {original_text}")
# 暗号化
encrypted = cipher.encrypt(original_text)
print(f"暗号文: {encrypted}")
# 復号
decrypted = cipher.decrypt(encrypted)
print(f"復号文: {decrypted}")
assert original_text == decrypted
🧷 移行時の注意点・ハマりポイント
✅ モジュールの競合に注意
pycrypto と pycryptodome は、どちらも内部的に Crypto という名前のディレクトリを作成します。両方がインストールされた状態だと、インポートエラーや予期せぬバグの原因になります。必ず pycrypto が完全に削除されていることを pip list で確認してください。
✅ 他のライブラリ依存関係
どうしても pycrypto への依存を外せない別の古いライブラリと共存させたい場合、pycryptodome の代わりに pycryptodomex をインストールするという手があります。
こちらは import Cryptodome という独立したネームスペースを使うため、競合を避けることができます。ただし、コードの書き換え(Crypto ⇨ Cryptodome)が必要になります。
🧷 まとめ
今回は pyCrypto から PyCryptodome への移行手順と、AES暗号化クラスの実装について解説しました。
-
pyCryptoは非推奨・脆弱性あり。今すぐ移行を検討すべき。 -
PyCryptodomeならAPI互換があるため、基本的にpipの入れ替えだけでOK。 -
Crypto.Util.Paddingを使えば、自前でのパディング計算が不要になりコードがすっきりする。
セキュリティはシステム運用において最も重要な要素の一つです。もし過去のプロジェクトで pyCrypto を使っているものがあれば、ぜひこの機会にアップデートしてみてください。