2009.06.18

Ruby Rails

OpenSSL::Cipher は Ruby と JRuby (Java) で実行結果が異なる (場合がある)

Ruby OpenSSL は JRuby OpenSSL とは実装が違うので、同じコードでも結果が異なる場合があります。

昨日 OpenSSL::Cipher を利用していてそんな状況に遭遇したので、メモメモ。

以下のコードは、Ruby と JRuby (or Java) で実行結果が異なります。つまり、Ruby で暗号化したデータを JRuby (or Java) で複合化できません。。。

require 'openssl'
def display_bytes(bytes) bytes.each_byte do |byte| print byte, ":" end end
plain_text = "some plain text is here" key = "1234567890abcdef" # 16 bytes iv = "abcdef1234567890" # 16 bytes
cipher = OpenSSL::Cipher::AES.new("128-CBC") cipher.iv, cipher.key = iv, key cipher.encrypt cipher_text = cipher.update(plain_text) + cipher.final
display_bytes(cipher_text)

そして、なんと赤文字の部分を逆にしてやると Ruby と JRuby の結果は同じになります。

cipher.encrypt
cipher.iv, cipher.key = iv, key

JRuby は順序を変えても結果は変わらないので、Ruby OpenSSL の方に問題があるのだと思います。

ちなみにこれと同じことをする Java コードは以下。

import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class CipherTest { public static void main(String[] args) throws Exception { String plain_text = "some plain text is here"; byte[] key = "1234567890abcdef".getBytes(); // 16 bytes byte[] iv = "abcdef1234567890".getBytes(); // 16 bytes Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); Key cipherKey = new SecretKeySpec(key, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(iv); cipher.init(Cipher.ENCRYPT_MODE, cipherKey, ivSpec); byte[] cipher_text = cipher.doFinal(plain_text.getBytes()); display_bytes(cipher_text); } public static void display_bytes(byte[] bytes) { for (byte b : bytes) { System.out.print(b); System.out.print(":"); } } }

ps.
Java と Ruby (or JRuby) では byte の扱いが違うので、Java の結果と Ruby (or JRuby) の結果を比較するには、Ruby (or JRuby) 側で以下のように 0..255 => -128..127 に変換する必要があります。

def display(title, str)
  str.each_byte do |byte|
    if byte.to_i > 127
      byte = byte - 256
    end
    print byte, ":"
  end
end