Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【PHP8.x】openssl_cms_sign()関数の使い方

openssl_cms_sign関数の使い方について、初心者にもわかりやすく解説します。

作成日: 更新日:

基本的な使い方

openssl_cms_sign関数は、指定されたデータにデジタル署名を施し、CMS (Cryptographic Message Syntax) 形式のメッセージとして生成する関数です。CMSは、電子署名や暗号化、データ圧縮など、様々な暗号学的処理を扱うための標準的なデータ構造です。この関数を使用することで、データの完全性(データが途中で改ざんされていないこと)と、署名者の認証(データが確かに特定の人物や組織によって署名されたこと)を保証できます。

具体的には、署名したい元のデータ、署名を行うエンティティ(個人や組織)のデジタル証明書、そしてその証明書に対応する秘密鍵を引数として指定します。関数はこれらの情報をもとに、データを暗号学的に処理し、署名済みのCMSメッセージを生成します。生成されたメッセージは文字列として返され、この文字列を他のシステムに渡すことで、受け取った側は署名を検証し、データの信頼性を確認することができます。

また、必要に応じて複数の署名者を指定したり、署名に含まれるヘッダー情報をカスタマイズしたりすることも可能です。処理に失敗した場合、この関数はfalseを返します。この機能は、セキュアな電子メール通信(S/MIMEなど)や、デジタルドキュメントの真正性を保証する場面で広く利用されます。システムエンジニアにとって、データのセキュリティと信頼性を確保するために重要な役割を果たす関数の一つです。

構文(syntax)

1<?php
2
3$inputFilename = 'path/to/data_to_sign.txt';
4$outputFilename = 'path/to/signed_output.cms';
5$certificate = 'file:///path/to/signer_certificate.pem'; // または証明書データ文字列
6$privateKey = 'file:///path/to/signer_private_key.pem';   // または秘密鍵データ文字列
7$headers = ['Content-Type' => 'application/pkcs7-mime']; // 任意のヘッダー配列
8$flags = OPENSSL_CMS_DETACHED; // 例えば OPENSSL_CMS_DETACHED, 0でデフォルト
9$encoding = OPENSSL_ENCODING_SMIME; // 例えば OPENSSL_ENCODING_SMIME, OPENSSL_ENCODING_DER
10
11$success = openssl_cms_sign(
12    $inputFilename,
13    $outputFilename,
14    $certificate,
15    $privateKey,
16    $headers,
17    $flags,
18    $encoding
19);
20
21// 最後の引数 $untrusted_certificates_filename (省略可能) はここでは含まれていません。
22
23?>

引数(parameters)

string $input_filename, string $output_filename, OpenSSLCertificate|string $certificate, OpenSSLAsymmetricKey|array|string $private_key, ?array $headers, int $flags = 0, int $encoding = OPENSSL_CMS_BINARY, ?string $untrusted_certificates_filename = null

  • string $input_filename: 署名対象のデータが格納されているファイル名を指定します。
  • string $output_filename: 署名済みのCMSデータを格納するファイル名を指定します。
  • OpenSSLCertificate|string $certificate: 署名に使用する証明書を指定します。OpenSSLCertificateオブジェクトまたは証明書ファイルパスを指定できます。
  • OpenSSLAsymmetricKey|array|string $private_key: 署名に使用する秘密鍵を指定します。OpenSSLAsymmetricKeyオブジェクト、秘密鍵ファイルパス、または秘密鍵を格納した配列を指定できます。
  • ?array $headers: 追加するカスタムヘッダーを指定する配列です。
  • int $flags = 0: 署名処理の挙動を制御するフラグを指定します。デフォルトは0です。
  • int $encoding = OPENSSL_CMS_BINARY: 出力されるCMSデータのエンコーディングを指定します。デフォルトはバイナリです。
  • ?string $untrusted_certificates_filename = null: 信頼されない証明書(中間証明書など)が格納されているファイル名を指定します。

戻り値(return)

bool

openssl_cms_sign 関数は、CMS (Cryptographic Message Syntax) メッセージの署名に成功したかどうかに応じて、成功した場合は true を、失敗した場合は false を返します。

サンプルコード

PHP openssl_cms_sign でファイルをCMS署名する

