RSA-OAEPによる公開鍵暗号
公開鍵でできる操作と秘密鍵が必要な操作、長い本文を共通鍵で扱う理由を説明します。
説明
公開鍵暗号では、公開してよい暗号化鍵と、受信者だけが保管する復号鍵を分けます。RSAを安全な暗号化方式として使うには、整数のべき乗だけでなく、OAEPなどの符号化が必要です。
公開用に作った2048ビットのRSA試料鍵と二つの暗号文を調べます。同じ本文の暗号文が異なること、別の鍵で復号できないこと、入力長の上限を実際の計算で確認します。
公開鍵でできる操作と秘密鍵が必要な操作、長い本文を共通鍵で扱う理由を説明します。
1公開鍵の構成を確認する
RSA公開鍵は法nと指数eを含みます。この鍵ファイルは演習専用の公開試料です。
from sc07 import *
private = rsa_key("recipient")
public = private.public_key()対応する公開鍵を取得する
numbers = public.public_numbers()法と公開指数を取得する
print(private.key_size,numbers.e,numbers.n.bit_length())鍵長2048ビット、公開指数65537です。公開情報から暗号化できますが、秘密指数を公開鍵に含める必要はありません。
2二つの暗号文を比較する
同じ本文を同じ公開鍵で別々にOAEP暗号化した二つの試料です。OAEP内部で乱数を使うため結果は通常異なります。
c1 = (HERE/"cipher-1.bin").read_bytes()1件目の固定暗号文を読む
c2 = (HERE/"cipher-2.bin").read_bytes()
print(len(c1),len(c2),c1 == c2暗号文同士を比較する)
print(c1[:32].hex())どちらも256バイトですが一致しません。暗号文が違うだけで、元の本文まで違うとは判断できません。
3受信者の秘密鍵で復号する
暗号化時と同じSHA-256・MGF1・空のlabelを指定して復号します。
print(rsa_open(private,c1)1件目を受信者の秘密鍵で復号する)
print(rsa_open(private,c2)2件目も復号する)両方ともorder=7&amount=100へ戻ります。乱数で変わる符号化を除いた後の本文は同じです。
4別人の秘密鍵で復号する
別の2048ビットRSA鍵を使います。鍵長が同じだけでは復号できません。
other = rsa_key("other")別の受信者の試料鍵を取得する
print(rsa_open(other,c1)別の鍵で復号を試す)decryption_failedです。受信者へ対応する秘密鍵が必要です。
5暗号文を変更する
末尾1ビットを反転し、元の受信者の鍵で復号します。
changed = c1[:-1] + bytes([c1[-1]^1末尾の1ビットを反転する])
print(rsa_open(private,changed)変更した暗号文を復号する)OAEPの復号検査に失敗します。ただし誰でも公開鍵で新しい暗号文を作れるため、これだけで送信者の身元を確認したことにはなりません。
6入力長の上限を計算する
OAEPの上限はk−2hLen−2バイトです。ここでは法の長さk=256、SHA-256のhLen=32です。
limit = private.key_size//8 - 2*32 - 2OAEPの最大入力長を計算する
print(limit)
box = rsa_encrypt(public,b"x"*limit)上限ちょうどの本文を暗号化する
print(len(box),rsa_open(private,box) == b"x"*limit)190バイトまで入り、暗号文は256バイトです。鍵長2048ビットだから2048バイトを入れられるわけではありません。
7上限を1バイト超える
191バイトの本文を渡します。
print(rsa_encrypt(public,b"x"*(limit+1)上限を1バイト超える入力を作る))message_too_longです。大きな文書は共通鍵で暗号化し、その鍵だけを公開鍵方式で保護するなど、用途に合った構成を使います。
まとめ
公開鍵で暗号化し、対応する秘密鍵で復号します。
OAEPは乱数を含み、同じ本文でも暗号文が変わります。
公開鍵暗号化だけで送信者の身元を証明することはできません。
操作履歴と解説を振り返れます。
Python 3.15 / cryptography 50.0.2 / 公開試料と固定時刻
from sc07 import *
private = rsa_key("recipient")
public = private.public_key()
numbers = public.public_numbers()
print(private.key_size,numbers.e,numbers.n.bit_length())2048 65537 2048
c1 = (HERE/"cipher-1.bin").read_bytes()
c2 = (HERE/"cipher-2.bin").read_bytes()
print(len(c1),len(c2),c1 == c2)
print(c1[:32].hex())256 256 False 5aaf006592646168ae1d76c3cb87d866c6bdd3994249a1fa2172688c49f29120
print(rsa_open(private,c1))
print(rsa_open(private,c2))b'order=7&amount=100' b'order=7&amount=100'
other = rsa_key("other")
print(rsa_open(other,c1))decryption_failed
changed = c1[:-1] + bytes([c1[-1]^1])
print(rsa_open(private,changed))decryption_failed
limit = private.key_size//8 - 2*32 - 2
print(limit)
box = rsa_encrypt(public,b"x"*limit)
print(len(box),rsa_open(private,box) == b"x"*limit)190 256 True
print(rsa_encrypt(public,b"x"*(limit+1)))message_too_long
演習完了