【PHP8.x】sodium_crypto_sign関数の使い方

作成日: 更新日:

『sodium_crypto_sign関数は、与えられたメッセージに対して、秘密鍵を用いてデジタル署名を生成し、メッセージと署名を結合した「署名付きメッセージ」を作成する関数です。この処理には、Ed25519という高速かつ安全な公開鍵署名アルゴリズムが使用されます。関数の第一引数に署名対象のメッセージを、第二引数に署名者の秘密鍵を渡して実行します。この秘密鍵は、事前にsodium_crypto_sign_keypair関数などで生成した鍵ペアの一部でなければなりません。関数が成功すると、元のメッセージの先頭に64バイトの署名データが連結されたバイナリ文字列が返されます。この署名付きメッセージを通信相手に送信することで、受信者は対応する公開鍵を用いて署名の正当性を検証できます。検証にはsodium_crypto_sign_open関数が使われ、メッセージが秘密鍵の所有者によって作成されたこと(認証)と、輸送中に改ざんされていないこと(完全性)を保証することが可能になります。

基本的な使い方

構文(syntax)

1<?php
2$secret_key = sodium_crypto_sign_secretkey(sodium_crypto_sign_keypair());
3$message_to_sign = 'Your message to be signed.';
4$signed_data = sodium_crypto_sign($message_to_sign, $secret_key);

引数(parameters)

string $message, string $secret_key

  • string $message: 署名する対象のメッセージを指定する文字列
  • string $secret_key: 署名に使用する秘密鍵を指定する文字列

戻り値(return)

string

署名されたバイナリデータが文字列として返されます。

サンプルコード

PHP sodium_crypto_sign_detached で署名と検証する