1<?php
2
3/**
4 * このスクリプトは、openssl_cms_sign 関数を使用してファイルをCMS形式でデジタル署名する方法を示します。
5 * 動作確認のため、一時的な秘密鍵と自己署名証明書を生成します。
6 * 実際のシステムでは、信頼された既存の秘密鍵と証明書を使用してください。
7 */
8
9// --- 1. 定数とファイルパスの定義 ---
10$inputFilename = 'document_to_sign.txt';
11$outputFilename = 'signed_document.cms';
12$certificateFilename = 'temp_certificate.pem';
13$privateKeyFilename = 'temp_private_key.pem';
14
15// --- 2. 署名対象のダミーファイルを作成 ---
16// 実際のアプリケーションでは、ここに署名したい元のデータを含むファイルパスを指定します。
17$originalContent = "これはデジタル署名されるコンテンツです。\n重要な情報やドキュメントが含まれることを想定しています。";
18if (file_put_contents($inputFilename, $originalContent) === false) {
19    echo "エラー: 入力ファイル '{$inputFilename}' の作成に失敗しました。\n";
20    exit(1);
21}
22echo "入力ファイル '{$inputFilename}' を作成しました。\n";
23
24// --- 3. デモンストレーション用の秘密鍵と自己署名証明書を生成 ---
25// 本番環境では、既存の信頼された証明書と鍵を使用してください。
26echo "デモンストレーション用の秘密鍵と証明書を生成中...\n";
27
28// 秘密鍵の生成
29$privateKey = openssl_pkey_new([
30    "private_key_bits" => 2048,           // 鍵のビット長
31    "private_key_type" => OPENSSL_KEYTYPE_RSA, // 鍵の種類 (RSA)
32]);
33
34if (!$privateKey) {
35    echo "エラー: 秘密鍵の生成に失敗しました。\n";
36    exit(1);
37}
38
39// 秘密鍵をPEM形式でファイルに保存
40if (!openssl_pkey_export($privateKey, $privateKeyPemContent, null, [])) {
41    echo "エラー: 秘密鍵のエクスポートに失敗しました。\n";
42    exit(1);
43}
44if (file_put_contents($privateKeyFilename, $privateKeyPemContent) === false) {
45    echo "エラー: 秘密鍵ファイル '{$privateKeyFilename}' の書き込みに失敗しました。\n";
46    exit(1);
47}
48echo "秘密鍵 '{$privateKeyFilename}' を生成しました。\n";
49
50// 証明書署名要求 (CSR) の生成
51$dn = [ // 識別名 (Distinguished Name)
52    "countryName" => "JP",
53    "stateOrProvinceName" => "Tokyo",
54    "localityName" => "Shinjuku-ku",
55    "organizationName" => "Example Corp",
56    "organizationalUnitName" => "IT Department",
57    "commonName" => "www.example.com", // ホスト名またはドメイン名
58    "emailAddress" => "webmaster@example.com",
59];
60$csr = openssl_csr_new($dn, $privateKey, ['digest_alg' => 'sha256']);
61
62if (!$csr) {
63    echo "エラー: CSRの生成に失敗しました。\n";
64    exit(1);
65}
66
67// 自己署名証明書の生成 (有効期間365日)
68// 第2引数にnullを渡すことで、生成したCSRを自身で署名します (自己署名)。
69$certificate = openssl_csr_sign($csr, null, $privateKey, 365, ['digest_alg' => 'sha256']);
70
71if (!$certificate) {
72    echo "エラー: 自己署名証明書の生成に失敗しました。\n";
73    exit(1);
74}
75
76// 証明書をPEM形式でファイルに保存
77if (!openssl_x509_export($certificate, $certificatePemContent)) {
78    echo "エラー: 証明書のエクスポートに失敗しました。\n";
79    exit(1);
80}
81if (file_put_contents($certificateFilename, $certificatePemContent) === false) {
82    echo "エラー: 証明書ファイル '{$certificateFilename}' の書き込みに失敗しました。\n";
83    exit(1);
84}
85echo "証明書 '{$certificateFilename}' を生成しました。\n";
86
87// --- 4. openssl_cms_sign 関数でファイルを署名 ---
88echo "入力ファイル '{$inputFilename}' をCMS形式で署名中...\n";
89
90$isSigned = openssl_cms_sign(
91    $inputFilename,        // 署名する入力ファイル
92    $outputFilename,       // 署名結果を保存する出力ファイル
93    $certificateFilename,  // 署名に使用する証明書ファイルのパス
94    $privateKeyFilename,   // 署名に使用する秘密鍵ファイルのパス
95    null,                  // オプションのヘッダー (ここでは使用しない)
96    0,                     // フラグ (0はデフォルトでコンテンツをCMSメッセージに含めます)
97    OPENSSL_CMS_BINARY     // 出力エンコーディングをバイナリ (DER) に設定
98);
99
100// --- 5. 署名結果の確認とメッセージ表示 ---
101if ($isSigned) {
102    echo "成功: '{$inputFilename}' がCMS形式で署名され、'{$outputFilename}' に保存されました。\n";
103    echo "生成された署名ファイルは、openssl_cms_verify 関数などで検証できます。\n";
104} else {
105    echo "エラー: openssl_cms_sign 関数による署名に失敗しました。\n";
106    // 詳細なOpenSSLエラーメッセージを表示
107    while ($msg = openssl_error_string()) {
108        echo "OpenSSL エラー: " . $msg . "\n";
109    }
110}
111
112// --- 6. リソースの解放 (PHPのGCが自動で行いますが、明示的な解放も可能) ---
113if (is_resource($privateKey)) {
114    openssl_pkey_free($privateKey);
115}
116if (is_resource($csr)) {
117    openssl_csr_free($csr);
118}
119if (is_resource($certificate)) {
120    openssl_x509_free($certificate);
121}
122
123// --- 7. 生成された一時ファイルのクリーンアップ (オプション) ---
124// スクリプト実行後に生成されたファイルを削除したい場合は、以下のコメントを外してください。
125/*
126echo "一時ファイルをクリーンアップ中...\n";
127unlink($inputFilename);
128unlink($outputFilename);
129unlink($certificateFilename);
130unlink($privateKeyFilename);
131echo "クリーンアップ完了。\n";
132*/
133
134?>

