ど素人から毛を生やす。<延>

mcrypt_encrypt()した暗号を、openssl_decrypt()できるのか?

Web > PHP 2025年8月15日(最終更新:1日前)

どもです。

PHP7.2以降、mcrypt関係の関数が大量に削除されました。
まあ普通はopensslに切り替えろという話なんですが、PHP7.2以降のサーバで、既存のmcryptで暗号化されたデータを復号だけして使用したい、という需要が発生しまして。

で、この問題。
結論から言えば、「opensslで使用できる暗号化方式なら、理論上は可能」です。

opensslで使用できる暗号化方式、というのは、bf-ecbとかaes-128-ecbとか、その辺。
詳しくはこちらの記事のコードをお使いの環境で試していただければ。

理論上は可能、というのは、元のmcrypt_encrypt()の時点で、ちゃんとしたパディングがされているなら可能、ということです。

どういうことかと言いますと…

$string = "暗号化する文字列";
$key = "01234567890123456789012345678901234567890";

$openssl_encrypt = openssl_encrypt($string, 'bf-ecb', $key);
echo '$openssl_encrypt : '.$openssl_encrypt."\n";

$mcrypt_encrypt1  = base64_encode(mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $string, MCRYPT_MODE_ECB));
echo '$mcrypt_encrypt1 : '.$mcrypt_encrypt1."\n";

$string = pkcs5_pad($string, mcrypt_get_block_size(MCRYPT_BLOWFISH, 'ecb'));
$mcrypt_encrypt2  = base64_encode(mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $string, MCRYPT_MODE_ECB));
echo '$mcrypt_encrypt2 : '.$mcrypt_encrypt2."\n";

echo 'mcrypt  decrypt1 : '.mcrypt_decrypt(MCRYPT_BLOWFISH, $key, base64_decode($mcrypt_encrypt1), "ecb")."\n";
echo 'mcrypt  decrypt2 : '.mcrypt_decrypt(MCRYPT_BLOWFISH, $key, base64_decode($mcrypt_encrypt2), "ecb")."\n";
echo 'mcrypt  decrypt2@: '.pkcs5_unpad(mcrypt_decrypt(MCRYPT_BLOWFISH, $key, base64_decode($mcrypt_encrypt2), "ecb"))."\n";
echo 'openssl decrypt1 : '.openssl_decrypt($mcrypt_encrypt1, "bf-ecb", $key)."\n";
echo 'openssl decrypt2 : '.openssl_decrypt($mcrypt_encrypt2, "bf-ecb", $key)."\n";

function pkcs5_pad($text, $blocksize){
	$pad = $blocksize - (strlen($text) % $blocksize);
	return $text . str_repeat(chr($pad), $pad);
}

function pkcs5_unpad($text){
	$pad = ord($text{strlen($text)-1});
	if ($pad > strlen($text)) return false;
	if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false;
	return substr($text, 0, -1 * $pad);
}
$openssl_encrypt : 9AyGCvn1BhocJ3IDFVIQb2RmhpXW4oN1ThgZIJZCJ0E=
$mcrypt_encrypt1 : 9AyGCvn1BhocJ3IDFVIQb2RmhpXW4oN1
$mcrypt_encrypt2 : 9AyGCvn1BhocJ3IDFVIQb2RmhpXW4oN1ThgZIJZCJ0E=
mcrypt  decrypt1 : 暗号化する文字列
mcrypt  decrypt2 : 暗号化する文字列⍁⍁⍁⍁⍁⍁⍁
mcrypt  decrypt2@: 暗号化する文字列
openssl decrypt1 :
openssl decrypt2 : 暗号化する文字列

こう。

・暗号化を行うには、文字列は暗号ルールに定められたブロック長でなければならない。
・ブロック長ルールのため、暗号化関数で文字列はパディングされている。
・mcryptのパディングはZeroBytePaddingという方式で、データ保全という点で安全性が低く、mcrypt以外と互換性が無い。

これが、普通にmcrypt_encrypt()した暗号を、openssl_decrypt()できない理由。

ZeroBytePaddingは良くないので別のパディング(今回の例では PKCS#5 Padding)を使うと正しい暗号化ができますが、復号後にパディングを取り除く必要が出てきて処理がややこしくなってしまう。

mcrypt_encrypt()←→mcrypt_decrypt()なら、復号時にバグることは取り敢えず無いので、ちゃんとパディングしてmcrypt_encrypt()されているケースはたぶん稀。

そのため、mcrypt_encrypt()した暗号を、openssl_decrypt()するのは、あくまで「理論上」可能であり、実際は難しいということです( º言º)

余談

mcryptでの暗号化を行う際、どのような暗号化であってもIV(初期化ベクトル)を引数にできます。
しかし、ECBモードの暗号化ではIVは使用しません。

つまり、

     mcrypt_ecb(MCRYPT_BLOWFISH, $key, $data, MCRYPT_DECRYPT, $iv)
 === mcrypt_ecb(MCRYPT_BLOWFISH, $key, $data, MCRYPT_DECRYPT)
 === mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $data, "ecb", $iv)
 === mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $data, "ecb")

という結果になります。opensslはちゃんとエラーを吐いてくれるんですが。

この記事は役に立ちましたか?
  • _(:3」∠)_ 面白かった (0)
  • (・∀・) 参考になった (0)
  • (`・ω・´) 役に立った (0)