2010年02月24日

PHPで簡単暗号化

中学の頃、エドガー・アラン・ポーの「黄金虫」を読んで以来、暗号(とその解読)にすっかり魅せられたYT@ゼロファクトリーです。

まーこの「黄金虫」という小説は、頭の良いおっさんが、キャプテン・キッドの財宝を記した暗号が書かれた羊皮紙を偶然見つけて、暗号を解読し、お宝を発見するストーリーです。
肝心の暗号解読の部分は、「おやおや、君は暗号の話しですっかり退屈してしまったようだねえ」とそれ以降の謎解きを省略してしまうので、実につまらない。
ぼくは頑張って省略された部分もちゃんと解読してみました。懐かしい。

さて。
暗号というのは、この情報社会ではなくてはならないものになりました。
無線通信のWEPもそうだし、ターミナルでサーバに命令するときのSSHもそう、ショッピングなんかするときのSSLもそうですね。

銀行その他で、パスワードをデータベースに含める場合も、平文のまま保存することはまずなく、単一方向に暗号化して保存します。
照会するときは、ユーザーの入力を同様にして単一方向で暗号化し、すでに暗号化されたパスワードと見比べる、というのは最低限、どこでもやってます。
だから管理者もユーザーのパスワードがわからないようになってますね。

しかし。
この単一方向暗号化は、元の平文が割れにくいという点ではいいんですが、やはり複号して平文をゲットしたい局面も多々あるものです。
もちろん複号できる暗号化のルーチンもたくさんある(3DESとかPGPとか、複数鍵方式のRSAとか)のですが、そこまでおおげさなことせんでも、ちょっとわかりにくくしたい、というだけなら、太古の昔より洗練されてきた暗号法をちょっぴり応用するだけで、どんな処理系でもできます。
PHPの場合だと、PEAR::BlowfishとかMcryptとか別途モジュールを導入しないといけないので、めんどくさいですしね。

というわけで。
今回はPHPで文字列を簡単暗号化してみようという、たまには技術的な内容でお送りする企画です。

ポイントとしては、mb_convert_encoding()とか、ord()とか、chr() とかどんな処理系にもあるような一般的な組み込み関数のみ利用してます。汎用性は高い。

平文:

君、時というものは、それぞれの人間によって、それぞれの速さで走るものなのだよ。

・・・というシェイクスピアの「お気に召すまま」のセリフを暗号化します。

まずこれを mb_convert_encoding() でEUC-JPに変換→base64encode()でエンコードします。
なんでEUCにするのかというと、UTF-8のままだと以前うまく動かなかったんですよねぇ・・・?
base64エンコードは、PHPはデフォで使えますが、使えない処理系でも、RFC-2045だかなんだかで定義されてるので探せばライブラリが必ずあります。

でまぁ、これをエンコードした結果はこれ。(1)
長いので改行してますが、1行です。

t6+horv+pMikpKSmpOKkzqTPoaKkvaTspL6k7KTOv8201qTLpO
ikw6TGoaKkvaTspL6k7KTOwq6ktaTHwfak66TipM6kyqTOpMCk
6KGj

次にこれを1文字づつASCIIコードに変換して3桁ゼロスタンプします。
PHPの場合は一文字づつ ord() してください。

(2)

11605404310411111411804311207710510711207508310911
20790751071221130840801110970751071180970841151120
76054107055075084079118056050048049113084076112079
10510711905408407111109707510711809708411511207605
41070550750840791191130541071160970840721191020971
07054054084105112077054107121113084079112077067107
054075071106

次に暗号化の鍵を決めます。適当な文字列で。

暗号鍵:

zerofactory

これも同様に base64encode() →(1)と同じ長さになるように繰り返して連結→1バイトづつ ord() します。

(3)

10110908612109805009010408905108211809911010706110
11090861210980500901040890510821180991101070611011
09086121098050090104089051082118099110107061101109
08612109805009010408905108211809911010706110110908
61210980500901040890510821180991101070611011090861
21098050090104089051082118099110107061101109086121
098050090104

次に(2)の数字と(3)の数字を一桁づつ足していきます。
その際に10を超える場合は、10引いてください。
要するに、2+3は5のままでいいですが、7+8は15でなく5と計算してください。
そうすると桁数は(2)または(3)と全く同じ別の数字が出てきます。

