[英]AES/CBC encrypt in Java, decrypt in Ruby
I am trying to translate the following (working) Java code to Ruby. 我正在尝试将以下(工作)Java代码转换为Ruby。
public static final String PROVIDER = "BC";
public static final int IV_LENGTH = 16;
private static final String HASH_ALGORITHM = "SHA-512";
private static final String PBE_ALGORITHM = "PBEWithSHA256And256BitAES-CBC-BC";
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
private static final String SECRET_KEY_ALGORITHM = "AES";
public String decrypt(SecretKey secret, String encrypted) {
Cipher decryptionCipher = Cipher.getInstance(CIPHER_ALGORITHM, PROVIDER);
String ivHex = encrypted.substring(0, IV_LENGTH * 2);
String encryptedHex = encrypted.substring(IV_LENGTH * 2);
IvParameterSpec ivspec = new IvParameterSpec(HexEncoder.toByte(ivHex));
decryptionCipher.init(Cipher.DECRYPT_MODE, secret, ivspec);
byte[] decryptedText = decryptionCipher.doFinal(HexEncoder.toByte(encryptedHex));
String decrypted = new String(decryptedText, "UTF-8");
return decrypted;
}
My (not working) Ruby code is this: 我的(不工作)Ruby代码是这样的:
require 'openssl'
require 'digest/sha2'
SECRET = "MY PASSWORD AS RAW TEXT"
IV_LENGHT = 16
encoded = "45D0EC4D910C0A6FF67325FF7362DCBC4613B6F3BFDFE35930D764FB1FE62251"
iv = encoded.slice(0, IV_LENGHT * 2)
e = encoded.slice(IV_LENGHT*2..-1)
binary_iv = iv.unpack('a2'*IV_LENGHT).map{|x| x.hex}.pack('c'*IV_LENGHT)
binary_e = e.unpack('a2'*IV_LENGHT).map{|x| x.hex}.pack('c'*IV_LENGHT)
c = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
c.decrypt
c.key = Digest::SHA256.digest(SECRET).slice(0, IV_LENGHT* 2 )
c.iv = binary_iv
d = c.update(binary_e)
d << c.final
puts "decrypted: #{d}\n"
I have tried the binary and non binary versions, with no luck. 我试过二进制和非二进制版本,没有运气。 Someone can point to the problem?
有人可以指出这个问题吗?
Based on the title here, I am going to assume that you want to be able to encrypt a message in Java, and then decrypt that message in Ruby, using password-based AES-CBC encryption. 基于此处的标题,我将假设您希望能够使用Java 加密消息,然后使用基于密码的AES-CBC加密在Ruby中解密该消息。
Now, the openssl
standard library in Ruby readily supports password-based key derivation function 2 based on PKCS5 . 现在,Ruby中的
openssl
标准库很容易支持基于PKCS5的基于密码的密钥派生功能2 。 You can greatly simplify your Ruby decryption code if you leverage this in your Java encryption . 如果在Java 加密中利用它,可以大大简化Ruby 解密代码。
Here is how you would encrypt using PBKDF2 in PKCS5 in Java: 以下是在Java中使用PKCS5中的PBKDF2加密的方法:
// in Java-land
import java.security.AlgorithmParameters;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
...
static String printHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", (b & 0xFF)));
}
return sb.toString();
}
public static Map<String,String> encrypt(String msg, String pwd, byte[] salt)
throws Exception {
Map<String,String> retval = new HashMap<String,String>();
// prepare to use PBKDF2/HMAC+SHA1, since ruby supports this readily
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
// our key is 256 bits, and can be generated knowing the password and salt
KeySpec spec = new PBEKeySpec(pwd.toCharArray(), salt, 1024, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
// given key above, our cippher will be aes-256-cbc in ruby/openssl
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
// generate the intialization vector
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
retval.put("iv", printHex(iv));
byte[] ciphertext = cipher.doFinal(msg.getBytes("UTF-8"));
retval.put("encrypted", printHex(ciphertext));
return retval;
}
public static void main(String[] args) throws Exception {
String msg = "To Ruby, from Java, with love...";
String pwd = "password";
String salt = "8 bytes!"; // in reality, you would use SecureRandom!
System.out.println("password (plaintext): " + pwd);
System.out.println("salt: " + salt);
Map<String,String> m = encrypt(msg, pwd, salt.getBytes());
System.out.println("encrypted: " + m.get("encrypted"));
System.out.println("iv: " + m.get("iv"));
}
Running the above will result in something like the following output. 运行以上将导致类似以下输出。
password (plaintext): password
salt: 8 bytes!
encrypted: 4a39f1a967c728e11c7a5a3fb5d73ad07561f504c9d084d0b1ae600cc1f75137cbb82a4d826c060cb06e2e283449738d
iv: ecbc985b3550edc977a17acc066f2192
Hex strings are used for the encrypted message and initialization vector since you can use OpenSSL to verify the encryption/decryption process (highly recommended). 十六进制字符串用于加密消息和初始化向量,因为您可以使用OpenSSL来验证加密/解密过程(强烈推荐)。
Now from a Ruby program, you would use the AES-256-CBC
cipher, and derive the secret key from the password
and salt
strings (not byte[]
as per Java). 现在,从Ruby程序中,您将使用
AES-256-CBC
密码,并从password
和salt
字符串(而非按照Java的byte[]
)派生密钥。 Using the output from the above-mentioned Java program, we have: 使用上述Java程序的输出,我们有:
# from Ruby-land
require 'openssl'
d = OpenSSL::Cipher.new("AES-256-CBC")
d.decrypt
key = OpenSSL::PKCS5.pbkdf2_hmac_sha1("password", "8 bytes!", 1024, d.key_len)
d.key = key
d.iv = "ecbc985b3550edc977a17acc066f2192".scan(/../).map{|b|b.hex}.pack('c*')
data = "4a39f1a967c728e11c7a5a3fb5d73ad07561f504c9d084d0b1ae600cc1f75137cbb82a4d826c060cb06e2e283449738d".scan(/../).map{|b|b.hex}.pack('c*')
d.update(data) << d.final
=> "To Ruby, from Java, with love..."
NOTE: The Ruby part of this code pretty much comes verbatim from the Japanese documentation on the openssl
standard library . 注意:此代码的Ruby部分几乎完全来自
openssl
标准库上的日文文档 。
I once had a similar problem with CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
我曾经遇到类似的问题CIPHER_ALGORITHM =“AES / CBC / PKCS5Padding”; and decryption through the openSSL-library in C which I could't solve.
并通过C中的openSSL库解密,我无法解决。 I avoided the problem by using
"AES/CBC/NoPadding" and by adding a particular padding to the plaintext manually.
我通过使用“AES / CBC / NoPadding”并通过手动向明文添加特定填充来避免此问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.