ChatGPTを使ってのコード実装: RSA暗号を例に
小西です。皆さんはChatGPTを有効に使えているでしょうか?既にバリバリ活用されている方もいれば、何となく話には聞いているものの使えていない、使ってみたけど仕事に活用しきれていない方もいらっしゃるかと思います。
ChatGPTは日常や仕事での作文以外に、プログラムのコード生成でも有効に用いる事ができます。このブログ記事では、RSA暗号を例にしてChatGPTのコード作成での活用の方法をまとめました。ここではアルゴリズムやコードの書き方について分からない部分をChatGPTに聞き、ChatGPTが出力するコードを活用する事でコーディングを進めています。
本記事ではプログラム言語としてPython, ChatGPTについては3.5(2023/8現在で課金が必要ないもの)を用いています。
アカデメイアBlogでは、本記事以外にもこちらにGPTに関するものが掲載されていますので合わせてご参照ください。
RSAの概要
本記事ではRSAの暗号アルゴリズムを実装します。RSAのアルゴリズムについて簡単に解説します。
RSA暗号は桁数の大きい数の素因数分解は難しい、という事を用いた暗号となっています。なので、高速な素因数分解が可能となるShorのアルゴリズム[2]が使えるような大規模な量子コンピュータが出来ると危ないのではとも言われています。2023年現在から見てそんな近い将来には実現しないのではとは思いますが。。現状では非常に強固な暗号として様々な場所で活用されています。(本記事の実装では小さい素数しか用いないので暗号としては弱いものとなります。現実には数百桁の素数が用いられているようです。) 特許も2000年に切れているので誰でも自由に利用できます。
- 鍵生成
- 素数p, qを選択
- p, qの積nを計算
- φ関数を計算(φ = (p-1)*(q-1))
- 公開鍵の生成
- 公開鍵指数eとして、φと互いに素な数を選ぶ
- 公開鍵は(e, n)
- 秘密鍵の生成
- (d*e)%φ = 1 となるような秘密鍵指数dを求める
- 秘密鍵は(d, n)
- 暗号化
- メッセージの各文字を数値化
- 各数値Mに対して C = (M**e)%nで暗号化する
- 復号化
- 暗号化された各数値Cに対して M = (C**d)%nで復号化する
- 復号化された各数値を文字列に変換する
- (d*e)%φ = 1となるようなdってどう求めるんだっけ?
- C = (M**e)%nてどう計算するんだっけ?
- 上式をそのままプログラムに書くとeが大きくなるとまずそう
- 文字列から数値、数値から文字への変換ってどうするんだっけ?
- 自分の使う言語(本記事ではPython)でどう実装するか
分かる部分のコード作成
ひとまず分かる部分に関してコードを作成します。公開鍵生成までは特に問題なく作成できそうです。
import random
import math
def get_e(phi):
while True:
x = random.randint(2, phi/2)*2 - 1
if math.gcd(x, phi) == 1:
return x
if __name__ == "__main__":
random.seed(1)
p = 3
q = 11
n = p*q
print("integer n: ", n)
phi = (p-1)*(q-1)
print("Phi function: ", phi)
e = get_e(phi)
print("RSA public key: ({}, {})".format(e, n))
とりあえず公開鍵の生成まで実装してみました。eはどう選んでも良いようですが、ここではランダムに条件に合ったものを選択する仕様にしています。(random.seed(1)は結果が再現するように入れています)
素数としては、p=3, q=11を用いています。
こちらを実行すると出力は下記の通り。
integer n: 33
Phi function: 20
RSA public key: (7, 33)
とりあえずは良さそうです。このコードをベースにしてコードを追加していきます。
ChatGPTに質問
ここからの実装方法でよく分からない部分が多かったので、ChatGPTさんに質問を投げてみました。
RSA暗号鍵のアルゴリズムを実装したいと考えています。大学生に説明するつもりで、手順について解説をお願いします。
RSA(Rivest-Shamir-Adleman)暗号は、公開鍵暗号方式の一種で、安全な通信を行うために広く使用されています。RSA暗号は、公開鍵と秘密鍵のペアを使用して暗号化および復号化を行うことができるため、セキュリティが高い方法として知られています。以下に、RSA暗号鍵のアルゴリズムを大学生向けに解説します。1. 鍵の生成:1.1 素数の選択: RSA暗号の基盤となるのは、2つの大きな素数(pとq)です。これらの素数はランダムに選ばれる必要があり、十分に大きいものを選びます。なぜなら、素数の積を素因数分解するのは困難であると考えられているからです。1.2 nの計算: nはpとqの積です。すなわち、n = p * q です。これは公開鍵として使われます。1.3 φ(n)の計算: φ(n)(ファイ関数またはオイラーのトーシェント関数)は、n未満でnと互いに素な数の個数です。φ(n) = (p – 1) * (q – 1) となります。1.4 公開鍵指数の選択: 通常、公開鍵の指数(e)として、φ(n)と互いに素な値を選びます。一般的な選択肢は、小さな素数である17や65537です。eは暗号化に使用されます。1.5 秘密鍵指数の計算: 秘密鍵指数(d)は、公開鍵指数(e)とφ(n)を用いて、以下のように計算されます: d ≡ e⁻¹ (mod φ(n))。つまり、d * e を φ(n) で割った余りが 1 となるような d を求めます。2. 暗号化:2.1 メッセージの数値化: 暗号化するメッセージを数値化します。これには通常、文字列をASCIIコードなどの数値に変換する方法が使われます。2.2 暗号化の計算: メッセージを暗号化するために、公開鍵指数(e)とnを使用して、暗号文 C を以下のように計算します: C ≡ M^e (mod n)。ここで、Mは数値化されたメッセージです。3. 復号化:3.1 暗号文の復号化: 暗号文 C を受け取った場合、秘密鍵指数(d)とnを使用して、元のメッセージ M を以下のように計算します: M ≡ C^d (mod n)。3.2 数値を文字列に変換: 復号化された数値 M を元の文字列に戻すために、適切な変換を行います。例えば、ASCIIコードを文字列に戻すなどです。これらがRSA暗号の基本的な手順です。公開鍵と秘密鍵の組み合わせによって、安全な通信を実現することができます。ただし、実際の実装では、さらにセキュリティや効率性に関する考慮事項が多くありますので、実際の使用にあたっては注意が必要です。
秘密鍵指数の計算について説明をお願いします。公開鍵指数eとファイ関数phiが求まっているとした場合にdを求めるPythonコードはどのように実装すれば良いでしょうか?
秘密鍵指数(d)を計算する際には、公開鍵指数(e)とファイ関数(φ)が与えられています。d * e ≡ 1 (mod φ) という条件を満たす d を求める必要があります。これは、拡張ユークリッドの互除法を使用して解決できます。以下に、Pythonコードでその手順を示します。
def extended_gcd(a, b):
if b == 0:
return a, 1, 0
else:
d, x, y = extended_gcd(b, a % b)
return d, y, x - y * (a // b)
def calculate_private_key(e, phi):
gcd, x, y = extended_gcd(e, phi)
# Ensure that the private key exponent (d) is positive
d = x if x > 0 else x + phi
return d
# Given values
e = 65537 # Public key exponent
phi = 46656 # Phi value (replace with the actual value)
# Calculate private key exponent
d = calculate_private_key(e, phi)
print("Private key exponent (d):", d)
このコードでは、`extended_gcd`関数を用いて拡張ユークリッドの互除法を実装し、秘密鍵指数(d)を計算しています。計算されたdが負の場合は、正の値に変換しています。
ただし、実際のRSA暗号では、一般的に鍵長や素数の選択、セキュリティ対策などが必要です。セキュリティに関する知識やツールの使用を通じて、より堅牢な実装を行うことが重要です。
# get_e関数の下に下記コードをコピーします。
def extended_gcd(a, b):
if b == 0:
return a, 1, 0
else:
d, x, y = extended_gcd(b, a % b)
return d, y, x - y * (a // b)
def calculate_private_key(e, phi):
gcd, x, y = extended_gcd(e, phi)
# Ensure that the private key exponent (d) is positive
d = x if x > 0 else x + phi
return d
if __name__ == "__main__":
# print("RSA public key: ({}, {})".format(e, n))の文の下に下記コードを記載します。
d = calculate_private_key(e, phi)
print("RSA private key: ({}, {})".format(d, n))
integer n: 33
Phi function: 20
RSA public key: (7, 33)
RSA private key: (3, 33)
dが求まりました。ありがとうございます。
続いて任意の文字列を暗号化したいと考えています。Pythonでどのようなコードを書けば良いでしょうか?
RSA暗号を使用して任意の文字列を暗号化するためには、以下の手順に従ってPythonコードを実装できます。ただし、実際のセキュリティ上の運用には注意が必要であり、安全なライブラリを使用することが推奨されます。
以下は、Pythonの標準ライブラリのみを使用して文字列を暗号化する例です。しかし、実際にはより専門的なライブラリ(例:`cryptography`など)を使用することをおすすめします。
import random
# Function to perform modular exponentiation using repeated squaring
def mod_exp(base, exponent, modulus):
result = 1
while exponent > 0:
if exponent % 2 == 1:
result = (result * base) % modulus
base = (base * base) % modulus
exponent //= 2
return result
# Encrypt a message using RSA public key
def encrypt(message, e, n):
encrypted_message = [mod_exp(ord(char), e, n) for char in message]
return encrypted_message
# Given public key (e, n)
e = 65537 # Public key exponent
n = 253804874693508157406763741479569491150529996606586323685179825620725204914673368682099888715317990176729163149244500499537007981536284372551801845546765556092699858430888558266101262215101051191899284541104207883272383804769632939218585758596276905152320547981501014718156518709505177987306091
# Message to encrypt
message = "Hello, RSA encryption!"
# Encrypt the message
encrypted_message = encrypt(message, e, n)
print("Original message:", message)
print("Encrypted message:", encrypted_message)
このコードは、指定された公開鍵(e, n)を使用して文字列を暗号化します。`mod_exp`関数は、モジュラ指数法(repeated squaring)を用いて効率的な指数計算を行います。ただし、セキュリティを考慮して、実際の運用では安全なライブラリを使用することをお勧めします。
# calculate_private_key関数の下に下記コードをコピーします。
def mod_exp(base, exponent, modulus):
result = 1
while exponent > 0:
if exponent % 2 == 1:
result = (result * base) % modulus
base = (base * base) % modulus
exponent //= 2
return result
def encrypt(message, e, n):
encrypted_message = [mod_exp(ord(char), e, n) for char in message]
return encrypted_message
if __name__ == "__main__":
# print("RSA private key: ({}, {})".format(d, n))文の下に下記コードを記載します。
message = "QUANTUM"
# Encrypt the message
encrypted_message = encrypt(message, e, n)
print("Original message:", message)
print("Encrypted message:", encrypted_message)
上記を実行すると下記の出力が得られます。
integer n: 33
Phi function: 20
RSA public key: (7, 33)
RSA private key: (3, 33)
Original message: QUANTUM
Encrypted message: [27, 13, 32, 12, 6, 13, 11]
こちらのコードで暗号化した文字列を復号化するにはどのようにすれば良いでしょうか?Pythonコードを示してください。
暗号化されたメッセージを復号化するためには、秘密鍵を使用して復号化を行います。以下に、前のコードで暗号化したメッセージを復号化するためのPythonコードを示します。
# Decrypt an encrypted message using RSA private key
def decrypt(encrypted_message, d, n):
decrypted_message = [chr(mod_exp(char, d, n)) for char in encrypted_message]
return ''.join(decrypted_message)
# Given private key (d, n)
d = 151866547746253051295635226035893938791751845078785447899828018057531388462871545464706015227124468726243791927808641775756943107579456973792199785031609201199365234049843808423748718063366605801057175300794984536997489780732045509488007491731768743622439067912511699164119920532946691573524809997799878261
# Decrypt the encrypted message
decrypted_message = decrypt(encrypted_message, d, n)
print("Decrypted message:", decrypted_message)
このコードは、与えられた秘密鍵(d, n)を使用して暗号化されたメッセージを復号化し、元のメッセージを取得します。ただし、実際の運用では安全なライブラリを使用して復号化を行うことが重要です。
# encrypt関数の下に下記コードをコピーします。
def decrypt(encrypted_message, d, n):
decrypted_message = [chr(mod_exp(char, d, n)) for char in encrypted_message]
return ''.join(decrypted_message)
if __name__ == "__main__":
# print("Encrypted message:", encrypted_message)文の下に下記コードを追加します。
decrypted_message = decrypt(encrypted_message, d, n)
print("Decrypted message:", decrypted_message)
integer n: 33
Phi function: 20
RSA public key: (7, 33)
RSA private key: (3, 33)
Original message: QUANTUM
Encrypted message: [27, 13, 32, 12, 6, 13, 11]
Decrypted message:
何が問題なのでしょうか?
ここで、数が小さすぎるのでは?との疑問が出てきました。そもそもn=33では上手く符号化、復号化できていないのでは…
integer n: 253
Phi function: 220
RSA public key: (37, 253)
RSA private key: (113, 253)
Original message: QUANTUM
Encrypted message: [82, 101, 43, 144, 182, 101, 209]
Decrypted message: QUANTUM
まとめ
参考文献
[1] R. Rivest, A. Shamir, and L. Adleman, Communications of the ACM. 21 (2) 120 (1978)
[2] P. W. Shor, SIAM J. Comput., 26 1484 (1997), arXiv:quant-ph/9508027
[3] M. Nielsen and I. Chuang, “Quantum Computation and Quantum Information”, Cambridge University Press (2000) 量子コンピュータ、量子情報の教科書です。RSA暗号についてはAppendixに記載があります。