RSAの暗号と署名の違い ~Rustで実装してみた~
はじめに
先日RSA暗号についての記事を書きました。
今回はRSA署名についての書いていきます。RSA暗号とRSA署名はどちらもRSAアルゴリズムを利用しますが、それぞれの目的とプロセスは異なります。RSAアルゴリズムによる暗号と署名のちがいと、Rustでの実装をしていきます。(RSA暗号のRust実装についてはこちらの記事参照)
RSA暗号の基本
-
目的: メッセージの秘匿化。送信者がメッセージを暗号化して受信者に送信し、受信者がそのメッセージを復号化する。
-
鍵の使用方法:
- 暗号化には公開鍵を使用
- 復号化には秘密鍵を使用
-
プロセス:
- 送信者はメッセージ
を受信者の公開鍵(m, n) で暗号化し、暗号文e を生成c
c = m^e \ (\text{mod} \ n) - 受信者は暗号文
を自身の秘密鍵c で復号化し、元のメッセージd をみることができるm
m = c^d \ (\text{mod} \ n) - 送信者はメッセージ
RSA署名の基本
- 目的: 特定の人が特定のメッセージを発行したことを証明する。送信者がメッセージに署名をして、受信者がその署名を検証する。
-
鍵の使用方法:
- 署名生成には秘密鍵を使用
- 署名検証には公開鍵を使用
-
プロセス:
-
送信者はメッセージ
のハッシュ値m を計算し、それを秘密鍵H(m) で暗号化して署名d を生成する。\sigma \sigma = H(m)^d \ (\text{mod} \ n) -
受信者は署名
を送信者の公開鍵\sigma で復号化し、元のハッシュ値e を絵得る。H(m) H(m) = \sigma^e \ (\text{mod} \ n) -
受信者は復号化されたハッシュ値
とメッセージH(m) から計算したハッシュ値を比較し、一致すれば署名が有効であると判断するm
-
比較
特徴 | RSA暗号 | RSA署名 |
---|---|---|
目的 | メッセージの秘密保持 | メッセージの真正性と整合性の確認 |
暗号化/署名 | 公開鍵 ( e ) を使用 | 秘密鍵 ( d ) を使用 |
復号/検証 | 秘密鍵 ( d ) を使用 | 公開鍵 ( e ) を使用 |
処理手順 | ||
出力 | 暗号文 |
署名 |
数学的な説明
RSA署名の生成と検証のプロセスについて、具体的な数式を用いて説明します。
1. 署名の生成
メッセージ
-
ハッシュ値の計算:
- メッセージ
のハッシュ値m を計算します。H(m)
- メッセージ
-
署名の生成:
-
ハッシュ値
を秘密鍵H(m) で暗号化します:d \sigma = H(m)^d \ (\text{mod} \ n)
-
2. 署名の検証
署名
-
署名の復号化:
-
署名
を公開鍵\sigma で復号化するe \sigma^e \ (\text{mod} \ n) = (H(m)^d)^e \ (\text{mod} \ n) = H(m)^{de} \ (\text{mod} \ n) -
RSAの鍵生成プロセスにより、
であるため(鍵生成プロセス参照)de \equiv 1 \ (\text{mod} \ \phi(n)) H(m)^{de} \equiv H(m)^1 \equiv H(m) \ (\text{mod} \ n)
-
-
ハッシュ値の比較:
- 復号化されたハッシュ値
とメッセージH(m) から計算されたハッシュ値を比較する。m - 一致すれば署名が有効であると判断できる
- 復号化されたハッシュ値
RustでのRSA署名の実装
1. プロジェクトの設定
まず、新しいRustプロジェクトを作成します。
cargo new rsa_signature_example
cd rsa_signature_example
次に、Cargo.toml
ファイルに必要な依存関係を追加します。
[dependencies]
rsa = "0.5.0"
rand = "0.8.3"
sha2 = "0.10.2"
base64 = "0.13.0"
2. 実装コード
src/main.rs
ファイルに以下のコードを追加します。
extern crate rsa;
extern crate rand;
extern crate sha2;
extern crate base64;
use rsa::{PublicKey, RsaPrivateKey, RsaPublicKey, PaddingScheme};
use rand::rngs::OsRng;
use sha2::{Sha256, Digest};
use base64::{encode, decode};
fn main() {
// 1. 鍵の生成
let mut rng = OsRng;
let bits = 2048;
let private_key = RsaPrivateKey::new(&mut rng, bits).expect("Failed to generate a key");
let public_key = RsaPublicKey::from(&private_key);
// 公開鍵と秘密鍵を表示
println!("Private Key: {:?}", private_key);
println!("Public Key: {:?}", public_key);
// 2. メッセージのハッシュ値の計算
let message = "Hello, RSA!";
let mut hasher = Sha256::new();
hasher.update(message);
let hashed = hasher.finalize();
// 3. 署名の生成
let signature = private_key.sign(
PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_256)),
&hashed,
).expect("Failed to sign");
// 署名をBase64でエンコードして表示
let encoded_signature = encode(&signature);
println!("Signature: {}", encoded_signature);
// 4. 署名の検証
let decoded_signature = decode(&encoded_signature).expect("Failed to decode base64");
match public_key.verify(
PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_256)),
&hashed,
&decoded_signature,
) {
Ok(_) => println!("Signature is valid."),
Err(_) => println!("Signature is invalid."),
}
}
コードの説明
1. 鍵の生成
RsaPrivateKey::new
メソッドを使用して2048ビットのRSA鍵ペアを生成します。
let mut rng = OsRng;
let bits = 2048;
let private_key = RsaPrivateKey::new(&mut rng, bits).expect("Failed to generate a key");
let public_key = RsaPublicKey::from(&private_key);
2. メッセージのハッシュ値の計算
SHA-256ハッシュ関数を使用してメッセージのハッシュ値を計算します。
let message = "Hello, RSA!";
let mut hasher = Sha256::new();
hasher.update(message);
let hashed = hasher.finalize();
3. 署名の生成
秘密鍵を使用してハッシュ値を暗号化し、署名を生成します。署名はBase64エンコードして表示します。
let signature = private_key.sign(
PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_256)),
&hashed,
).expect("Failed to sign");
let encoded_signature = encode(&signature);
println!("Signature: {}", encoded_signature);
4. 署名の検証
公開鍵を使用して署名を復号化し、元のハッシュ値を取得します。復号化されたハッシュ値とメッセージのハッシュ値を比較し、一致すれば OK
に入り Signature is valid.
と出力します。
let decoded_signature = decode(&encoded_signature).expect("Failed to decode base64");
match public_key.verify(
PaddingScheme::new_pkcs1v15_sign(Some(rsa::Hash::SHA2_256)),
&hashed,
&decoded_signature,
) {
Ok(_) => println!("Signature is valid."),
Err(_) => println!("Signature is invalid."),
}
実行方法
プロジェクトのディレクトリで以下のコマンドを実行して、プログラムをビルドおよび実行します。
cargo run
「RSA鍵ペアを生成 -> メッセージのハッシュ値を計算 -> 署名の生成 -> その署名を検証する」
という一連の流れになっています。
Discussion