電子署名(公開鍵暗号)

 今回はPHP環境で「公開鍵暗号」を扱う際の実作業に触れます。

これらの実作業に言及する場合、多くの記事がコマンドプロンプト上での処理を例示しているのですが、実際にはPHPスクリプトで自動的に処理させるケースの方が多いと考えられ、このブログではPHPスクリプトとして書く場合の記述を例示します。なお、公開鍵暗号関連の処理はPHPに実装されており利用の継続性が担保されているOpenSSLを用いて行います。

1. 暗号鍵ペア

よく勘違いされているのですが、「秘密鍵」と「公開鍵」のふたつしか存在しないと思われているのですが、これらは暗号鍵ペア本体から抽出された複製生物であって、これらを生成できる大元の暗号鍵ペアが配列の状態で存在します。その大元に格納された暗号化アルゴリズムの要素(数値)を使ってこれらふたつが生成されている状態です。

上記をサーバー上で実行するとブラウザの画面には下記が表示されます。上段の「—–BEGIN PRIVATE KEY—–」で始まる部分が秘密鍵、下段の「—–BEGIN PUBLIC KEY—–」で始まる部分が公開鍵です。

秘密鍵は他人に知られるとその人も暗号を復号できるようになってしまうので人には知られないようにします。可能であればユーザーインターフェイスにも秘密鍵が描画されないようにすべきです(サーバーとブラウザの間の通信を傍受された場合に知られてしまうので)。

新規にkeyをopenssl_pkey_new関数で生成しopenssl_pkey_export関数でメモリー上に変数として秘密鍵が生成されるわけですが、秘密鍵の扱い方としては、openssl_pkey_export_to_file関数を使ってASCII 変換された (PEM エンコードされた)状態の文字列としてサーバーのローカルに保存し、後日に必要な場合はopenssl_pkey_get_private関数を使ってそのファイルから読み込みます。秘密鍵に「パスフレーズ(秘密鍵として利用する際にかけるパスワード)」がかかっている場合はこのopenssl_pkey_get_private関数を使ってファイルから読み込む必要がありますが、パスフレーズがかかっていない秘密鍵であればfile_get_contents関数で読み込んでも利用できます。

2. 秘密鍵と公開鍵はどんな関係か

opensslの関数でopenssl_pkey_get_details関数を使ってキーペアの詳細を表示させることができますが、公開鍵はそのまま出力されるのに対して秘密鍵はそのままの値で表示されません。下記のとおり大元のキーペアの詳細を配列としてダンプすると、公開鍵は保存されていますが秘密鍵は見当たりません。なお、一部の文字列が文字化けしているように見えますが、これはこれらの値がバイナリとして格納されているためで、人間が読むときはbase64_encodeしてASCII文字列にします。

出力は下記。

これは、前回の記事でも少し言及しましたが、RSA暗号を構成する要素が別々に格納されている状態で、秘密鍵(正確にいうと秘密鍵の核となる数値)はdです。dはこれらの$keydetail[‘rsa’][‘e’]と$keydetail[‘rsa’][‘p’]と$keydetail[‘rsa’][‘q’]を使って e^(-1) mod (p-1)x(q-1) となる正の整数として計算できます。

実際に秘密鍵と公開鍵にこれらの値が含まれていることを見てみましょう。n (= pとqの積), e, d, p, qの値はこのままでは読めないのでbase64_encodeします。

n(= pとqの積)の値 $keydetail[‘rsa’][‘n’]:

u12cLtGphx04sRkUx4RqI/7YHylTR+XJFjKM5ZWKaymOyTSB6+8L4nC7Eik
22xtJcxvX6imbJs4UiGpWs4efjGWQShfJxZLUvuKJsp90e4B1EmVs9jKm77
9gWBDHU52+oeU45Q+kgwhvFuKGw/a3vVWdUfDsuzdtQnd8kyGBwX5tBCN4A
PEWoHiJWzxw1u38cL2Dn1cxkzKpCapTUedb3pXWqfroszv2vQ/mTDs4yiAw
08q2sH1NfD6x4oYtNHtA3r22P0RQp9hvQMxGHYu/Jm+Mh25n8I9DzJvxsLi
VGHJQWI9MJE2EsdEb1/MqW7CcE8xNp07gHFBUU/Mjq7uDXw==

eの値 $keydetail[‘rsa’][‘e’]:

