2015-04-16 36 views
0

我已經「繼承」了一個Ruby on Rails應用程序,並且我必須將此應用程序從Ruby轉換爲Java,並且最重要的是,我沒有與創作者。AES-256:在Ruby和Java實現之間的IV矢量誤解

我的問題是在AES-256認證中的IV矢量。 Ruby應用程序使用AESCrypt gem來加密和解密用戶的密碼。它工作正常,我已經有成千上萬的用戶在數據庫中。

問題是,當我嘗試在Java中做同樣的事情時(我已經更新了JCE以允許256位密鑰長度)。 Key和IV在ruby源代碼中被寫成二進制字符串(見下文),當我嘗試在Java中使用它時,我得到一個異常,它說IV長度必須是16字節長(我知道它必須是長度爲16個字節,但Ruby中的二進制字符串有32個字符)。

Ruby代碼(正常工作):

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) 

我想我的問題是我轉換字節[]中的IV Java字符串的方式。我認爲ruby中的openSSL代碼是將IV的32個字節解包(或內部執行)到16個字節。我嘗試了很多東西,但我要瘋了。

任何人都有同樣的問題或找出哪裏可能是我的問題? 我已經發布了加密代碼,但我解密相同的問題。

在此先感謝,我會非常感謝每一個答案。 :)

+1

我不知道ruby/openssl是如何操作的,但可能只有前16個字符默默地用於IV。 –

+0

我剛剛完成了考試,只考慮了我的IV字符串的拳頭16個字符。我加密的文本是「123456」,ruby app的加密文本是「APkjtkW8yg4ibXhc/OgO3w ==」,而java的加密文本是「n6ZD4972iputzGCstjifXw ==」。他們不匹配:(也許早期的KEY代碼不適用於它,應該是我新加密的。無論如何感謝@ArtjomB。 –

+0

您是否在Ruby代碼中編碼UTF-8字符串?因爲Java是UTF-8默認情況下,UTF-8字符串的編碼方式不同於ASCII /拉丁字符串 – mcfinnigan

回答

1

首先,你的IV實際上不是IV,IV應該是HEX編碼的,但是你有ASCII字符串「por874hyufijdue7w63ysxwet4320o90」,可能是一些怎麼編碼的?

其次,IV.getBytes()將IV的每個字符transofr到十六進制編碼像P = 0x70,O = 0x6F,R = 0x72,等等...

它不是一個有用的答案,但也可以是暗示。

其實IV必須和塊密碼單塊長度一樣長。你有四十二個字節長的IV本身,如果你使IV.getBytes()IV的長度應該與密碼塊的長度相匹配

+0

你好,關鍵是它的長度應該是32個字節,而不是26個字節,如」AB1CD237690AF13B6721AD237A「,這個鍵的其餘部分是未定義的,它不會再現相同的密碼。鍵或IV太長。對於測試,只要具有最小長度就可以使用ASCII值。 –

+0

嗨@Barmak,你是對的,我已經將密鑰的MD5摘要更改爲:Key = DigestUtils.md5Hex(KEY).getBytes();的apache_commons_codec,然後它有32個字節。我認爲我已經解決了這個問題,但是我仍然有IV長度的問題。 Juris,我知道我的IV很奇怪,它的長度是32個字節。 Ruby是否將這些32位字節轉換爲16字節? –

+0

@Javier,我不知道Ruby和MD5,我以爲MD5會返回128位(16字節)的密鑰。只需使用ASCII值進行測試,使關鍵字長度爲32個字符。 IV 16個字符。消息長度爲16個字符(以避免填充)。讓我們看看它是否適用於測試。 –