RSA暗号と電子署名

RSA

RSAとは

RSAとは暗号化方式の一つです。公開鍵暗号の一種で文書内容を秘匿するための暗号化と電子署名を行うことが可能です。このページではPython上でのRSAの鍵生成から取扱方法、暗号化と復号化、電子署名について解説します。

pycryptodome

暗号化用のライブラリは色々あるのですが、このページではpycryptodomeを使用したサンプルを紹介します。

Pythonではpycryptoという暗号化ツールキットが以前はよく使われていたのですが、Python3.3が出た頃以降からメンテナンスされなくなっています。このため、近年は代替としてpycryptodomeというものがよく使われるようになっています。

ツールキットというわけでRSA以外にAES、DESなどの暗号化アルゴリズムやSHA256のようなハッシュを扱うものが揃っています。pycryptoの代替を謳っているためpycryptoを利用したコードは概ねそのまま置き換えることが可能なはずだったのですが、セキュリティホールとなりうる古い機能は一部廃止されたため、暗号化や署名のメソッドの使い方が大きく変わっています。詳しくは公式ドキュメントを参照してください。

https://www.pycryptodome.org/en/latest/

pypiに登録されているため、pipでのインストールが可能です。

pip install pycryptodome

鍵の生成

まず、暗号化のための鍵のペアを生成してpem形式で保存してみましょう。Unix系のOSを利用されている方は通常コマンドライン上で行うことが多いと思います。これから紹介するPythonコードは以下のコマンドと同等です。

# 秘密鍵の生成
openssl genrsa 1024 > private-key.pem
# 秘密鍵に基づいて公開鍵を生成
openssl rsa -in private-key.pem -pubout -out public-key.pem

それではPythonコードです。

from Crypto.PublicKey import RSA

# 秘密鍵の生成
private_key = RSA.generate(1024)
with open('private.pem', 'w') as f:
    f.write(private_key.export_key().decode('utf-8'))

# 秘密鍵に基づいて公開鍵を生成
public_key = private_key.publickey()
with open('public.pem', 'w') as f:
    f.write(public_key.export_key().decode('utf-8'))

上のコードではRSA.generateで秘密鍵を生成、private_key.publickey()で公開鍵を生成しています。また、pem形式で保存する場合はexport_key()メソッドを使用します。

一方でpem形式を読み込んで元のオブジェクトに戻すにはRSA.import_keyを使用します。

# pemロード
with open('private.pem', 'br') as f:
    private_pem = f.read()
    private_key = RSA.import_key(private_pem)

with open('public.pem', 'br') as f:
    public_pem = f.read()
    public_key = RSA.import_key(public_pem)

 
※ 以降のサンプルでは鍵の生成もしくは読み込みの記述は省略し、いきなりpublic_key、private_keyと表しています。

暗号化と復号化

次に生成した公開鍵でテキストを暗号化し、秘密鍵で復号化してみましょう。

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP

# メッセージを暗号化
message = "テストメッセージ"
public_cipher = PKCS1_OAEP.new(public_key)
ciphertext = public_cipher.encrypt(message.encode())

# メッセージを復号
private_cipher = PKCS1_OAEP.new(private_key)
message2 = private_cipher.decrypt(ciphertext).decode("utf-8")

暗号化ではencrypt、復号する場合はdecryptを使用します。

電子署名

次に電子署名を使ってみましょう。データ送信側の本人確認や送信データが改ざんされていないことを確認する処理で使用される技術で、有名所ではSSLで使われています。また、決済などが関わるAPIで呼び出し元の妥当性検証などに利用されることもあります。

秘密鍵で署名を作成し、公開鍵で署名の妥当性を検証してみましょう。

from Crypto.PublicKey import RSA
from Crypto.Hash import SHA256
from Crypto.Signature import pkcs1_15

# メッセージと秘密鍵から署名を生成
message = 'テストメッセージ'
h1 = SHA256.new(message.encode())
signature = pkcs1_15.new(private_key).sign(h1)

# 公開鍵から妥当性を検証
h2 = SHA256.new(message.encode())
try:
    pkcs1_15.new(public_key).verify(h2, signature)
    verified = True
except ValueError:
    verified = False

print(verified)

verifyメソッドで不備がある場合はValueErrorが発生します。これを利用して上のサンプルではValueErrorが発生しなかった場合は認証成功としています。