結論
ivとkeyは(opensslで使える)テキスト形式*1ではなく、オリジナルのバイナリ形式のまま使いましょう。
経緯
$ openssl aes-128-cbc -d -in a.in -out a.out -p -nosalt -iv 00000000000000000000000000000001 -K 9c4ceae9595bcde045cc53c1a214e512
というdecrypt(復号化)をRubyで行いたかったので
def decrypt(str, key, iv) decipher = OpenSSL::Cipher.new("aes-128-cbc") decipher.decrypt decipher.key = key decipher.iv = iv decipher.update(str) + decipher.final end
というメソッドを使って
iv = "00000000000000000000000000000001" # '%032x' % 1 key = "9c4ceae9595bcde045cc53c1a214e512" a_out = decrypt(a_in, key, iv)
としたら
OpenSSL::Cipher::CipherError: bad decrypt
と怒られました。
解決法
もしもどこかからivやkeyを取ってきた場合にはオリジナルのバイナリのまま使います。
今回のようにopensslで使うようなテキスト形式になっている場合には
iv_bin = "00000000000000000000000000000001".unpack('a2'*16).map{ |x| x.hex }.pack('C'*16) # => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" key_bin = "9c4ceae9595bcde045cc53c1a214e512".unpack('a2'*16).map{ |x| x.hex }.pack('C'*16) # => "\x9CL\xEA\xE9Y[\xCD\xE0E\xCCS\xC1\xA2\x14\xE5\x12"
のように16進数へ変換します*2。
なお最後のpack('C'*16)はpack('C*')でもpack('C16')でもかまいません。
参考
Why are the first 16 bytes wrong with Ruby openssl aes 128 cbc? - Stack Overflow
なお、こちらでは最後のpackはpack('c'*16)となっています。unpackで2バイトASCIIに変換→String#hexメソッドで16進数表記と解釈して整数化、なので符号付き整数のほうが適切だと思います。ということで、上ではpack('C'*16)と記載変更しました。
おまけ:逆変換(テキスト変換)
iv_text = iv_bin .unpack('C'*16).inject(''){ |str, hex| str << '%02x' % hex } # => "00000000000000000000000000000001" key_text = key_bin.unpack('C'*16).inject(''){ |str, hex| str << '%02x' % hex } # => "9c4ceae9595bcde045cc53c1a214e512"
逆変換だから、と思ってunpackしてmap{|x| x.to_s(16) }してpackしたらx.to_s(16)のところで桁数が落ちてしまったので、String#%で二桁確保するようにしました。
もちろんバイナリ変換と対称的にこうやってもOK
iv_text = iv_bin.unpack('C'*16).map{ |hex| '%02x' % hex }.pack('a2'*16) # => "00000000000000000000000000000001" key_text = key_bin.unpack('C'*16).map{ |hex| '%02x' % hex }.pack('a2'*16) # => "9c4ceae9595bcde045cc53c1a214e512"
ですが、packがあまり意味ないのでinjectを使うほうが好き。