(4)

21715302922510916410814719102818721510118518016021
31780512281101630741841900480572151071071811762131
75030228043025074173197007032156038223181037213178
18122810700407417519004805721510710718117621317503
02280430250741731981640362151051071810332102010732
28042004074209191028036215110223181030213176043228
042025061200

次にこれを今度は3桁づつ取り出し、chr()を使って、ASCIIコードから通常の文字列に変換。
なんか変なバイナリデータになりますが、だいじょーぶ!(PHPの場合は)
これを再びbase64encode()します。

暗号文:

2Zkd4W2kbJO/HLvXZbm0oNWyM+Ruo0q4vjA512trtbDVrx7kKx
lKrcUHIJwm37Ul1bK15GsESq++MDnXa2u1sNWvHuQrGUqtxqQk
12lrtSHSyUnkKgRK0b8cJNdu37Ue1bAr5CoZPcg=

はい、これで暗号化ができました!

複号する時はこれを逆に辿るだけで良いので簡単です。
まず、暗号文を base64decode() →1バイトづつ ord() します。

(5)

21715302922510916410814719102818721510118518016021
31780512281101630741841900480572151071071811762131
75030228043025074173197007032156038223181037213178
18122810700407417519004805721510710718117621317503
02280430250741731981640362151051071810332102010732
28042004074209191028036215110223181030213176043228
042025061200

これが、(4)と同じものにならないときはどっか間違えてます。デバッグ、デバッグぅ。(笑)
次に暗号鍵から、長さを揃えて、(3)と同じ数字をもっかい作ってください。

(3)再掲

10110908612109805009010408905108211809911010706110
11090861210980500901040890510821180991101070611011
09086121098050090104089051082118099110107061101109
08612109805009010408905108211809911010706110110908
61210980500901040890510821180991101070611011090861
21098050090104089051082118099110107061101109086121
098050090104

次に!
再びこの(5)と(3)を1桁づつ計算するわけですが、今度は足すのではなく引いてください
当然さっきとは逆で、マイナスになる場合は10足してください。

(6)

11605404310411111411804311207710510711207508310911
20790751071221130840801110970751071180970841151120
76054107055075084079118056050048049113084076112079
10510711905408407111109707510711809708411511207605
41070550750840791191130541071160970840721191020971
07054054084105112077054107121113084079112077067107
054075071106

これが(2)と同じになってない場合は、やはりどっか間違えてます。デバッグ、デバ(ry

次にこれを3桁づつ chr()base64decode()mb_convert_encoding() でEUC-JPから元の文字コードに変換します。

(7)

君、時というものは、それぞれの人間によって、それぞれの速さで走るものなのだよ。

元の平文がゲットできましたかー?
できた方、おめでとうございます!
後はこれを基本として、間に複雑な文字列変換処理を入れれば、その暗号ルーチンはどんどん強化されていきます。
できてない方はデバッグ、デバ(ry

最後に、こういう簡易暗号化の良い点と悪い点についても述べておきます。

良い点:
* 安全度の高い3DESとか使うよりは処理が速い。(・∀・)
* PHP以外の処理系にも十分応用が効く。(・∀・)
* ショボイとは言え独自ルーチンなためパスワード総当りアタックにも強い。(・∀・)
* お望みとあらば画像だってなんだって、base64エンコードできるものならなんでも暗号化できます。(・∀・)

悪い点:
* なにしろショボイのですぐ解読される恐れも十分ある。('A`)
そもそも、最後の暗号文を見ればbase64エンコードしてるのがバレバレである点。(´;ω;`)

従って使いどころとしては、万が一のデータ流出の際、被害拡大を防ぐため、個人情報を一部簡易暗号化するとか。
クッキーに生データ突っ込みたくない場合とか。

・・・そんなときにくらいしかないですけどねー。(笑)
もうだいぶん長くなったので、実際のコードまで載せないですけど、ご要望があれば次のエントリーにでも。
ではでは。

Posted by YT@ゼロファクトリー : 2/24 | コメント (0)

コメントする





スタッフブログについて

ホームページ制作会社
福岡のゼロファクトリー

で働くwebデザイナー・プログラマーと、そのによるスタッフブログです。

カテゴリー

アーカイブ

最近のエントリー

最近のコメント

リンク集

スタッフ作品集

求人採用情報

検索フォーム