试图解密来自Java的attr_encrypted存储值 [英] trying to decrypt attr_encrypted stored value from Java

查看:201
本文介绍了试图解密来自Java的attr_encrypted存储值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个rails应用程序,在其中一个模型中加密(使用attr_encrypted)2个字段。



我的进程的另一部分,它不是web应用程序我需要使用这些数据执行一些任务(明文)。



我正在尝试从数据库中读取存储的值并对其进行解密,但不能...... / p>

我的模型如下所示:

  class SecretData< ActiveRecord :: Base 
mysecret =mylittlesecret

attr_encrypted:data1,:key => mysecret,:algorithm => aes-256-cbc
attr_encrypted:data2,:key => mysecret,:algorithm => aes-256-cbc

...
结束

数据库字段(encrypted_data1和encrypted_data2)充满了数据,但是当我尝试解码base64(默认情况下attr_encrypted执行此操作)并解密时(我尝试使用命令行中的openssl并使用Java)我得到恶魔数( openssl)或关于密钥长度的各种错误(在Java中)。我花了很多时间试图解密这些字符串,但却找不到方法。



以下是我拥有的所有数据:

加密+ base64字符串(对于data1和data2)是:

  cyE3jDkKc99GVB8TiUlBxQ == 
sqcbOnBTl6yy3wwjkl0qhA ==

我可以从它们中解码base64并得到一些字节数组。
当我尝试:

  echo cyE3jDkKc99GVB8TiUlBxQ == | openssl aes-256-cbc -a -d(并键入mylittlesecret作为密码)

I get:bad magic number



当我尝试以下Java代码时:

  Key key = generateKey(); 
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.DECRYPT_MODE,key);
byte [] decordedValue = new BASE64Decoder()。decodeBuffer(encryptedData);
byte [] decValue = c.doFinal(decordedValue);
String decryptedValue = new String(decValue);

我得到java.security.InvalidKeyException:无效的AES密钥长度:14个字节

我已经尝试了很多Java代码的变体,所以可能是这个特定的一个是完全错误..



当我尝试使用ruby时:

  irb(main):069:0> Encryptor.decrypt(Base64.decode64(cyE3jDkKc99GVB8TiUlBxQ ==),,, key =>'mylittlesecret')
=> data1-value

我得到正确的解密值(如你所见)。



我还注意到,当我尝试在Java中加密相同的字符串并在Base64中编码时,我得到一个更长的字符串(在base64之后)。不知道为什么,但它可能是相关的..



我认为我还应该加密值加盐/ iv,但我不认为它存储在任何地方..我试图加密相同的值两次并获得相同的输出字符串,因此它不是随机的。



有谁知道attr_encrypted(它使用ruby的加密器)如何加密数据以及如何在外部解密?

解决方案

好吧,多亏了owlstead,我能够解决这个问题。我在ruby和Java中发布代码以防将来有人需要它:



正如owlstead所提到的那样,问题确实存在于EVP_BytesToKey中(密钥生成来自密码和盐)。 Ruby由于某种原因不使用标准的,因此Java(或openssl)无法解码。



这是一个使用标准方法的ruby实现:

  def self.encrypt(options)

plaintext = options [:value]
return如果是plaintext.blank,则为true?

cipher = OpenSSL :: Cipher :: Cipher.new(@@ cipher_type)
cipher.encrypt

iv = cipher.random_iv
salt = (0 ... @@ salt_length)。map {65。+(rand(25))。chr} .join #random salt
key = OpenSSL :: PKCS5.pbkdf2_hmac_sha1(@@ password,salt,@@ pkbdf_num_iters,cipher.key_len)

cipher.key = key
cipher.iv = iv

enc_data = cipher.update(plaintext)
enc_data< ;< cipher.final

final_data = salt<< iv<< enc_data
Base64.strict_encode64(final_data)
end

def self.decrypt(options)

ciphertext = options [:value]
如果ciphertext.blank返回true?


cipher = OpenSSL :: Cipher :: Cipher.new(@@ cipher_type)
cipher.decrypt

cipher_data = Base64.decode64(ciphertext )

salt = cipher_data [0 .. @@ salt_length-1]
iv = cipher_data [@@ salt_length .. @@ salt_length + cipher.iv_len]
enc_data = cipher_data [@@ salt_length + cipher.iv_len .. -1] #the rest

key = OpenSSL :: PKCS5.pbkdf2_hmac_sha1(@@ password,salt,@@ pkbdf_num_iters,cipher.key_len)

cipher.key = key
cipher.iv = iv

plaintext = cipher.update(enc_data)
plaintext<< cipher.final

plaintext
end

我已设置以下参数:
- cipher_type = aes-128-cbc(Java仅支持128但开箱即用。除此之外,您需要安装一些额外的软件包)
- salt_length = 8
- pkbdf_num_iters = 1024



这是用于解码的Java方法:

  public String decrypt(String ciphertext)throws Exception {
byte [] crypt = Base64.decodeBase64(ciphertext);

//解析加密数据并得到盐和IV
byte [] salt = Arrays.copyOfRange(crypt,0,saltLength);
byte [] iv = Arrays.copyOfRange(crypt,saltLength,saltLength + ivLength);
byte [] encryptedData = Arrays.copyOfRange(crypt,saltLength + ivLength,crypt.length);

//从salt生成密钥和密码
SecretKeyFactory f = SecretKeyFactory.getInstance(secretKeyName);
KeySpec ks = new PBEKeySpec(password.toCharArray(),salt,pbkdfNumIters,keyLength);
SecretKey s = f.generateSecret(ks);
Key keySpec = new SecretKeySpec(s.getEncoded(),AES);

//使用密钥初始化密码对象和IV
Cipher cipher = Cipher.getInstance(cipherAlgo);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE,keySpec,ivSpec);

