今天接触的了一个php版本的 aes-128-cbc
en/de原文如下
class SecretUtil {
public static $hex_iv = '00000000000000000000000000000000'; # converted JAVA byte code in to HEX and placed it here
//public static $hex_iv = 'EDD80600A09CFBCB4B152C2B2FAAA237'; # converted JAVA byte code in to HEX and placed it here
public static $key='my key';
public static function setKey(){
self::$key = hash('sha256', self::$key, true);
}
public static function encrypt($str){
self::setKey();
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
mcrypt_generic_init($td, self::$key, self::hexToStr(self::$hex_iv));
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$pad = $block - (strlen($str) % $block);
$str .= str_repeat(chr($pad), $pad);
$encrypted = mcrypt_generic($td, $str);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return base64_encode($encrypted);
}
}
看起来好简单的样子。可是我发现怎么也还原不了,无头绪了半天。。。。。。真的半天,直到发现libmcrypt的确是个坑货
If you're writing code to encrypt/encrypt data in 2015, you should use openssl_encrypt() and openssl_decrypt(). The underlying library (libmcrypt) has been abandoned since 2007, and performs far worse than OpenSSL (which leverages AES-NI on modern processors and is cache-timing safe).
Also, MCRYPT_RIJNDAEL_256 is not AES-256, it's a different variant of the Rijndael block cipher. If you want AES-256 in mcrypt, you have to use MCRYPT_RIJNDAEL_128 with a 32-byte key. OpenSSL makes it more obvious which mode you are using (i.e. 'aes-128-cbc' vs 'aes-256-ctr').
那么好吧。事情就简单了
self::$key = hash('sha256', self::$key, true);
这个坑就填了。原来的加解密其实是使用的aes-256-cbc
第二个坑容易找 $pad = $block - (strlen($str) % $block);
正好加密内容的长度为 block_size,也就是16时,这家伙会直接追加16个0x10,这个比较有个性。所以使用openssl时也不能使用padding了,得自己来
我们来试试对应的ruby实现
#encoding:utf-8
require 'openssl'
require 'base64'
key=OpenSSL::Digest::SHA256.digest('my key')
%w(AES-128-CBC AES-256-CBC).each do |flavour|
string ='12345678901234561234567890123456asfg'
c = OpenSSL::Cipher::Cipher .new flavour
pad= c.block_size - string.length % c.block_size
str=string
str<< Array.new(pad){|x| x=pad}.pack('c*') #unless pad == 16
puts "string pad #{pad} to #{str.length}: #{str.unpack('H*')[0]}"
c.encrypt
c.padding=0
c.key = key
iv=Array.new(16,0).pack('c*')
c.iv=iv
enc = c.update(str) + c.final
en_str=Base64.strict_encode64(enc)
puts "#{flavour} encrypt #{string} \r\n#{en_str}"
#解密过程
d= OpenSSL::Cipher::Cipher .new flavour
d.decrypt
d.padding=0
d.key=key
d.iv=iv
dec=Base64.strict_decode64(en_str)
de_str=d.update(dec)+ d.final
puts "#{flavour} decrypt #{en_str} \r\n#{de_str.unpack('H*')[0]}"
#前面填的坑要自己来埋,去掉最后的padding个padding字符
last_char=de_str[-1,1].ord
padding=Array.new(last_char,last_char).pack('c*')
puts "padding #{padding.unpack('H*')[0] }"
de_str=de_str[0,de_str.length-padding.length] if de_str.end_with?(padding)
puts "drop padding #{de_str} \r\nhex:#{de_str.unpack('H*')[0]}"
end