我已经"继承了"一个Ruby on Rails应用程序,我必须将这个应用程序从Ruby翻译成Java,最重要的是,我没有与创建者联系。
我的问题在于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)
我想我的问题是我在byte []中转换IV java字符串的方式。我认为ruby中的openSSL代码是将IV的32个字节解压缩(或在内部执行)到16个字节。我尝试了很多东西,但我发疯了。
任何人都有同样的问题或弄清楚哪里可能是我的问题? 我已经发布了加密代码,但解密时遇到了同样的问题。
在此先感谢,我对每一个答案都非常感激。 :)
答案 0 :(得分:1)
首先,你的IV实际上不是iv,IV应该是HEX编码的,但是你有ASCII字符串" por874hyufijdue7w63ysxwet4320o90",可能是一些如何编码?
其次,IV.getBytes()会将IV的每个字符转换为十六进制编码,如p = 0x70,o = 0x6F,r = 0x72等......
这不是一个有用的答案,但可能暗示。
实际上,IV必须与块密码单块长度相同。你有43个字节长的IV本身,如果你使IV.getBytes()IV长度应该与密码块长度相匹配