//解密
byte [] decBytes = cipher.doFinal(encryptedData);

返回new String(decBytes);
}

为我工作。



希望它有所帮助(或将会对某人有所帮助)



Zach


I have a rails application that encrypts (with attr_encrypted) 2 fields in one of the models.

Another part of my process, which is not the web-application needs to perform some tasks using this data (plaintext).

I'm trying to read the stored values from the DB and decrypt them but just can't..

my model looks like this:

class SecretData < ActiveRecord::Base
  mysecret = "mylittlesecret"

  attr_encrypted :data1, :key=>mysecret, :algorithm => "aes-256-cbc"
  attr_encrypted :data2, :key=>mysecret, :algorithm => "aes-256-cbc"

  ...
end

The DB fields (encrypted_data1 and encrypted_data2) are filled with data but when I try to decode the base64 (attr_encrypted does that by default) and decrypt (I tried with openssl from commandline and using Java) I get "bad magic number" (openssl) or various errors about key length (in Java). I spent a lot of time trying to decrypt those strings but just couldn't find the way.

Here is all the data I have:
encrypted + base64 strings (for data1 and data2) are:

cyE3jDkKc99GVB8TiUlBxQ==
sqcbOnBTl6yy3wwjkl0qhA==

I can decode base64 from both of them and get some byte array. When I try:

echo cyE3jDkKc99GVB8TiUlBxQ== | openssl aes-256-cbc -a -d   (and type "mylittlesecret" as the password)

I get: "bad magic number"

When I try the following Java code:

Key key = generateKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.DECRYPT_MODE, key);
byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedData);
byte[] decValue = c.doFinal(decordedValue);
String decryptedValue = new String(decValue);

I get "java.security.InvalidKeyException: Invalid AES key length: 14 bytes"
I've tried many variations for the Java code, so it might be that this particular one is a complete mistake..

When I try in ruby:

irb(main):069:0> Encryptor.decrypt(Base64.decode64("cyE3jDkKc99GVB8TiUlBxQ=="), ,key=>'mylittlesecret')
=> "data1-value"

I get the correct value decrypted (as you can see).

I've also noticed that when I try to encrypt the same string in Java and encode in Base64 I get a longer string (after base64). Don't know why but it's probably related..

I thought I should also have a salt/iv with the encrypted value, but I don't see it stored anywhere.. I tried to encrypt the same value twice and got the same output string so it's not a random one.

Does anyone know how does attr_encrypted (it's using ruby's Encryptor) encrypts data and how I should decrypt it externally?

解决方案

Well, thanks to owlstead I was able to solve this. I'm posting the code in ruby and Java in case someone needs it in the future:

The problem, as owlstead mentioned, is indeed in the EVP_BytesToKey (key generation from a password and salt). Ruby from some reason doesn't use the standard one and therefore Java (or openssl) can't decode.

Here is a ruby implementation that uses a standard method:

def self.encrypt(options)

   plaintext = options[:value]
   return true if plaintext.blank?

   cipher = OpenSSL::Cipher::Cipher.new(@@cipher_type)
   cipher.encrypt

   iv = cipher.random_iv
   salt = (0 ... @@salt_length).map{65.+(rand(25)).chr}.join   # random salt
   key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(@@password, salt, @@pkbdf_num_iters, cipher.key_len)

   cipher.key = key
   cipher.iv = iv

   enc_data = cipher.update(plaintext)
   enc_data << cipher.final

   final_data = salt << iv << enc_data
   Base64.strict_encode64(final_data)
end

def self.decrypt(options)

   ciphertext = options[:value]
   return true if ciphertext.blank?


   cipher = OpenSSL::Cipher::Cipher.new(@@cipher_type)
   cipher.decrypt

   cipher_data = Base64.decode64(ciphertext)

   salt = cipher_data[0 .. @@salt_length-1]
   iv = cipher_data[@@salt_length .. @@salt_length+cipher.iv_len]
   enc_data = cipher_data[@@salt_length+cipher.iv_len .. -1]  # the rest

   key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(@@password, salt, @@pkbdf_num_iters, cipher.key_len)

   cipher.key = key
   cipher.iv = iv

   plaintext = cipher.update(enc_data)
   plaintext << cipher.final

   plaintext
  end

I've set the following parameters: - cipher_type = aes-128-cbc (Java supports only 128 but out of the box. For more than that you need to install some additional packages) - salt_length = 8 - pkbdf_num_iters = 1024

This is the Java method for decoding:

public String decrypt(String ciphertext) throws Exception {
    byte[] crypt = Base64.decodeBase64(ciphertext);

    // parse the encrypted data and get salt and IV
    byte[] salt = Arrays.copyOfRange(crypt, 0, saltLength);
    byte[] iv = Arrays.copyOfRange(crypt, saltLength, saltLength + ivLength);
    byte[] encryptedData = Arrays.copyOfRange(crypt, saltLength + ivLength, crypt.length);

    // generate key from salt and password  
    SecretKeyFactory f = SecretKeyFactory.getInstance(secretKeyName);
    KeySpec ks = new PBEKeySpec(password.toCharArray(), salt, pbkdfNumIters, keyLength);
    SecretKey s = f.generateSecret(ks);
    Key keySpec = new SecretKeySpec(s.getEncoded(),"AES");

    // initialize the cipher object with the key and IV
    Cipher cipher = Cipher.getInstance(cipherAlgo);
    IvParameterSpec ivSpec = new IvParameterSpec(iv);
    cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

    // decrypt
    byte[] decBytes = cipher.doFinal(encryptedData);

    return new String(decBytes);
}

Worked for me.

Hope it helps (or will, to someone..)

Zach

这篇关于试图解密来自Java的attr_encrypted存储值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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