0

我已经“继承”了一个 Ruby on Rails 应用程序,我必须将这个应用程序从 Ruby 翻译成 Java,最重要的是,我与创建者没有联系。

我的问题是 AES-256 身份验证中的 IV 向量。Ruby 应用程序使用 AESCrypt gem 来加密和解密用户的密码。它工作正常,我已经在数据库中有数千个用户。

问题是当我尝试在 Java 中做同样的事情时(我已经更新了 JCE 以允许 256 位密钥长度)。密钥和 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 个字节。我已经尝试了很多东西,但我快要疯了。

任何人都有同样的问题或找出我的问题在哪里?我已经发布了加密代码,但我在解密时遇到了同样的问题。

在此先感谢,我将非常感谢每一个答案。:)

4

1 回答 1

1

首先,您的 IV 实际上不是 iv,IV 应该是 HEX 编码的,但是您有 ASCII 字符串“por874hyufijdue7w63ysxwet4320o90”,可能是一些如何编码的?

其次,IV.getBytes() 会将 IV 的每个字符转换为十六进制编码,例如 p = 0x70、o = 0x6F、r = 0x72 等...

这不是一个有用的答案,但可能是提示。

实际上 IV 的长度必须与块密码的单个块长度相同。你有 32 字节长的 IV 本身,如果你让 IV.getBytes() IV 长度应该匹配密码块长度

于 2015-04-16T17:55:29.393 回答