Crypto - Data - Data Security - Digital Signature - PHP - XML

電子署名(生成手順)

 今回は実際に電子署名を生成して相手に伝える手順に言及します。

1. おおまかな手順

手順1: メッセージの受信者が送信者の公開鍵を持っている状態にする

    署名が「秘密鍵を使って生成され、公開鍵を使って検証する」という性質上、予め受信者の手元に秘密鍵とペアをなす公開鍵が届いている必要があります。私は実際の運用上で「公開鍵をUSBドライブに入れて持ってきてくれ」と言われたことがあるのですが、そこまで神経質になる必要はないと持っています。というのは、自分の手元にある秘密鍵とペアになるはずの公開鍵を他人が偽造したとしてもすぐに偽物であることが露見しますし、だいいち偽造した公開鍵では何もできませんから。予め普通にメールでもいいので送ればいいと思います。

手順2: メッセージに対する署名を生成する

    OpenSSLのopenssl_sign関数を使ってメッセージに対する署名を生成します。この際には手元の秘密鍵を使い、アルゴリズムを指定します。受信者とはアルゴリズムを一致させるよう合意しておく必要があります。

手順3: メッセージの送信プロトコルに沿ったデータ形式にする

    プレーンテキストであれば楽なのですが、後述しますが、メールの場合は「S/MIME」に沿ったデータ形式で、XMLの場合はXMLに込める形で署名を送る必要があります。

手順4: メッセージと署名を受信者へ送る

    相手と合意した方式で署名を含むデータを送ります。

手順5: 受信者はメッセージと署名を比較して改竄の有無を確認する

    受信者はOpenSSLのopenssl_verify関数を使ってメッセージに改竄がないかを検証します。

2. 実際のケース

 電子署名の生成に関して例としてテキストの一文に対して電子署名を生成することはありますが、実際の利用ケースとしてテキスト平文に直接に電子署名を生成するケースはあまりありません。というのは、電子署名に含まれるダイジェスト値は1文字増減しただけで全く異なる値になり、平文の前後にスペースなどが入っただけでダイジェスト値が変わってしまうためです。「どこからどこまでをダイジェスト生成の対象にしたよ」という境界線が曖昧な状態で電子署名を生成すると混乱を招きます。

 ですので、あまり言及されないことですが、テキストデータに電子署名をつける場合、「署名対象とするテキストデータ」「電子署名」をそれぞれ独立したファイルとして存在させ、相手にはこれらをファイルとして渡すことが望ましい状況にあります。下記に実例を記載します。

 公開鍵暗号の秘密鍵は「PrivateKey.key」というファイルに、受信者に予め渡しておく公開鍵は「PublicKey.key」というファイルに入れておき、電子署名の対象とするテキストデータは「message.txt」というファイルにして下記のメッセージを記載したとします。

This is a message to be signed digitally.
このメッセージが電子署名されるメッセージです。

 上記のmessage.txtに書いてある文字列を読み込んで、送信者の立場でそれに対して電子署名を生成してみましょう。

<?php

// 公開鍵暗号の秘密鍵を用意します
$priv_key_id = file_get_contents("./PrivateKey.key");

// メッセージを読み込みます
$data = file_get_contents("./message.txt");

// 署名を計算します(アルゴリズムはSHA1とします)
openssl_sign($data, $signature, $priv_key_id, OPENSSL_ALGO_SHA1);

echo base64_encode($signature);

// メモリからキーを開放する
openssl_free_key($priv_key_id);

?>

 上記の結果、下記の署名が生成されます。署名というと、送信者の名前があって日付があってというイメージがありますが、このごちゃごちゃした文字列が電子署名です。

 なお、上記では署名対象メッセージのダイジェストを要約関数で求める作業をしていませんが、openssl/sign関数の内部では要約関数でダイジェスト値を求める処理が行われています。

EQNtQZhOEjv3OF49mGHYB8BXu4uZQ85QAQsiGVJsHRs9W0oQQAZPCbx5VKquPfeHtSlRCvjFlVWEL0UmYmrLkG2W4nqNMFd4mTCi4LyYBu1nuMyw3ewVSgi8XQUsfVCOnNOmS5xVGXS35T9SXC2M0rMk82Gb0Nz1gcbsk+ukZfZYS1Z17HQeBwVaWJ/UzkydXQOJur4vYi2wBuGJtPQkyB/FnOVzsgOywyvoa/OVj3IFHhL1MbKmIxc1D+M894feuq21iQBRRQTn0dKXVgeJ7lclVHD4JupO+tZUCTXCPKsRtC2WVULhjA9+j2x9REjo5LfTxtKsNPpUAN3MVhW4qw==

次にメッセージの受信者の立場になって、受信したメッセージと署名を比較して改竄の有無を確認します。

<?php

// 送られてきたメッセージを読み込みます
$data = file_get_contents("./message.txt");

// メッセージに添付された署名(base64エンコードされている状態です)
$signature = "EQNtQZhOEjv3OF49mGHYB8BXu4uZQ85QAQsiGVJsHRs9W0oQQAZPCbx5VKquPfeHtSlRCvjFlVWEL0UmYmrLkG2W4nqNMFd4mTCi4LyYBu1nuMyw3ewVSgi8XQUsfVCOnNOmS5xVGXS35T9SXC2M0rMk82Gb0Nz1gcbsk+ukZfZYS1Z17HQeBwVaWJ/UzkydXQOJur4vYi2wBuGJtPQkyB/FnOVzsgOywyvoa/OVj3IFHhL1MbKmIxc1D+M894feuq21iQBRRQTn0dKXVgeJ7lclVHD4JupO+tZUCTXCPKsRtC2WVULhjA9+j2x9REjo5LfTxtKsNPpUAN3MVhW4qw==";

// 署名を生成した際の秘密鍵とペアになっている公開鍵を用意します(予め送信者から渡されている前提)
$_PublicKey = file_get_contents("./PublicKey.key");

// 署名を検証($rが1なら改竄はないものと判定)
$r = openssl_verify($data, $signature, $_PublicKey, OPENSSL_ALGO_SHA1);
var_dump($r);
echo $r;

// メモリからキーを開放する
openssl_free_key($priv_key_id);

?>

 この結果、$rは1でしたので改竄はないものと判定されます。


 上記が基本的な電子署名の生成手順です。公開鍵暗号やダイジェスト(ハッシュ)など複数の要素が含まれるせいで難しそうに感じますが、それほど難しくはありません。

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments