java - 试图从 Java 解密 attr_encrypted 存储值

标签 java ruby-on-rails ruby encryption attr-encrypted

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

我的流程的另一部分,不是网络应用程序,需要使用此数据(明文)执行一些任务。

我正在尝试从数据库中读取存储的值并解密它们,但就是不能......

我的模型是这样的:

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

数据库字段(encrypted_data1 和 encrypted_data2)充满了数据,但是当我尝试解码 base64(attr_encrypted 默认情况下这样做)和解密(我尝试从命令行使用 openssl 并使用 Java)时,我得到“错误的魔数(Magic Number)” (openssl) 或有关 key 长度的各种错误(在 Java 中)。我花了很多时间试图解密这些字符串,但就是找不到方法。

这是我所有的数据:
加密 + base64 字符串(对于 data1 和 data2)是:

cyE3jDkKc99GVB8TiUlBxQ==
sqcbOnBTl6yy3wwjkl0qhA==

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

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

我得到:“错误的魔数(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 key 长度:14 字节”
我已经为 Java 代码尝试了许多变体,所以这个特定的变体可能是一个完全错误的..

当我在 ruby​​ 中尝试时:

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

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

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

我想我也应该有一个带有加密值的 salt/iv,但我没有看到它存储在任何地方。我尝试对相同的值加密两次并得到相同的输出字符串,所以它不是随机的。

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

最佳答案

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

正如 owlstead 提到的,问题确实出在 EVP_BytesToKey(从密码和 salt 生成 key )中。由于某种原因,Ruby 不使用标准的,因此 Java(或 openssl)无法解码。

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

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

我设置了以下参数: - 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);

    // 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);
}

为我工作。

希望它能帮助(或将对某人......)

扎克

关于java - 试图从 Java 解密 attr_encrypted 存储值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12568581/

相关文章:

ruby-on-rails - number_to_currency 问题 100{ :separator= >",", :delimiter= >".", :raise=>true}000

ruby-on-rails - 通过 GET 发送表单时如何忽略某些参数?

ruby-on-rails - 如何为门卫的 skip_authorization block 指定 super 应用列表?

ruby-on-rails - heroku 中的 wkhtmltopdf : memory issues

java 用子列表替换列表

java - 在 Java 1.5 中关闭 Swing JTabbedPane 选项卡?

javascript - 未命中 Javascript/JQuery 中的调试器

java - Java 中的 SortedSet 迭代

java - Android 上使用最广泛的提要解析库是什么

ruby-on-rails - 防止单引号在 .ERB 中的 image_tag onClick 中被转义