====== PHPでお手軽に暗号化、復号する - CodeBook.php ======
===== 注意 =====
**CodeBook.phpが内部的に使用しているphp-mcryptは、[[http://0-oo.net/php-manual/intro.mcrypt.html|PHP 7.1.0 で 非推奨 となり、 PHP 7.2.0 で削除]]されました。**
----
コメントと更新履歴は[[http://0-oo.net/log/category/php-tool-box/code-book/|Code Book Archive - ゼロと無限の間のログ]]へどうぞ。
{{php-tool-box:pencil.png|}}
PHPはphp-mcryptさえあればいろんな暗号アルゴリズムを使えるけど、いざやろうとすると意外に面倒。特に、Javaなど他のアプリやツールとやり取りするときとか。\\
IVやパディングについて考えるのは大したことではないと言えばそれまでだが、毎回チョコチョコ書くのはちょっと手間。パディングがPKCS#5の場合なんか特に。
なので手軽に暗号化と復号ができるクラスを作った。(要php-mcrypt。)
クラス名はサイモン・シンの力作、[[http://www.amazon.co.jp/gp/product/410215972X?ie=UTF8&tag=wiki03-22&linkCode=as2&camp=247&creative=7399&creativeASIN=410215972X|暗号解読]]にちなんで。;-)\\
この本によれば、PHPによる暗号化はCODEではなくCIPHERだそうだけれど。
===== ライセンス =====
[[http://0-oo.net/pryn/MIT_license.txt|MITライセンス]]で。\\
===== 使い方の例 =====
encrypt($key, $text); //暗号化
$decrypted = $codeBook->decrypt($key, $encrypted, $iv); //復号
// => "秘密のメッセージ => (略) => 秘密のメッセージ"
echo $text . ' => ' . $encrypted . ' => ' . $decrypted;
echo '
';
echo var_dump($text === $decrypted); // => bool(true)
echo '
';
/* RIJNDAEL256、ECB、Null文字でPAD */
$codeBook = new CodeBook(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB, CodeBook::PAD_NULL);
list($encrypted) = $codeBook->encrypt($key, $text); //暗号化(ECBはIV不要)
$decrypted = $codeBook->decrypt($key, $encrypted); //復号
// => "秘密のメッセージ => (略) => 秘密のメッセージ"
echo $text . ' => ' . $encrypted . ' => ' . $decrypted;
echo '
';
echo var_dump($text === $decrypted); // => bool(true)
echo '
';
/* Blowfish、CBC、スペースでPAD */
$codeBook = new CodeBook(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC, CodeBook::PAD_SPACE);
list($encrypted, $iv) = $codeBook->encrypt($key, $text); //暗号化
$decrypted = $codeBook->decrypt($key, $encrypted, $iv); //復号
// => "秘密のメッセージ => (略) => 秘密のメッセージ"
echo $text . ' => ' . $encrypted . ' => ' . $decrypted;
echo '
';
echo var_dump($text === $decrypted); // => bool(true)
===== ソースコード =====
_cipher = $cipher;
$this->_mode = $mode;
$this->_padding = $padding;
}
/**
* 暗号化する
* 初期化ベクトル(IV)を渡さない場合、ランダムな初期化ベクトルを生成する
* @param string $key 暗号鍵
* @param string $encryptee 暗号化するデータ
* @param string $iv (省略可)初期化ベクトル(ECBでは不要)
* @return array hex化した暗号化済みデータと、hex化した初期化ベクトル(IV)
*/
public function encrypt($key, $encryptee, $iv = null) {
$this->_checkKeySize($key);
if (!$iv) {
$iv = $this->_getRandIv();
}
if ($this->_padding === self::PAD_PKCS5) {
$encryptee = $this->padPkcs5($encryptee);
} else {
$encryptee = $this->pad($encryptee, $this->_padding);
}
$bin = mcrypt_encrypt($this->_cipher, $key, $encryptee, $this->_mode, $iv);
return array(bin2hex($bin), bin2hex($iv));
}
/**
* 復号する
* @param string $key 暗号鍵
* @param string $encrypted 暗号化されてhex化されたデータ
* @param string $iv (省略可)hex化した初期化ベクトル(ECBでは不要)
* @return string 復号したデータ
*/
public function decrypt($key, $encrypted, $iv = null) {
$this->_checkKeySize($key);
$bin = $this->hex2bin($encrypted);
if ($iv) {
$iv = $this->hex2bin($iv);
} else {
$iv = $this->_getRandIv(); //Warningを出さないためのダミーのIV
}
$decrypted = mcrypt_decrypt($this->_cipher, $key, $bin, $this->_mode, $iv);
if ($this->_padding === self::PAD_PKCS5) {
$decrypted = $this->trimPkcs5($decrypted);
} else {
$decrypted = rtrim($decrypted, $this->_padding);
}
return $decrypted;
}
/**
* ブロック長に合わせてパディングする
* @param string $data パディング対象のデータ
* @param string $padChar パディング文字
* @return string パディングしたデータ
*/
public function pad($data, $padChar) {
$size = $this->_getBlockSize();
return str_pad($data, ceil(strlen($data) / $size) * $size, $padChar);
}
/**
* PKCS#5でパディングする
* @param string $data パディング対象のデータ
* @return string パディングしたデータ
*/
public function padPkcs5($data) {
$size = $this->_getBlockSize();
$padLen = $size - (strlen($data) % $size);
return $data . str_repeat(chr($padLen), $padLen);
}
/**
* PKCS#5のパディングを除去する
* @param string $data PKCS#5でパディングされたデータ
* @return string パディングしたデータ
*/
public function trimPkcs5($data) {
return substr($data, 0, ord(substr($data, -1, 1)) * -1);
}
/**
* hex化したデータをバイナリに変換する(bin2hex()の反対)
* @param string $hex hex化されたデータ
* @return string バイナリになったデータ
*/
public function hex2bin($hex) {
return pack('H*', $hex);
}
/**
* 暗号鍵の長さをチェックする
* @param string $key 暗号鍵
* @throws Exception 長さが不正な場合に例外を投げる
*/
private function _checkKeySize($key) {
$sizes = mcrypt_module_get_supported_key_sizes($this->_cipher);
//可変の場合は空なのでチェックしない
if ($sizes && !in_array(strlen($key), $sizes)) {
throw new Exception("Invalid key length ($key)");
}
}
/**
* ランダムな初期化ベクトル(IV)を生成する
* @return string 生成した初期化ベクトル
*/
private function _getRandIv() {
srand();
return mcrypt_create_iv($this->_getBlockSize(), MCRYPT_RAND);
}
/**
* 暗号アルゴリズムと暗号モードに応じたブロックサイズを取得する
* @return integer ブロックサイズ
*/
private function _getBlockSize() {
return mcrypt_get_iv_size($this->_cipher, $this->_mode);
}
}