AQAB

dの値 $keydetail[‘rsa’][‘d’]:

qDGVNZYyl77MmHYKaDxex6dKvVZskzscc1e3kZTXJ/GrYsVG59O0GHbYB6q
FutTY74MMJQFqbMBMQFUtNYZG3FKD329RSpHPdveHYpiviPo8neH8mVnI76
kdRJDzTgVqDozlsQKEzLeyeZmqHpoIQOdtDRS4fit4YlU+uBk5EMbwB7/ll
J1WxU7wHGPZLKQaO0+7VeclTo5kJ3Ig+BmjQT12OarSV9kB/CcdiQh3HfiE
Szqb/rd0Vt9+OZaMjOp5nHK9JB06+Ljxdt8+ZyPAtdZEYXwDkHB/u/h3A0A
ZaAmPsvofcvSMZ7iAPNIuSyG35Os3pVSVRbzRZ6h82nooyQ==

pの値 $keydetail[‘rsa’][‘p’]: 

9mO2X2QYqeidGtSLff/rxLhjF9bKW7A5wa0Ps8R4go3CkQil19ZFV4IbFiz
1TWeMInp96ZQ8U8e6xGQVi6Ht3Byui9BQWLAjWPBDcBhyiJhYBsvjMOVO9X
cd+WijR6xG/nf1EuJK/0Ur/CDKzGlME/uAyT+QpllwBBY3MmbQZ1s=

qの値 $keydetail[‘rsa’][‘q’]: 

wqyGA/RB9E1X/Fn3mt1SR849u8TZbKXtWPqOUwJvufE74hPFUxAqUoAimsg
Om4nY7xfu2AJjCLCqQB+JL43HVuqawNZk3LkwxH2jdzaYdFWWo+k0yDF2As
PBwtjXop1U/qqoh4Sux8Yq8bKPjEnUpGcT1EuhuJUwot1GrWvt100=

これらn, e, d, p, qが秘密鍵と公開鍵のどこに含まれているか見てみましょう。

秘密鍵:

—–BEGIN PRIVATE KEY—–
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7XZwu0am
HHTixGRTHhGoj/tgfKVNH5ckWMozllYprKY7JNIHr7wvicLsSKTbbG0lzG9
fqKZsmzhSIalazh5+MZZBKF8nFktS+4omyn3R7gHUSZWz2Mqbvv2BYEMdTn
b6h5TjlD6SDCG8W4obD9re9VZ1R8Oy7N21Cd3yTIYHBfm0EI3gA8RageIlb
PHDW7fxwvYOfVzGTMqkJqlNR51veldap+uizO/a9D+ZMOzjKIDDTyrawfU1
8PrHihi00e0DevbY/RFCn2G9AzEYdi78mb4yHbmfwj0PMm/GwuJUYclBYj0
wkTYSx0RvX8ypbsJwTzE2nTuAcUFRT8yOru4NfAgMBAAECggEBAKgxlTWWM
pe+zJh2Cmg8XsenSr1WbJM7HHNXt5GU1yfxq2LFRufTtBh22AeqhbrU2O+D
DCUBamzATEBVLTWGRtxSg99vUUqRz3b3h2KYr4j6PJ3h/JlZyO+pHUSQ804
Fag6M5bEChMy3snmZqh6aCEDnbQ0UuH4reGJVPrgZORDG8Ae/5ZSdVsVO8B
xj2SykGjtPu1XnJU6OZCdyIPgZo0E9djmq0lfZAfwnHYkIdx34hEs6m/63d
FbffjmWjIzqeZxyvSQdOvi48XbfPmcjwLXWRGF8A5Bwf7v4dwNAGWgJj7L6
H3L0jGe4gDzSLksht+TrN6VUlUW80WeofNp6KMkCgYEA
// ここからpの値が入っている
9mO2X2QYqeidGtS
Lff/rxLhjF9bKW7A5wa0Ps8R4go3CkQil19ZFV4IbFiz1TWeMInp96ZQ8U8
e6xGQVi6Ht3Byui9BQWLAjWPBDcBhyiJhYBsvjMOVO9Xcd+WijR6xG/nf1E
uJK/0Ur/CDKzGlME/uAyT+QpllwBBY3MmbQZ1s
// ここまでpの値が入っている
CgYEA
// ここからqの値が入っている
wqyGA/RB
9E1X/Fn3mt1SR849u8TZbKXtWPqOUwJvufE74hPFUxAqUoAimsgOm4nY7xf
u2AJjCLCqQB+JL43HVuqawNZk3LkwxH2jdzaYdFWWo+k0yDF2AsPBwtjXop
1U/qqoh4Sux8Yq8bKPjEnUpGcT1EuhuJUwot1GrWvt100
// ここまでqの値が入っている
CgYEA
8cbLETQyF5jRgaCL+WqGuxWM97mn/O0i3/Co9eiXsNTW1mc8Bl6W0NY1Kj+
XvwsbMkItCGaUBEp79XeYxNpzhIkIpt4V2dGN/hpWssrqqSuFRbYRDMwX/T
uUSEfr4csmJ1MjRBKXMYN5NAIhvxCcvQcRS2p60SH2j6wVIfpOCv0CgYANo
N5Q8IydebyrIfE/mjbkZ//SYVmHTaZsED5eeVExBz5V+WRbCEquM+I4S8Ea
CnBxbI9/98rPFO8WRh7ogVhvMUVcMqYk/aH4X4NOGThbDJLbLrxPN4c3clT
iOyP9cl485d2efx9tROZEvWdnPtPwHnc46YKM1YTuIeeMxH2GEQKBgFgbSb
BUSUZrYMYG0Q1ny+70qB/N20hzJHe0a+qcNMayvkHSW0DSku4lZ+pr8sZF8
RyCWmNgT+NswOV1KBsTAP7I4/sUfZBeTDdq9W+nQxhOdyJczkDdy90dYJ3Q
tF36aq388Cf92STXT6tzgHZSjUK0xGRLEcihn/MfiHON+1eu
—–END PRIVATE KEY—–