1<?php
2
3/**
4 * PHPのlibsodium拡張を使って、メッセージの署名と検証を行うサンプルです。
5 * sodium_crypto_sign_detached 関数に焦点を当てています。
6 *
7 * デジタル署名は、メッセージの作成者が誰であるかを確認し、
8 * メッセージが転送中に改ざんされていないことを保証するために使用されます。
9 */
10function generateAndVerifyDetachedSignature(): void
11{
12    echo "--- デジタル署名 (detached) のデモンストレーション ---" . PHP_EOL;
13
14    // 1. 署名キーペアの生成
15    // 安全なデジタル署名のためには、秘密鍵を厳重に管理する必要があります。
16    // sodium_crypto_sign_keypair() は署名と検証のためのキーペア(秘密鍵と公開鍵)を生成します。
17    $keyPair = sodium_crypto_sign_keypair();
18    $secretKey = sodium_crypto_sign_secretkey($keyPair); // 秘密鍵の抽出
19    $publicKey = sodium_crypto_sign_publickey($keyPair); // 公開鍵の抽出
20
21    echo "署名キーペアが生成されました。" . PHP_EOL;
22
23    // 署名するオリジナルメッセージ
24    $originalMessage = "こんにちは、これは秘密のメッセージです。";
25    echo "オリジナルメッセージ: \"" . $originalMessage . "\"" . PHP_EOL;
26
27    // 2. メッセージの署名 (detached形式)
28    // sodium_crypto_sign_detached は、指定されたメッセージと秘密鍵を使って、
29    // メッセージの署名だけを生成します。署名とメッセージは別々に扱われます。
30    $signature = sodium_crypto_sign_detached($originalMessage, $secretKey);
31
32    echo "メッセージの署名が生成されました。" . PHP_EOL;
33    // 署名はバイナリデータなので、可読性のためBase64エンコードして表示
34    echo "生成された署名 (Base64エンコード): " . base64_encode($signature) . PHP_EOL;
35
36    // 3. 署名の検証
37    // sodium_crypto_sign_verify_detached を使って、
38    // 生成された署名、元のメッセージ、公開鍵が一致するか検証します。
39    // 検証が成功すれば true、失敗すれば false を返します。
40    $isValid = sodium_crypto_sign_verify_detached($signature, $originalMessage, $publicKey);
41
42    if ($isValid) {
43        echo "署名の検証結果: 成功。メッセージは改ざんされておらず、作成者は信頼できます。" . PHP_EOL;
44    } else {
45        echo "署名の検証結果: 失敗。メッセージが改ざんされたか、署名が不正です。" . PHP_EOL;
46    }
47
48    echo PHP_EOL;
49
50    // --- 失敗例: メッセージを改ざんした場合 ---
51    echo "--- 失敗例: メッセージ改ざんの試み ---" . PHP_EOL;
52    $tamperedMessage = "こんにちは、これは改ざんされたメッセージです!"; // メッセージを意図的に変更
53    echo "改ざんされたメッセージ: \"" . $tamperedMessage . "\"" . PHP_EOL;
54
55    $isTamperedValid = sodium_crypto_sign_verify_detached($signature, $tamperedMessage, $publicKey);
56
57    if ($isTamperedValid) {
58        echo "署名の検証結果: 成功。(予期せぬ結果: 改ざんを検出できませんでした。)" . PHP_EOL;
59    } else {
60        echo "署名の検証結果: 失敗。メッセージが改ざんされたためです。(期待通りの結果)" . PHP_EOL;
61    }
62
63    echo PHP_EOL;
64
65    // --- 失敗例: 不正な署名を使用した場合 ---
66    echo "--- 失敗例: 不正な署名の使用 ---" . PHP_EOL;
67    // 別のメッセージで生成された署名や、ランダムなバイト列など
68    $anotherKeyPair = sodium_crypto_sign_keypair();
69    $anotherSecretKey = sodium_crypto_sign_secretkey($anotherKeyPair);
70    $fakeSignature = sodium_crypto_sign_detached("全く別のコンテンツ", $anotherSecretKey); // 別の秘密鍵とメッセージで生成した署名
71    echo "オリジナルメッセージ: \"" . $originalMessage . "\"" . PHP_EOL;
72    echo "不正な署名 (Base64エンコード): " . base64_encode($fakeSignature) . PHP_EOL;
73
74    $isFakeSignatureValid = sodium_crypto_sign_verify_detached($fakeSignature, $originalMessage, $publicKey);
75
76    if ($isFakeSignatureValid) {
77        echo "署名の検証結果: 成功。(予期せぬ結果: 不正な署名を検出できませんでした。)" . PHP_EOL;
78    } else {
79        echo "署名の検証結果: 失敗。署名が不正なためです。(期待通りの結果)" . PHP_EOL;
80    }
81}
82
83// スクリプトのエントリーポイント
84// sodium拡張がPHPにロードされているかを確認します。
85if (!extension_loaded('sodium')) {
86    echo "エラー: 'sodium' 拡張機能がPHPにロードされていません。" . PHP_EOL;
87    echo "php.iniファイルに 'extension=sodium' を追加してPHPを再起動してください。" . PHP_EOL;
88} else {
89    // 拡張機能が利用可能であれば、デモンストレーション関数を実行します。
90    generateAndVerifyDetachedSignature();
91}
92

PHPのsodium拡張機能は、安全な暗号化機能を提供し、デジタル署名もその一つです。デジタル署名は、メッセージの作成者が誰であるかを確認し、メッセージが転送中に改ざんされていないことを保証するために利用されます。このサンプルコードでは、署名とメッセージを別々に扱う「detached(分離された)」形式の署名を生成・検証するデモンストレーションを行っています。

まず、sodium_crypto_sign_keypair()関数で署名と検証に必要なキーペア(秘密鍵と公開鍵)を安全に生成します。秘密鍵は署名に使われ、厳重に管理する必要があります。次に、sodium_crypto_sign_detached()関数が活躍します。この関数は、指定された $message(署名する元のメッセージ)と $secret_key(生成した秘密鍵)を使って、メッセージ本体とは独立した「署名」データのみを生成し、string型で返します。

生成された署名は、sodium_crypto_sign_verify_detached()関数により検証されます。この関数は、署名、元のメッセージ、そして公開鍵を照合し、メッセージが改ざんされていないか、署名が正規のものであるかを確認します。検証が成功すればメッセージの信頼性が確認され、失敗すればメッセージの改ざんや不正な署名が検出されます。サンプルコードでは、意図的にメッセージを改ざんしたり、不正な署名を使用したりする失敗例も示されており、デジタル署名のセキュリティ上の有効性を具体的に理解できます。スクリプト実行前にsodium拡張機能がロードされていることを確認してください。

このコードで最も重要なのは鍵の管理です。署名を作成する「秘密鍵」は、絶対に他人に知られてはいけません。実際のシステムではファイルや専用の管理サービスで厳重に保管してください。一方、署名を検証する「公開鍵」は、検証者に安全に配布する必要があります。また、sodium関数が生成する署名や鍵はバイナリデータのため、データベース保存や通信で利用する際はBase64などでエンコードするのが一般的ですが、検証前には必ず元のバイナリデータに戻す必要があります。detached署名では、元のメッセージ、署名、公開鍵の3つすべてが揃わないと正しく検証できない点にも注意してください。

PHP Libsodiumで署名と検証を行う

1<?php
2
3// Libsodium拡張が利用可能かチェックします。
4// これがないと暗号化機能は利用できません。
5if (!extension_loaded('sodium')) {
6    die('Libsodium extension is not loaded. Please enable it in php.ini.');
7}
8
9/**
10 * メッセージのデジタル署名を生成し、その署名を検証するサンプルコード。
11 *
12 * この関数は、Libsodiumライブラリを使用して、メッセージのデジタル署名を生成し、
13 * その署名が元のメッセージと公開鍵に対して有効であることを検証する一連の処理を示します。
14 * `sodium_crypto_sign_keypair`, `sodium_crypto_sign_secretkey`,
15 * `sodium_crypto_sign_publickey`, `sodium_crypto_sign_detached`,
16 * `sodium_crypto_sign_verify_detached` 関数を使用します。
17 */
18function signAndVerifyMessage(): void
19{
20    echo "--- Libsodium デジタル署名と検証のサンプル ---\n\n";
21
22    // 1. 署名鍵ペアの生成
23    // メッセージの署名と検証に必要な秘密鍵と公開鍵のペアを生成します。
24    // 秘密鍵はメッセージを署名するために使用され、公開鍵は署名を検証するために使用されます。
25    $keyPair = sodium_crypto_sign_keypair();
26    $secretKey = sodium_crypto_sign_secretkey($keyPair); // 署名に使う秘密鍵を鍵ペアから抽出
27    $publicKey = sodium_crypto_sign_publickey($keyPair); // 検証に使う公開鍵を鍵ペアから抽出
28
29    echo "鍵ペアが生成されました。\n\n";
30
31    // 2. 署名する元のメッセージを準備
32    $originalMessage = 'これはデジタル署名されるべき重要なメッセージです。';
33    echo "元のメッセージ: \"{$originalMessage}\"\n\n";
34
35    // 3. メッセージへの署名 (detachedモード)
36    // 秘密鍵を使用して、元のメッセージのデジタル署名を生成します。
37    // `sodium_crypto_sign_detached` は署名データのみを返します。
38    // (引数: 署名するメッセージ, 秘密鍵)
39    $signature = sodium_crypto_sign_detached($originalMessage, $secretKey);
40
41    echo "メッセージが署名されました。\n";
42    // echo "生成された署名 (Base64): " . base64_encode($signature) . "\n\n"; // デバッグ目的で署名を表示
43
44    // 4. 署名の検証
45    // 公開鍵、元のメッセージ、そして生成された署名を用いて、署名が有効であるかを検証します。
46    // `sodium_crypto_sign_verify_detached` は、署名が有効であれば `true` を、無効であれば `false` を返します。
47    // (引数: 署名, 元のメッセージ, 公開鍵)
48    $isValid = sodium_crypto_sign_verify_detached($signature, $originalMessage, $publicKey);
49
50    echo "署名の検証結果 (元のメッセージ): ";
51    if ($isValid) {
52        echo "✅ 署名は有効です。メッセージは改ざんされていません。\n";
53    } else {
54        echo "❌ 署名は無効です。メッセージが改ざんされたか、異なる鍵で署名されています。\n";
55    }
56
57    echo "\n--- 署名検証の失敗例 (メッセージ改ざん) ---\n\n";
58
59    // 5. 署名検証の失敗例: メッセージが改ざんされた場合
60    $tamperedMessage = 'これは改ざんされたメッセージです!';
61    echo "改ざんされたメッセージ: \"{$tamperedMessage}\"\n";
62    $isInvalidTampered = sodium_crypto_sign_verify_detached($signature, $tamperedMessage, $publicKey);
63
64    echo "改ざんされたメッセージでの検証結果: ";
65    if ($isInvalidTampered) {
66        echo "❌ 署名は有効です (予期せぬ結果!この行は通常実行されません)\n";
67    } else {
68        echo "✅ 署名は無効です。期待通りの結果です。\n"; // メッセージが改ざんされたため、検証は失敗します
69    }
70
71    echo "\n--- 署名検証の失敗例 (異なる鍵) ---\n\n";
72
73    // 6. 署名検証の失敗例: 異なる公開鍵で検証を試みた場合
74    // 別の鍵ペアを生成し、その公開鍵で検証を試みます。
75    $otherKeyPair = sodium_crypto_sign_keypair();
76    $otherPublicKey = sodium_crypto_sign_publickey($otherKeyPair);
77    echo "別の公開鍵を使用して検証を試みます。\n";
78    $isInvalidOtherKey = sodium_crypto_sign_verify_detached($signature, $originalMessage, $otherPublicKey);
79
80    echo "異なる公開鍵での検証結果: ";
81    if ($isInvalidOtherKey) {
82        echo "❌ 署名は有効です (予期せぬ結果!この行は通常実行されません)\n";
83    } else {
84        echo "✅ 署名は無効です。期待通りの結果です。\n"; // 異なる公開鍵のため、検証は失敗します
85    }
86}
87
88// 関数を実行します。
89signAndVerifyMessage();