openssl_cms_sign関数は、指定されたファイルをCMS(Cryptographic Message Syntax)形式でデジタル署名するためのPHP関数です。これにより、メッセージの完全性と送信者の認証を保証できます。

このサンプルコードでは、まず署名対象となるダミーのテキストファイルを作成します。次に、署名に必要な秘密鍵と自己署名証明書をデモンストレーション用に一時的に生成しています。実際のシステムでは、信頼された既存の秘密鍵と証明書を使用することが重要です。

openssl_cms_sign関数は、署名する入力ファイル名、署名結果を保存する出力ファイル名、署名に使用する証明書、そして秘密鍵を主要な引数として受け取ります。追加の引数として、オプションのヘッダーや、署名されたコンテンツをCMSメッセージに含めるかどうかのフラグ、出力エンコーディングなどを指定可能です。このサンプルでは、コンテンツをCMSメッセージに含め、バイナリ形式で出力するように設定しています。

関数が成功するとtrueが返され、署名済みのCMSデータが指定された出力ファイルに書き込まれます。失敗した場合はfalseが返され、エラーが発生したことを示します。生成された署名ファイルは、後からopenssl_cms_verify関数などを使用して検証できます。

本サンプルコードで一時的に生成している秘密鍵と自己署名証明書は、デモンストレーション用途に限定されます。実際のシステム運用では、認証局によって発行された信頼性のある証明書と、厳重に管理された秘密鍵を使用することが不可欠です。秘密鍵の漏洩は深刻なセキュリティインシデントに直結します。また、openssl_cms_sign関数をはじめ、関連するOpenSSL関数は予期せぬエラーで失敗する場合がありますので、必ず戻り値を検証し、openssl_error_string()で詳細なエラーメッセージを確認して適切なエラー処理を実装してください。引数に指定するファイルパスは、実行環境からのアクセス権限と存在を確認し、正確に指定することが重要です。

PHPでopenssl_cms_signによる署名と検証

