2015-04-16 3 views
0

У меня есть «унаследованное» приложение Ruby on Rails, и я должен перевести это приложение с Ruby на Java, и самое главное, у меня нет контакта с Создатель.AES-256: недопонимание векторов между реализацией Ruby и Java

Моя проблема связана с вектором IV в аутентификации AES-256. Приложение Ruby использует AESCrypt gem для шифрования и дешифрования пароля пользователя. Он отлично работает, и у меня уже есть несколько тысяч пользователей в БД.

Проблема в том, что я пытаюсь сделать то же самое в Java (я уже обновил JCE, чтобы разрешить длину в 256 бит). Ключ и IV записываются как двоичные строки в исходном коде ruby ​​(см. Ниже), и когда я пытаюсь использовать его в Java, я получаю исключение, которое говорит о том, что длина IV должна быть 16 байтов (я знаю, что это должно быть 16 байтов, но двоичная строка в Ruby имеет 32 символа).

рубин код (работает отлично):

require 'openssl' 
require 'digest/md5' 
require 'base64' 

module AESCrypt 
    KEY = "AB1CD237690AF13B6721AD237A" 
    IV = "por874hyufijdue7w63ysxwet4320o90" 
    TYPE = "AES-256-CBC" 

    def AESCrypt.key(key) 
    key = Digest::MD5.hexdigest(key) 
    key.slice(0..32) 
    end 

# Encrypts a block of data given an encryption key and an 
    # initialization vector (iv). Keys, iv's, and the data returned 
    # are all binary strings. Cipher_type should be "AES-256-CBC", 
    # "AES-256-ECB", or any of the cipher types supported by OpenSSL. 
    # Pass nil for the iv if the encryption type doesn't use iv's (like 
    # ECB). 
    #:return: => String 
    #:arg: data => String 
    #:arg: key => String 
    #:arg: iv => String 
    #:arg: cipher_type => String 
    def AESCrypt.encrypt(data) 
    return nil if data.nil? 
    return data if data.blank? 
    aes = OpenSSL::Cipher::Cipher.new(TYPE) 
    aes.encrypt 
    aes.key = AESCrypt.key(KEY) 
    aes.iv = IV if IV != nil 
    result = aes.update(data) + aes.final 
    Base64.encode64(result) 
    end 
end 

и это мой Java-код (он должен делать то же самое, кажется, что работает с 16 символов/байт IV):

public static void main(String[] args) throws UnsupportedEncodingException { 
      String KEY = "AB1CD237690AF13B6721AD237A"; 
      String IV = "por874hyufijdue7w63ysxwet4320o90"; 
      SecretKeySpec key = generateKey(KEY); 
      String message = "password"; 

      final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
      IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes()); 
      cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec); 
      byte[] ciphedText = cipher.doFinal(message.getBytes()); 
      String encoded = Base64.encodeBase64String(ciphedText); 

      System.out.println("ENCRYPTED text= " + encoded); 
} 

public static SecretKeySpec generateKey(final String password) throws NoSuchAlgorithmException, UnsupportedEncodingException { 
     final MessageDigest digest = MessageDigest.getInstance("MD5"); 
     byte[] bytes = password.getBytes("UTF-8"); 
     digest.update(bytes, 0, bytes.length); 
     byte[] key = digest.digest();  
     SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES"); 
     return secretKeySpec; 
    } 

И я получаю это исключение (очевидно):

java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 16 bytes long 
    at com.sun.crypto.provider.CipherCore.init(CipherCore.java:516) 
    at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:339) 
    at javax.crypto.Cipher.implInit(Cipher.java:801) 
    at javax.crypto.Cipher.chooseProvider(Cipher.java:859) 
    at javax.crypto.Cipher.init(Cipher.java:1370) 
    at javax.crypto.Cipher.init(Cipher.java:1301) 
    at com.javi.test.security.Test.main(Test.java:129) 

Я думаю, что моя проблема в том, как я преобразовать Java строку IV в байтах []. Я думаю, что openSSL-код в ruby ​​распаковывает (или делает что-то внутренне) 32 байта от IV до 16 байтов. Я много пробовал, но я схожу с ума.

У кого-то была такая же проблема или выяснить, где может быть моя проблема? Я опубликовал код шифрования, но у меня такая же проблема с расшифровкой.

Заранее благодарим за каждый ответ. :)

+1

Я не знаю, как работает ruby ​​/ openssl, но, вероятно, только первые 16 символов молча используют внутривенно –

+0

Я только что сделал тест, взяв в расчет только первые 16 символов моей строки IV. Зашифрованный мной текст - «123456», зашифрованный текст с помощью ruby-приложения - «APkjtkW8yg4ibXhc/OgO3w ==», а зашифрованный текст java - «n6ZD4972iputzGCstjifXw ==». Они не соответствуют друг другу :(Может быть, более раннее поколение KEY не работает, должно быть, я новичок в шифровании. Спасибо в любом случае @ArtjomB. –

+0

Вы кодируете строку UTF-8 в Ruby-коде? UTF-8 по умолчанию, а строка UTF-8 будет кодироваться по-разному с ASCII/латинской строкой. – mcfinnigan

ответ

1

Во-первых, ваш IV на самом деле не iv, IV должен быть закодирован HEX, но у вас есть строка ASCII «por874hyufijdue7w63ysxwet4320o90», может быть, это какая-то кодировка?

Во-вторых, IV.getBytes() будет transofr IV, каждый символ в шестнадцатеричной кодировке, как р = 0x70, о = 0x6F, г = 0x72, и т.д. ...

Это не полезный ответ, но может быть намек.

Фактически, длина IV должна быть такой же длины, как длина блока блочного шифра. У вас есть сам 32 байта длиной 4 байта, если вы делаете IV.getBytes() длина IV должна соответствовать длине блока шифрования

+0

Вы близко. Проблема заключается в том, что она должна быть длиной 32 байта, а не 26 байтов как« AB1CD237690AF13B6721AD237A », остальная часть этого ключа не определена, она не будет воспроизводить один и тот же шифр. ключ или IV слишком длинный.Для тестирования можно использовать значения ASCII, если они имеют минимальную длину. –

+0

Привет @Barmak, вы правы, я изменил дайджест MD5 ключа к этому: Key = DigestUtils.md5Hex (KEY) .getBytes(); apache_commons_codec, а затем он имеет 32 байта. Я думаю, что я исправил это, но у меня все еще есть проблема длины IV. Юрис, я знаю, что мой IV странный, а его длина составляет 32 байта. Рубины что-то делают, чтобы преобразовать эти 32 - 16 байт? –

+0

@Javier, я не знаю Ruby и MD5, я думал, MD5 возвращает 128 бит (16 байт). Просто используйте значение ASCII для тестирования, заставьте ключ длиной 32 символа. IV 16 символов. Сообщение длиной 16 символов (во избежание заполнения). Посмотрим, работает ли это для тестирования. –