このコードは、PHPのLibsodium拡張を利用したデジタル署名の生成と検証の方法を初心者にも分かりやすく示しています。デジタル署名は、メッセージが改ざんされていないこと(完全性)と、メッセージの送信元が本物であること(真正性)を証明するために重要な技術です。

まず、sodium_crypto_sign_keypair関数で、メッセージを署名するための秘密鍵と、その署名を検証するための公開鍵のペアを生成します。秘密鍵は誰にも知られてはいけない重要な鍵で、署名専用です。公開鍵は広く公開され、署名の検証に使われます。

次に、sodium_crypto_sign_detached関数を使用して、用意した元のメッセージと秘密鍵からデジタル署名を生成します。この関数は引数として署名したいメッセージと秘密鍵を受け取り、string型の署名データのみを戻り値として返します。これは署名データと元のメッセージが分離している「detached(分離型)」の署名方式です。

生成された署名は、sodium_crypto_sign_verify_detached関数によって検証されます。この関数は、引数として署名データ、元のメッセージ、そして公開鍵を受け取ります。もし署名が有効であり、メッセージが改ざんされていない場合はtrueを返し、そうでなければfalseを返します。サンプルコードでは、メッセージが改ざんされた場合や、異なる公開鍵で検証しようとした場合に検証が失敗する例も示されており、デジタル署名がどのように機能するかの理解を深めることができます。

Libsodium拡張の有効化は必須です。これがないとデジタル署名機能は利用できません。秘密鍵はメッセージに署名する能力を持つため、厳重に管理し、絶対に漏洩させないでください。漏洩した場合、署名の信頼性が失われ、悪用される可能性があります。署名の検証に使用する公開鍵は、それが本当に信頼できる相手のものであることを別途確認する仕組みも重要です。サンプルで利用しているsodium_crypto_sign_detachedは、メッセージ本体とは別に署名データのみを生成します。そのため、署名と元のメッセージの両方を正しく保管・転送する必要があります。デジタル署名は、メッセージの作成者を保証し、内容の改ざんがないことを確認する強力な手段となります。

【PHP8.x】sodium_crypto_sign関数の使い方 | いっしー@Webエンジニア