1<?php
2
3/**
4 * Demonstrates how to sign a file using `openssl_cms_sign` and then verify the signature
5 * and extract the original content using `openssl_cms_verify`.
6 *
7 * This example is suitable for system engineers learning about digital signatures
8 * and PHP's OpenSSL extension, specifically for CMS (Cryptographic Message Syntax).
9 */
10function runCmsSignAndVerifyExample(): void
11{
12    // --- 1. セットアップ: 一時ファイルのパスを定義し、一時ディレクトリを作成します ---
13    $tempDir = __DIR__ . '/temp_cms_example';
14    if (!is_dir($tempDir)) {
15        mkdir($tempDir, 0777, true);
16    }
17
18    $privateKeyPath = $tempDir . '/private_key.pem';
19    $certificatePath = $tempDir . '/certificate.pem';
20    $inputFilePath = $tempDir . '/input.txt';
21    $signedFilePath = $tempDir . '/signed_output.cms'; // CMS形式の署名済みファイル
22    $verifiedOutputPath = $tempDir . '/verified_output.txt'; // 検証後に元のデータが抽出されるファイル
23
24    echo "--- CMS署名と検証のサンプルを開始します ---" . PHP_EOL;
25
26    // --- 2. 秘密鍵と自己署名証明書を生成します ---
27    echo "秘密鍵と自己署名証明書を生成しています..." . PHP_EOL;
28    // 2048ビットのRSA秘密鍵を生成
29    $privateKey = openssl_pkey_new([
30        "private_key_bits" => 2048,
31        "private_key_type" => OPENSSL_KEYTYPE_RSA,
32    ]);
33
34    if (!$privateKey) {
35        echo "秘密鍵の生成エラー: " . openssl_error_string() . PHP_EOL;
36        cleanup($tempDir);
37        return;
38    }
39
40    // 証明書署名要求 (CSR) の詳細を設定
41    $csrConfig = [
42        "countryName" => "JP",
43        "stateOrProvinceName" => "Tokyo",
44        "localityName" => "Shibuya",
45        "organizationName" => "Example Corp",
46        "organizationalUnitName" => "IT Dept",
47        "commonName" => "example.com",
48        "emailAddress" => "admin@example.com",
49    ];
50
51    // CSRを生成
52    $csr = openssl_csr_new($csrConfig, $privateKey);
53
54    if (!$csr) {
55        echo "CSRの生成エラー: " . openssl_error_string() . PHP_EOL;
56        cleanup($tempDir);
57        return;
58    }
59
60    // CSRと秘密鍵を使用して自己署名証明書を生成 (有効期限365日)
61    $certificate = openssl_csr_sign($csr, null, $privateKey, 365);
62
63    if (!$certificate) {
64        echo "自己署名証明書の生成エラー: " . openssl_error_string() . PHP_EOL;
65        cleanup($tempDir);
66        return;
67    }
68
69    // 秘密鍵と証明書をPEM形式でファイルにエクスポート
70    if (!openssl_pkey_export($privateKey, $privateKeyContent)) {
71        echo "秘密鍵のエクスポートエラー: " . openssl_error_string() . PHP_EOL;
72        cleanup($tempDir);
73        return;
74    }
75    file_put_contents($privateKeyPath, $privateKeyContent);
76
77    if (!openssl_x509_export($certificate, $certificateContent)) {
78        echo "証明書のエクスポートエラー: " . openssl_error_string() . PHP_EOL;
79        cleanup($tempDir);
80        return;
81    }
82    file_put_contents($certificatePath, $certificateContent);
83
84    echo "秘密鍵と証明書が正常に生成されました。" . PHP_EOL;
85
86    // --- 3. 署名対象の入力データを作成します ---
87    $inputData = "このメッセージはCMS形式で署名されます。";
88    file_put_contents($inputFilePath, $inputData);
89    echo "入力ファイル '{$inputFilePath}' が作成されました。" . PHP_EOL;
90
91    // --- 4. `openssl_cms_sign` を使用して入力ファイルを署名します ---
92    echo "入力ファイル '{$inputFilePath}' を `openssl_cms_sign` で署名しています..." . PHP_EOL;
93    $signSuccess = openssl_cms_sign(
94        $inputFilePath,            // 署名する入力ファイルのパス
95        $signedFilePath,           // 署名済みコンテンツを保存する出力ファイルのパス
96        $certificate,              // 署名に使用する証明書リソースまたはパス
97        $privateKey,               // 署名に使用する秘密鍵リソースまたはパス
98        null,                      // (オプション) 追加のヘッダー配列
99        0,                         // (オプション) フラグ (デフォルト値)
100        OPENSSL_CMS_BINARY         // (オプション) エンコーディング (バイナリ形式)
101        // null                      // (オプション) untrusted_certificates_filename
102    );
103
104    if (!$signSuccess) {
105        echo "ファイルの署名エラー: " . openssl_error_string() . PHP_EOL;
106        cleanup($tempDir);
107        return;
108    }
109    echo "ファイルは正常に署名され、'{$signedFilePath}' に保存されました。" . PHP_EOL;
110
111    // --- 5. `openssl_cms_verify` を使用して署名済みデータを検証します ---
112    echo "署名済みファイル '{$signedFilePath}' を `openssl_cms_verify` で検証しています..." . PHP_EOL;
113
114    // `openssl_cms_verify` は、署名されたファイルを検証し、元のコンテンツを抽出します。
115    // 第3引数には、署名者の証明書ファイルまたはCA証明書のバンドルを指定します。
116    // 今回は自己署名証明書なので、生成した証明書自体を指定します。
117    $verifySuccess = openssl_cms_verify(
118        $signedFilePath,            // 検証する署名済み入力ファイルのパス
119        null,                       // (オプション) trust_chain_filename (通常はCA証明書チェーン)
120        $certificatePath,           // 署名者の証明書ファイル (PEM形式)
121        [],                         // (オプション) ヘッダーチェック用
122        $verifiedOutputPath,        // 検証された元のコンテンツを保存するファイルのパス
123        OPENSSL_CMS_BINARY | OPENSSL_CMS_NOINTERN | OPENSSL_CMS_NOCRL // 検証フラグ
124    );
125
126    if ($verifySuccess) {
127        echo "検証成功!" . PHP_EOL;
128        $extractedData = file_get_contents($verifiedOutputPath);
129        echo "抽出されたデータ: '{$extractedData}'" . PHP_EOL;
130        if ($extractedData === $inputData) {
131            echo "抽出されたデータは元の入力と一致します。" . PHP_EOL;
132        } else {
133            echo "警告: 抽出されたデータが元の入力と一致しません!" . PHP_EOL;
134        }
135    } else {
136        echo "検証失敗: " . openssl_error_string() . PHP_EOL;
137    }
138
139    // --- 6. クリーンアップ: 一時ファイルを削除します ---
140    echo "--- 一時ファイルをクリーンアップしています ---" . PHP_EOL;
141    cleanup($tempDir);
142    echo "--- サンプルが終了しました ---" . PHP_EOL;
143}
144
145/**
146 * 指定されたディレクトリ内の一時ファイルとディレクトリを削除します。
147 *
148 * @param string $directoryPath クリーンアップする一時ディレクトリのパス。
149 */
150function cleanup(string $directoryPath): void
151{
152    if (is_dir($directoryPath)) {
153        $files = glob($directoryPath . '/*');
154        foreach ($files as $file) {
155            if (is_file($file)) {
156                unlink($file);
157            }
158        }
159        rmdir($directoryPath);
160    }
161}
162
163// サンプルコードを実行
164runCmsSignAndVerifyExample();

