Java中的AES / CBC加密,Ruby中的解密 [英] AES/CBC encrypt in Java, decrypt in Ruby

查看:264
本文介绍了Java中的AES / CBC加密,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 = 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));
decryptCipher.init(Cipher.DECRYPT_MODE,secret,ivspec);
byte [] decryptptedText = decryptCipher.doFinal(HexEncoder.toByte(encryptedHex));
String decryptpted = new String(decryptedText,UTF-8);
返回解密;
}

我的(不工作)Ruby代码是这样的:

  require'openssl'
require'digest / sha2'

SECRET =以PASSWORD为原始文本
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。解密
c.key = Digest :: SHA256.digest(SECRET).slice(0,IV_LENGHT * 2)
c.iv = binary_iv
d = c.update(binary_e)
d< c.final
放解密:#{d} \\\

我有尝试二进制和非二进制版本,没有运气。
有人可以指出问题?

解决方案

根据这里的标题,我将假设你想要可以使用Java加密 Java中的消息,然后使用基于密码的AES-CBC加密在Ruby中解密该消息。



现在,Ruby中的 openssl 标准库很容易支持基于PKCS5的基于密码的密钥导出功能2。如果您在Java 加密中使用此功能,您可以大大简化您的Ruby 解密代码。



将使用Java中的PKCS5中的PBKDF2加密:

  //在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异常{
Map< String,String> retval = new HashMap< String,String>();

//准备使用PBKDF2 / HMAC + SHA1,因为ruby很容易支持
SecretKeyFactory factory = SecretKeyFactory.getInstance(PBKDF2WithHmacSHA1);
//我们的密钥是256位,可以生成知道密码和盐
KeySpec spec = new PBEKeySpec(pwd.toCharArray(),salt,1024,256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(),AES);

//给出键,我们的cippher将是ruby / openssl
中的aes-256-cbc加密密码= Cipher.getInstance(AES / CBC / PKCS5Padding);
cipher.init(Cipher.ENCRYPT_MODE,secret);
AlgorithmParameters params = cipher.getParameters();

//生成初始化向量
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异常{
String msg =来自Java,使用爱...
String pwd =password;
String salt =8 bytes!; //在现实中,你会使用SecureRandom!

System.out.println(password(plaintext):+ pwd);
System.out.println(salt:+ salt);

地图< String,String> m = encrypt(msg,pwd,salt.getBytes());
System.out.println(encrypted:+ m.get(encrypted));
System.out.println(iv:+ m.get(iv));
}

运行以上将导致类似以下输出。

 密码(明文):密码
盐:8字节!
加密:4a39f1a967c728e11c7a5a3fb5d73ad07561f504c9d084d0b1ae600cc1f75137cbb82a4d826c060cb06e2e283449738d
iv:ecbc985b3550edc977a17acc066f2192

十六进制字符串用于加密消息和初始化向量,因为您可以使用OpenSSL验证加密/解密过程(强烈推荐)。



现在,从Ruby程序,您将使用 AES-256-CBC 密码,并从密码 salt 字符串(不是 byte [] 根据Java)。使用上述Java程序的输出,我们有:

 #from Ruby-land 
require'openssl '

d = OpenSSL :: Cipher.new(AES-256-CBC)
d.decrypt
key = OpenSSL :: PKCS5.pbkdf2_hmac_sha1(password, 8字节!,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数据)<< d.final
=> 给Ruby,从Java,爱...

注意:这个代码的Ruby部分几乎来自 日语文档在 openssl 标准库


I am trying to translate the following (working) Java code to 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:

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.

Now, the openssl standard library in Ruby readily supports password-based key derivation function 2 based on PKCS5. You can greatly simplify your Ruby decryption code if you leverage this in your Java encryption.

Here is how you would encrypt using PBKDF2 in PKCS5 in Java:

// 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).

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). Using the output from the above-mentioned Java program, we have:

# 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.

这篇关于Java中的AES / CBC加密,Ruby中的解密的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