はじめに
この実験(Lab)では、現代暗号の礎石である Diffie-Hellman (DH) 鍵交換の基本について探求します。DH プロトコルは、事前に何も知らない二者間が、安全でない通信チャネルを介して共有秘密鍵を共同で確立することを可能にします。この共有秘密鍵は、その後の通信を共通鍵暗号方式を用いて暗号化するために使用できます。
本実験では、強力なコマンドラインツールである openssl を使用して、DH プロセス全体をシミュレーションします。これには、公開パラメータの生成、二つの異なる当事者(パーティ)の秘密鍵と公開鍵の作成、そして最終的な共有秘密鍵の導出と検証が含まれます。この実験を終える頃には、この不可欠な暗号学的ハンドシェイクがどのように機能するかについて、実践的な理解が得られるでしょう。
ディフィー・ヘルマン鍵交換の原理
このステップでは、Diffie-Hellman 鍵交換の理論的原理について解説します。このステップで実行するコマンドはなく、目的は実装する前に概念を理解することです。
プロセスは以下の通りに進みます。
公開パラメータの合意: アリスとボブと呼ぶ二者間は、まず二つの公開数値、すなわち大きな素数 $p$(法、modulus)と基数 $g$(生成元、generator)について合意します。これらの数値は秘密ではなく、安全でないチャネルを通じて送信できます。
秘密鍵の生成:
- アリスは秘密の整数 $a$ を選択します。彼女はこの数値を自分だけが保持します。
- ボブは秘密の整数 $b$ を選択します。彼もこの数値を自分だけが保持します。
公開鍵の計算:
- アリスは自身の秘密鍵 $a$ を用いて公開鍵 $A$ を計算します:$A = g^a \mod p$。彼女はこの公開鍵 $A$ をボブに送信します。
- ボブは自身の秘密鍵 $b$ を用いて公開鍵 $B$ を計算します:$B = g^b \mod p$。彼はこの公開鍵 $B$ をアリスに送信します。
共有秘密鍵の導出:
- アリスはボブの公開鍵 $B$ を受け取り、自身の秘密鍵 $a$ を用いて共有秘密鍵 $S$ を計算します:$S = B^a \mod p$。
- ボブはアリスの公開鍵 $A$ を受け取り、自身の秘密鍵 $b$ を用いて共有秘密鍵 $S$ を計算します:$S = A^b \mod p$。
合同算術の性質により、アリスとボブはどちらも $S$ について全く同じ値に到達します。
- アリスの計算:$S = (g^b \mod p)^a \mod p = g^{ba} \mod p$
- ボブの計算:$S = (g^a \mod p)^b \mod p = g^{ab} \mod p$
チャネルを盗聴している第三者は、$p$、$g$、$A$、$B$ を見ることができますが、秘密鍵 $a$ や $b$ を容易に計算することはできません。この困難さは離散対数問題(discrete logarithm problem)として知られており、これが Diffie-Hellman 交換のセキュリティの基盤となっています。
次のステップでは、openssl を使用してこれらの操作を実行します。
ディフィー・ヘルマンパラメータの生成
このステップでは、両当事者が使用する公開 DH パラメータ($p$と$g$)を生成します。openssl ツールは、適切な素数と生成元を見つけるための複雑な数学処理を代行してくれます。本実験のすべての操作は、デフォルトのディレクトリ ~/project 内で実行されます。
パラメータを生成するために openssl dhparam コマンドを使用します。ここでは、この目的に適した一般的で安全な長さである 2048 ビットの鍵長を指定します。
ターミナルで以下のコマンドを実行してください。
openssl dhparam -out dhparam.pem 2048
このコマンドを分解してみましょう。
openssl dhparam: OpenSSL の DH パラメータ管理ツールを呼び出します。-out dhparam.pem: このフラグは、出力をdhparam.pemという名前のファイルに保存することを指定します。2048: これは、素数法 $p$ の希望するビット長です。
このコマンドは、強力な素数を見つけるための探索を行うため、完了までに 1 分ほどかかる場合があります。実行中は、以下のような出力が表示されます。
Generating DH parameters, 2048 bit long safe prime
完了すると、カレントディレクトリに dhparam.pem という名前のファイルが作成されます。このファイルには、鍵交換のために両当事者が使用する公開パラメータが含まれています。
Party A の鍵の生成
このステップでは、「パーティ A」の動作をシミュレーションします。パーティ A は、前のステップで共有した DH パラメータを使用して自身の秘密鍵を生成し、それに対応する公開鍵を導出します。
まず、パーティ A の秘密鍵を生成します。この鍵は、理論的な例における秘密の数値 $a$ に相当します。ここでは、汎用的な鍵生成ユーティリティである openssl genpkey コマンドを使用します。
パーティ A の秘密鍵を生成するために、以下のコマンドを実行します。
openssl genpkey -paramfile dhparam.pem -out a_private_key.pem
genpkey: 秘密鍵を生成するためのコマンドです。-paramfile dhparam.pem:genpkeyに対し、dhparam.pemファイルのパラメータを使用して DH 鍵を作成するように指示します。-out a_private_key.pem: 生成された秘密鍵をa_private_key.pemファイルに保存します。
次に、パーティ A は秘密鍵から公開鍵を導出する必要があります。この公開鍵がパーティ B と共有されるものです。
公開鍵を抽出するために、このコマンドを実行します。
openssl pkey -in a_private_key.pem -pubout -out a_public_key.pem
pkey: 公開鍵と秘密鍵を管理するためのコマンドです。-in a_private_key.pem: 入力となる秘密鍵を指定します。-pubout: このフラグは、鍵の公開部分を出力するようにコマンドに指示します。-out a_public_key.pem: 結果の公開鍵をa_public_key.pemに保存します。
これで二つの新しいファイルができました。パーティ A が秘密に保持すべき a_private_key.pem と、パーティ A が安全でないチャネルを通じてパーティ B に送信する a_public_key.pem です。
Party B の鍵の生成
このステップでは、「パーティ B」に対して、パーティ A と同様のアクションを実行します。パーティ B はパーティ A とは独立して動作しますが、dhparam.pem からの同じ公開 DH パラメータを使用します。
まず、パーティ B の秘密鍵を生成します。これは、理論的な例における秘密の数値 $b$ に対応します。
以下のコマンドを実行します。
openssl genpkey -paramfile dhparam.pem -out b_private_key.pem
このコマンドの構造はパーティ A のものと同一ですが、区別するために出力を b_private_key.pem に保存します。
次に、パーティ A と同様に、パーティ B も新しい秘密鍵から公開鍵を導出する必要があります。
パーティ B の公開鍵を抽出するために、このコマンドを実行します。
openssl pkey -in b_private_key.pem -pubout -out b_public_key.pem
実際の交換において、この時点でパーティ A はパーティ B の公開鍵(b_public_key.pem)を持ち、パーティ B はパーティ A の公開鍵(a_public_key.pem)を持つことになります。両当事者はそれぞれの秘密鍵を秘密に保っています。これで、プロトコルの鍵生成と交換の部分のシミュレーションが成功裏に完了しました。
共通シークレットの計算
これは最後にして最も重要なステップです。ここでは、両当事者がそれぞれ自身の秘密鍵と相手の公開鍵を使用して、独立して共有シークレットを計算します。プロトコルが成功した場合、両者は全く同じシークレット値に到達します。
まず、パーティ A の観点から共有シークレットを計算します。パーティ A は自身の秘密鍵(a_private_key.pem)とパーティ B の公開鍵(b_public_key.pem)を使用します。
以下のコマンドを実行します。
openssl pkeyutl -derive -inkey a_private_key.pem -peerkey b_public_key.pem -out a_shared_secret.bin
pkeyutl: 公開鍵操作を実行するためのユーティリティです。-derive: このアクションは、ユーティリティに共有シークレットを導出するように指示します。-inkey a_private_key.pem: パーティ A 自身の秘密鍵を指定します。-peerkey b_public_key.pem: 相手(「ピア」)の公開鍵を指定します。-out a_shared_secret.bin: 結果のバイナリシークレットをファイルに保存します。
次に、パーティ B の観点から共有シークレットを計算します。パーティ B は自身の秘密鍵(b_private_key.pem)とパーティ A の公開鍵(a_public_key.pem)を使用します。
以下のコマンドを実行します。
openssl pkeyutl -derive -inkey b_private_key.pem -peerkey a_public_key.pem -out b_shared_secret.bin
これで、a_shared_secret.bin と b_shared_secret.bin という二つのファイルができました。鍵交換の成功を検証するには、これら二つのファイルが同一でなければなりません。これを確認するために cmp (compare) コマンドを使用できます。
cmp a_shared_secret.bin b_shared_secret.bin
ファイルが同一であれば、このコマンドは何も出力せずサイレントに終了します。この沈黙は成功を意味します!
視覚的に確認するために、両ファイルの暗号学的ハッシュを計算することもできます。ハッシュは一致しなければなりません。
sha256sum *.bin
両ファイルが全く同じ SHA256 ハッシュを持つ出力が表示されるはずです。実際のハッシュ値は実行ごとに異なりますが、両ファイルで同一であることが重要です。
e3705a4ab5ae5d86f59dfe968f0177b49d5144e2d731dbd8d41b2eda318412ec a_shared_secret.bin
e3705a4ab5ae5d86f59dfe968f0177b49d5144e2d731dbd8d41b2eda318412ec b_shared_secret.bin
(注:あなたのハッシュ値はこの例とは異なりますが、重要なのは a_shared_secret.bin と b_shared_secret.bin のハッシュが同一であり、両当事者が同じ共有シークレットを導出したことを証明している点です。)
おめでとうございます、Diffie-Hellman 鍵交換を正常に実行できました!
まとめ
この実験(Lab)では、openssl コマンドラインツールを使用して、完全な Diffie-Hellman 鍵交換をシミュレーションすることに成功しました。この重要な暗号プロトコルの基本的なステップについて、実践的な経験を積みました。
以下の方法を学習しました。
- 共有公開 DH パラメータの生成(
openssl dhparam)。 - これらのパラメータに基づいた二つの独立したパーティの秘密鍵および公開鍵ペアの作成(
openssl genpkeyおよびopenssl pkey)。 - 自身の秘密鍵とピアの公開鍵から共有シークレットを導出すること(
openssl pkeyutl -derive)。 - 両当事者が独立して全く同じシークレットを計算したことを検証し、交換の成功を証明すること。
このプロセスは、インターネット上のデータを保護する TLS/SSL のような安全な通信システムの基本的な構成要素です。これにより、安全でないネットワーク上で開始された場合でも、二つの当事者がどのように通信のための安全なチャネルを確立できるかについて、実践的な理解を得ることができました。



