一覧SC-07-1 · 公開鍵と電子署名

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 / 公開試料と固定時刻

1
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
2
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
3
print(rsa_open(private,c1)) print(rsa_open(private,c2))
b'order=7&amount=100'
b'order=7&amount=100'
4
other = rsa_key("other") print(rsa_open(other,c1))
decryption_failed
5
changed = c1[:-1] + bytes([c1[-1]^1]) print(rsa_open(private,changed))
decryption_failed
6
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
7
print(rsa_encrypt(public,b"x"*(limit+1)))
message_too_long

演習完了