PHPのopenssl_cms_sign関数は、指定された入力ファイルの内容をデジタル署名し、その署名済みデータをCMS(Cryptographic Message Syntax)形式で出力する機能を提供します。これにより、データの完全性や改ざん検知、送信者の認証などを実現できます。

この関数は、署名したい元データのファイルパスを$input_filename、署名済みデータを出力するファイルパスを$output_filenameとして受け取ります。さらに、署名に使用する証明書を$certificate、秘密鍵を$private_keyとして指定します。これらの引数はファイルパスまたはOpenSSLリソースとして渡すことが可能です。その他の引数には、オプションのヘッダーやエンコーディング形式などを設定できます。処理が成功した場合はtrueを、失敗した場合はfalseを返します。

サンプルコードでは、まず秘密鍵と自己署名証明書を生成し、署名対象となる入力ファイルを準備しています。次に、openssl_cms_signを用いて入力ファイルを署名し、署名済みファイルを生成しています。その後、関連するopenssl_cms_verify関数で署名済みデータを検証し、元のデータが正しく抽出されることを確認する一連の処理が示されており、デジタル署名の作成から検証までの流れを具体的に理解することができます。

この関数はファイルのデジタル署名に利用され、秘密鍵と証明書が正確に指定されているか確認が不可欠です。特に秘密鍵は厳重に管理し、絶対に公開しないでください。サンプルでは自己署名証明書を使用していますが、実際のシステムでは認証局が発行した信頼できる証明書を利用し、その信頼チェーンも適切に設定する必要があります。関数実行後は必ず戻り値をチェックし、エラー発生時はopenssl_error_string()で原因を特定することが重要です。署名検証には、署名に使用された証明書またはその信頼チェーンが正しく提供されないと成功しませんので注意が必要です。ファイルパスを引数に指定する際は、ファイルアクセス権限やパスの検証を怠らないようにしてください。

関連コンテンツ

関連IT用語

関連プログラミング言語