過去にPHPでencryptしてDBに突っ込まれたデータを、
今回Rubyでdecryptする際に結構はまったので。
encrypt
PHP側のコードはだいたいこんな感じ
暗号方式は 3DES ECB だった。
mcrypt.php
<?php
$mcrypt_key = 'hoge'; #3DESの規定キー長より短い
$plaintext = 'あいうえお'; #マルチバイト文字
$td = mcrypt_module_open(MCRYPT_TRIPLEDES, '', MCRYPT_MODE_ECB, '');
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
mcrypt_generic_init($td, $mcrypt_key, $iv);
$ciphertext = mcrypt_generic($td, $plaintext);
# 表示の関係上base64エンコード
echo base64_encode($ciphertext);
実行結果:QERSswy6yzBsSrZfRxS+ow==
ポイントは
- キー長が短い(エラーにならない)
- マルチバイト文字
decrypt
で、ruby側。バージョンは1.9.3
今回はruby-mcryptを利用。
mcrypt.rb
# -*- encoding:utf-8 -*-
require 'mcrypt'
require 'base64'
mcrypt_key = 'hoge'
ciphertext = 'QERSswy6yzBsSrZfRxS+ow=='
crypto = ::Mcrypt.new(:tripledes, :ecb)
# キー長が足りない分は \0 で埋める
if mcrypt_key.length < crypto.key_size
mcrypt_key = mcrypt_key.ljust(crypto.key_size, "\0")
end
crypto.key = mcrypt_key
crypto.iv = nil
crypto.padding = :zeros
plaintext = crypto.decrypt(::Base64.decode64(ciphertext))
# decrypt結果の文字コード情報をUTF-8にする
p plaintext.force_encoding('utf-8')
実行結果:あいうえお
- キー長が短い場合は \0 で埋める(そのままだとエラーになる)
- decryptの結果は ascii-8bit になってるので適宜encodingの変更をする
余談
OpenSSL::Cipherでも試してみた。
キーの\0埋めや最後のforce_encodingはこちらも同様。
ただ、paddingの方式がOpenSSLの場合PKCS#5で固定(?)で
PHPのMcryptは \0 埋めが固定(?)みたいなので、OpenSSL側でのpaddingをせずに自前で除去する必要がありそう。
openssl.rb
# -*- encoding:utf-8 -*-
require 'openssl'
require 'base64'
mcrypt_key = 'hoge'
ciphertext = 'QERSswy6yzBsSrZfRxS+ow=='
crypto = ::OpenSSL::Cipher.new('des-ede3')
crypto.decrypt
# キー長が足りない分は \0 で埋める
if mcrypt_key.length < crypto.key_len
mcrypt_key = mcrypt_key.ljust(crypto.key_len, "\0")
end
crypto.key = mcrypt_key
# 明示的にpaddingを無効
crypto.padding = 0
plaintext = crypto.update(::Base64.decode64(ciphertext))
# 末尾の\0を除去し、文字コード情報をUTF-8にする
p plaintext.sub(/\0*\Z/, '').force_encoding('utf-8')
実行結果:あいうえお