【PHP8.x】openssl_pkey_derive()関数の使い方
openssl_pkey_derive関数の使い方について、初心者にもわかりやすく解説します。
基本的な使い方
『openssl_pkey_derive関数は、通信相手の公開鍵と自身の秘密鍵を用いて、共有秘密鍵を計算(導出)する処理を実行する関数です。この関数は、主に楕円曲線ディフィー・ヘルマン鍵共有(ECDH)などの鍵交換プロトコルにおいて中心的な役割を果たします。その目的は、盗聴の可能性がある安全でない通信経路上であっても、通信を行う二者間が安全に共通の秘密情報を生成することにあります。引数には、通信相手から受け取った公開鍵と、自身が保有する秘密鍵を指定します。これらの鍵は、OpenSSLAsymmetricKeyオブジェクトやPEM形式の文字列などで指定することが可能です。処理が成功すると、計算された共有秘密が文字列として返され、失敗した場合はfalseが返されます。この関数によって得られた共有秘密は、そのまま暗号化キーとして使用されることは少なく、通常は鍵導出関数(KDF)への入力として用いられ、最終的な共通鍵を生成するための元データとして利用されます。
構文(syntax)
1<?php 2 3$myPrivateKey = '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----'; 4$peerPublicKey = '-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----'; 5 6$derivedSharedSecret = openssl_pkey_derive($myPrivateKey, $peerPublicKey);
引数(parameters)
OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $public_key, OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $private_key, int $key_length = 0
- OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $public_key: 共有鍵導出に使用する公開鍵。
OpenSSLAsymmetricKeyオブジェクト、OpenSSLCertificateオブジェクト、または公開鍵を表す配列や文字列で指定します。 - OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $private_key: 共有鍵導出に使用する秘密鍵。
OpenSSLAsymmetricKeyオブジェクト、OpenSSLCertificateオブジェクト、または秘密鍵を表す配列や文字列で指定します。 - int $key_length = 0: 導出する共有鍵のバイト長を指定する整数。0を指定すると、鍵のアルゴリズムによってデフォルトの長さが使用されます。
戻り値(return)
string|false
openssl_pkey_derive 関数は、共有秘密鍵の導出に成功した場合は文字列でその結果を返します。導出に失敗した場合は false を返します。
サンプルコード
PHP openssl_pkey_derive で共有秘密鍵を導出する
1<?php 2 3/** 4 * OpenSSLを利用して2つのキーペアから共有秘密鍵を導出する関数。 5 * システムエンジニアを目指す初心者向けに、Diffie-Hellmanのようなキー合意の概念を体験できるよう設計されています。 6 * 7 * このサンプルでは、まずキーペアを生成し、そのプライベートキーを文字列としてエクスポート・インポートすることで、 8 * キーワードである openssl_pkey_get_private の使用方法も示します。 9 * openssl_pkey_derive は、一方のプライベートキーと他方の公開キーを使って、 10 * 双方が同じ共有秘密鍵を生成できることを示します。 11 */ 12function deriveSharedSecretExample(): void 13{ 14 echo "共有秘密鍵導出の例を開始します。\n\n"; 15 16 // キーペア生成時の設定 (RSA 2048ビット) 17 $config = [ 18 "digest_alg" => "sha512", 19 "private_key_bits" => 2048, 20 "private_key_type" => OPENSSL_KEYTYPE_RSA, 21 ]; 22 23 // --- ユーザーAのキーペアを生成し、準備します --- 24 echo "ユーザーAのキーペアを生成中...\n"; 25 $keyResourceA = openssl_pkey_new($config); // OpenSSLAsymmetricKeyオブジェクトを生成 26 if ($keyResourceA === false) { 27 echo "エラー: ユーザーAのキーペア生成に失敗しました。\n"; 28 return; 29 } 30 31 // 生成したプライベートキーをPEM形式の文字列としてエクスポート 32 $privateKeyStringA = ''; 33 if (!openssl_pkey_export($keyResourceA, $privateKeyStringA)) { 34 echo "エラー: ユーザーAのプライベートキーのエクスポートに失敗しました。\n"; 35 openssl_pkey_free($keyResourceA); 36 return; 37 } 38 echo "ユーザーAのプライベートキーを文字列としてエクスポートしました。\n"; 39 40 // キーペアから公開キーを抽出 (PEM形式の文字列) 41 $detailsA = openssl_pkey_get_details($keyResourceA); 42 if ($detailsA === false || !isset($detailsA['key'])) { 43 echo "エラー: ユーザーAの公開キーの取得に失敗しました。\n"; 44 openssl_pkey_free($keyResourceA); 45 return; 46 } 47 $publicKeyA = $detailsA['key']; 48 echo "ユーザーAの公開キーを抽出しました。\n"; 49 // 一時的なキーリソースは不要になったため解放 50 openssl_pkey_free($keyResourceA); 51 52 // --- ユーザーBのキーペアを生成し、準備します --- 53 echo "\nユーザーBのキーペアを生成中...\n"; 54 $keyResourceB = openssl_pkey_new($config); // OpenSSLAsymmetricKeyオブジェクトを生成 55 if ($keyResourceB === false) { 56 echo "エラー: ユーザーBのキーペア生成に失敗しました。\n"; 57 return; 58 } 59 60 // 生成したプライベートキーをPEM形式の文字列としてエクスポート 61 $privateKeyStringB = ''; 62 if (!openssl_pkey_export($keyResourceB, $privateKeyStringB)) { 63 echo "エラー: ユーザーBのプライベートキーのエクスポートに失敗しました。\n"; 64 openssl_pkey_free($keyResourceB); 65 return; 66 } 67 echo "ユーザーBのプライベートキーを文字列としてエクスポートしました。\n"; 68 69 // キーペアから公開キーを抽出 (PEM形式の文字列) 70 $detailsB = openssl_pkey_get_details($keyResourceB); 71 if ($detailsB === false || !isset($detailsB['key'])) { 72 echo "エラー: ユーザーBの公開キーの取得に失敗しました。\n"; 73 openssl_pkey_free($keyResourceB); 74 return; 75 } 76 $publicKeyB = $detailsB['key']; 77 echo "ユーザーBの公開キーを抽出しました。\n"; 78 // 一時的なキーリソースは不要になったため解放 79 openssl_pkey_free($keyResourceB); 80 81 // --- エクスポートしたプライベートキー文字列を openssl_pkey_get_private で再度ロードします --- 82 // openssl_pkey_derive は OpenSSLAsymmetricKey オブジェクトをプライベートキー引数として受け取るため、このステップが必要です。 83 echo "\nエクスポートしたプライベートキーをロード中 (openssl_pkey_get_private)...\n"; 84 $loadedPrivateKeyA = openssl_pkey_get_private($privateKeyStringA); 85 if ($loadedPrivateKeyA === false) { 86 echo "エラー: ユーザーAのプライベートキーのロードに失敗しました。\n"; 87 return; 88 } 89 echo "ユーザーAのプライベートキーをロードしました。\n"; 90 91 $loadedPrivateKeyB = openssl_pkey_get_private($privateKeyStringB); 92 if ($loadedPrivateKeyB === false) { 93 echo "エラー: ユーザーBのプライベートキーのロードに失敗しました。\n"; 94 openssl_pkey_free($loadedPrivateKeyA); // 既にロードしたキーがあれば解放 95 return; 96 } 97 echo "ユーザーBのプライベートキーをロードしました。\n"; 98 99 echo "\n----------------------------------------\n"; 100 echo "キー交換と共有秘密鍵の導出\n"; 101 echo "----------------------------------------\n"; 102 103 // --- ユーザーAが共有秘密鍵を導出します --- 104 // ユーザーAは自身のプライベートキー ($loadedPrivateKeyA) とユーザーBの公開キー ($publicKeyB) を使用します。 105 // key_lengthは導出される鍵の長さをバイト単位で指定します (0はデフォルトの長さ)。 106 $keyLength = 32; // 例として32バイト (256ビット) の鍵長を指定 107 echo "ユーザーAが自身のプライベートキーとユーザーBの公開キーを使って共有秘密鍵を導出中 (長さ: {$keyLength}バイト)...\n"; 108 $sharedSecretA = openssl_pkey_derive($publicKeyB, $loadedPrivateKeyA, $keyLength); 109 110 if ($sharedSecretA === false) { 111 echo "エラー: ユーザーAによる共有秘密鍵の導出に失敗しました。\n"; 112 openssl_pkey_free($loadedPrivateKeyA); 113 openssl_pkey_free($loadedPrivateKeyB); 114 return; 115 } 116 echo "ユーザーAが導出した共有秘密鍵: " . bin2hex($sharedSecretA) . "\n"; 117 118 // --- ユーザーBが共有秘密鍵を導出します --- 119 // ユーザーBは自身のプライベートキー ($loadedPrivateKeyB) とユーザーAの公開キー ($publicKeyA) を使用します。 120 echo "ユーザーBが自身のプライベートキーとユーザーAの公開キーを使って共有秘密鍵を導出中 (長さ: {$keyLength}バイト)...\n"; 121 $sharedSecretB = openssl_pkey_derive($publicKeyA, $loadedPrivateKeyB, $keyLength); 122 123 if ($sharedSecretB === false) { 124 echo "エラー: ユーザーBによる共有秘密鍵の導出に失敗しました。\n"; 125 openssl_pkey_free($loadedPrivateKeyA); 126 openssl_pkey_free($loadedPrivateKeyB); 127 return; 128 } 129 echo "ユーザーBが導出した共有秘密鍵: " . bin2hex($sharedSecretB) . "\n"; 130 131 echo "\n----------------------------------------\n"; 132 echo "導出結果の検証\n"; 133 echo "----------------------------------------\n"; 134 135 // --- 両者が同じ共有秘密鍵を導出できたか検証します --- 136 if ($sharedSecretA === $sharedSecretB) { 137 echo "成功: ユーザーAとユーザーBは同じ共有秘密鍵を導出しました!\n"; 138 echo "この秘密鍵は、例えばAES暗号化の共通鍵として、安全な通信チャネルを確立するために使用できます。\n"; 139 } else { 140 echo "失敗: ユーザーAとユーザーBは異なる共有秘密鍵を導出しました。\n"; 141 } 142 143 // ロードしたキーリソースを解放します (メモリリーク防止)。 144 openssl_pkey_free($loadedPrivateKeyA); 145 openssl_pkey_free($loadedPrivateKeyB); 146 147 echo "\n共有秘密鍵導出の例を終了します。\n"; 148} 149 150// 関数の実行 151deriveSharedSecretExample();
PHP 8のopenssl_pkey_derive関数は、公開鍵暗号の原理を利用して、二者間で安全な共有秘密鍵を導出するために使用されます。これは、自身の秘密鍵と相手の公開鍵を組み合わせることで、双方が同じ共通の秘密鍵を生成する「キー合意」の仕組みを実現します。この導出された秘密鍵は、その後のデータ暗号化など、安全な通信チャネルを確立するための共通鍵として利用できます。
引数には、相手の公開鍵と自身の秘密鍵を指定します。これらはOpenSSLAsymmetricKeyオブジェクトやPEM形式の文字列などとして渡すことができます。オプションのkey_length引数で、導出される鍵の長さをバイト単位で指定可能です。関数は、導出された共有秘密鍵をバイナリ文字列として返しますが、処理に失敗した場合はfalseを返します。
サンプルコードでは、まずopenssl_pkey_new関数で2つのキーペアを生成し、それぞれのプライベートキーと公開キーを用意します。特に、openssl_pkey_get_private関数を使ってPEM形式でエクスポートされたプライベートキー文字列を、openssl_pkey_deriveが要求するOpenSSLAsymmetricKeyオブジェクトに変換している点に注目してください。その後、それぞれのユーザーが自身の秘密鍵と相手の公開鍵を用いてopenssl_pkey_deriveを実行し、両者が同じ共有秘密鍵を生成できることを確認しています。
このサンプルコードでは、各OpenSSL関数の戻り値を必ず確認し、エラー処理を行うことが重要です。生成またはロードしたキーリソースは、openssl_pkey_freeを呼び出して確実に解放してください。openssl_pkey_derive関数には、公開キーはPEM形式の文字列、プライベートキーはOpenSSLAsymmetricKeyオブジェクトとして渡す必要があります。キーワードのopenssl_pkey_get_privateは、プライベートキー文字列をこのオブジェクトに変換する役割です。導出した共有秘密鍵は非常に機密性が高いため、実運用ではログ出力などを避け、厳重に管理し、安全に破棄することが重要です。鍵長の設定はセキュリティ強度に直結しますので、適切な長さを選択してください。
PHP openssl_pkey_newとopenssl_pkey_deriveで鍵導出
1<?php 2 3/** 4 * OpenSSL拡張機能を使用して鍵ペアを生成し、その鍵ペアから共有シークレットを導出するデモンストレーション関数。 5 * システムエンジニアを目指す初心者が、openssl_pkey_new と openssl_pkey_derive の使い方を理解するのに役立ちます。 6 */ 7function demonstrateKeyDerivation(): void 8{ 9 echo "--- 鍵ペアの生成と鍵導出のデモンストレーション ---" . PHP_EOL . PHP_EOL; 10 11 // 1. RSA鍵ペアの生成 12 // openssl_pkey_new() を使用して新しい秘密鍵と公開鍵のペアを生成します。 13 // これは OpenSSLAsymmetricKey オブジェクトを返します。 14 // キーワードとして指定された 'php openssl_pkey_new' がここで使われています。 15 $config = [ 16 "private_key_bits" => 2048, // 鍵のビット長を2048ビットに設定 17 "private_key_type" => OPENSSL_KEYTYPE_RSA, // 鍵のタイプをRSAに指定 18 ]; 19 $privateKeyObject = openssl_pkey_new($config); 20 21 if (!$privateKeyObject) { 22 echo "エラー: 鍵ペアの生成に失敗しました。" . PHP_EOL; 23 // openssl_error_string() でOpenSSL関連のエラーメッセージを取得できます。 24 while ($msg = openssl_error_string()) { 25 echo "OpenSSLエラー: " . $msg . PHP_EOL; 26 } 27 return; 28 } 29 30 echo "ステップ1: RSA鍵ペアを生成しました (OpenSSLAsymmetricKeyオブジェクトとして)。" . PHP_EOL; 31 32 // 2. 秘密鍵と公開鍵の準備 33 // openssl_pkey_derive() の第一引数には公開鍵、第二引数には秘密鍵が必要です。 34 // ここで生成した $privateKeyObject は秘密鍵オブジェクトですが、 35 // その詳細情報には対応する公開鍵も含まれています。 36 // 導出のためには、公開鍵の形式を抽出する必要があります。 37 38 // 公開鍵の詳細を取得し、PEM形式の公開鍵文字列を取り出します。 39 $publicKeyDetails = openssl_pkey_get_details($privateKeyObject); 40 if (!isset($publicKeyDetails['key'])) { 41 echo "エラー: 公開鍵の抽出に失敗しました。" . PHP_EOL; 42 return; 43 } 44 $publicKeyString = $publicKeyDetails['key']; // PEM形式の公開鍵文字列 45 46 // 秘密鍵は OpenSSLAsymmetricKey オブジェクトのまま使用できます。 47 // これは openssl_pkey_derive の第二引数に直接渡すことができます。 48 $privateKeyForDerivation = $privateKeyObject; 49 50 echo "ステップ2: 導出に使用する公開鍵(PEM形式)と秘密鍵(OpenSSLAsymmetricKeyオブジェクト)を準備しました。" . PHP_EOL; 51 52 // 3. openssl_pkey_derive() を使用して共有シークレット(導出された鍵)を生成 53 // 第一引数に公開鍵、第二引数に秘密鍵を渡します。 54 // この例では、生成した鍵ペア自身の公開鍵と秘密鍵を使用していますが、 55 // 実際の鍵交換プロトコルでは、通信相手の公開鍵と自分の秘密鍵を使用し、 56 // 両者で同じ共有シークレットを安全に導出します。 57 // 第3引数は導出する鍵の長さ(バイト)。0の場合、OpenSSLがデフォルトの長さを決定します。 58 $keyLength = 32; // 32バイト (256ビット) の鍵を導出するように指定 59 $derivedKey = openssl_pkey_derive( 60 $publicKeyString, // 公開鍵 (PEM形式文字列) 61 $privateKeyForDerivation, // 秘密鍵 (OpenSSLAsymmetricKey オブジェクト) 62 $keyLength // 導出する鍵の長さ(バイト) 63 ); 64 65 if ($derivedKey === false) { 66 echo "エラー: 鍵導出に失敗しました。" . PHP_EOL; 67 while ($msg = openssl_error_string()) { 68 echo "OpenSSLエラー: " . $msg . PHP_EOL; 69 } 70 return; 71 } 72 73 echo "ステップ3: openssl_pkey_derive() を使用して鍵を導出しました。" . PHP_EOL; 74 echo "導出された鍵の長さ: " . strlen($derivedKey) . "バイト" . PHP_EOL; 75 echo "導出された鍵 (hex): " . bin2hex($derivedKey) . PHP_EOL; 76 77 // 鍵オブジェクトはスクリプト終了時に自動的に解放されます。 78 // PHP 8.0以降では openssl_pkey_free() を明示的に呼び出す必要はほとんどありません。 79 80 echo PHP_EOL . "--- デモンストレーション終了 ---" . PHP_EOL; 81} 82 83// 上記のデモンストレーション関数を実行 84demonstrateKeyDerivation();
openssl_pkey_derive関数は、OpenSSL拡張機能の一部として、非対称鍵ペアから「共有シークレット」または「導出鍵」と呼ばれる共通の鍵を生成するために使用されます。主にDiffie-Hellman鍵交換プロトコルなどで、通信相手と安全に共通の暗号鍵を確立する際に活用されます。
この関数は、第一引数$public_keyに相手の公開鍵を、第二引数$private_keyに自身の秘密鍵を渡して呼び出します。これらの鍵は、openssl_pkey_new()関数で生成されるOpenSSLAsymmetricKeyオブジェクトや、PEM形式の文字列など様々な形式で指定できます。第三引数$key_lengthには、導出する鍵の長さをバイト単位で指定し、0を指定するとOpenSSLが適切なデフォルト長を決定します。
処理に成功すると、バイナリ形式の導出鍵が文字列として返されます。これは、通信相手と自身が互いの鍵情報から独立して生成できる共通の鍵となり、以降の暗号化通信に利用されます。鍵導出に失敗した場合は、falseが返されます。この機能により、安全なデータ通信の基盤となる共通鍵を効率的に確立することが可能です。
PHPのopenssl_pkey_derive関数は、公開鍵と秘密鍵から共有シークレットを導出する際に使用します。サンプルコードは鍵生成から導出までの一連の流れを示していますが、実際のシステムでは生成した秘密鍵を安全な場所に厳重に保管することが非常に重要です。また、openssl_pkey_derive関数は引数として複数の鍵形式を受け入れるため、渡す鍵が適切な形式であるか注意してください。この導出された鍵は、一般的に通信相手の公開鍵と自分の秘密鍵から生成され、双方で同じ共有シークレットを確立するために利用されます。関数がfalseを返した場合は、openssl_error_string()で詳細なエラーメッセージを確認し、問題解決に役立ててください。導出する鍵の長さはセキュリティ要件に合わせて適切に指定することが求められます。