公開鍵:

—–BEGIN PUBLIC KEY—–
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
// ここからnの値が入っている
u12cLtGphx04sRkUx4RqI/7YHylTR+XJFjKM5ZWKaymOyTSB6+8L4nC7Eik
22xtJcxvX6imbJs4UiGpWs4efjGWQShfJxZLUvuKJsp90e4B1EmVs9jKm77
9gWBDHU52+oeU45Q+kgwhvFuKGw/a3vVWdUfDsuzdtQnd8kyGBwX5tBCN4A
PEWoHiJWzxw1u38cL2Dn1cxkzKpCapTUedb3pXWqfroszv2vQ/mTDs4yiAw
08q2sH1NfD6x4oYtNHtA3r22P0RQp9hvQMxGHYu/Jm+Mh25n8I9DzJvxsLi
VGHJQWI9MJE2EsdEb1/MqW7CcE8xNp07gHFBUU/Mjq7uDXw
// ここまでnの値が入っている
ID
// ここからeの値が入っている
AQAB
// ここまでeの値が入っている
—–END PUBLIC KEY—–

というように秘密鍵にはpとqの両方が、公開鍵にはpとqの積であるnが入っています。eは鍵のアルゴリズムによって固定値なので、秘密鍵の方が計算上有利な情報が入っていることがわかります。なお、秘密鍵に肝心のdが含まれていないじゃ無いかと思うかもしれませんが、他の値のようにbase64_encode/decodeしただけではわからない状態で入っています。このように、秘密鍵と公開鍵が「キーペア」と呼ばれ相互に密接な関係を持っているとはいえ、秘密鍵には非常に多くの大事な情報が含まれています。秘密鍵がこれらの大事な情報を個別要素として持っているせいで秘密鍵があれば公開鍵を生成できますが、公開鍵があっても秘密鍵は生成できません。

3. 暗号と復号をしてみる

上記で「多くのケースでは公開鍵を使って不特定多数に暗号にしてもらい秘密鍵を使って復号するが、秘密鍵を使って暗号にし公開鍵を使って復号することもできる」と述べました。対象とする平分を「This is a pen.」として実際にやってみましょう。

下記のケースでは「公開鍵で暗号にし、秘密鍵で復号する」と「秘密鍵で暗号にし、公開鍵で復号する」の両方を行っています。

なお、openSSLには「openssl_encrypt」「openssl_decrypt」という上記と類似した関数がありますが、これらは「共通鍵暗号」用なので間違えないようにしてください。


次回はCSR、要約関数に言及します。

0
Would love your thoughts, please comment.x
()
x