1

モデルの1つで2つのフィールドを(attr_encryptedで)暗号化するRailsアプリケーションがあります。

Webアプリケーションではない私のプロセスの別の部分は、このデータ(プレーンテキスト)を使用していくつかのタスクを実行する必要があります。

DBから保存された値を読み取って復号化しようとしていますが、できません。

私のモデルは次のようになります。

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

DBフィールド(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   (and type "mylittlesecret" as the password)

「悪いマジックナンバー」

次の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コードのさまざまなバリエーションを試したので、この特定のバリエーションは完全な間違いである可能性があります。

私がルビーで試してみると:

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

正しい値が復号化されます(ご覧のとおり)。

また、Javaで同じ文字列を暗号化し、Base64でエンコードしようとすると、より長い文字列(base64の後)が得られることにも気づきました。理由はわかりませんが、おそらく関連しています。

暗号化された値を持つsalt/ivも必要だと思いましたが、どこにも保存されていません。同じ値を2回暗号化しようとしましたが、同じ出力文字列を取得したため、ランダムではありません。

attr_encrypted(rubyのEncryptorを使用)がデータを暗号化する方法と、データを外部で復号化する方法を知っている人はいますか?

4

2 に答える 2

2

さて、owlsteadのおかげで私はこれを解決することができました。誰かが将来それを必要とする場合に備えて、私はルビーとジャワでコードを投稿しています:

owlsteadが述べたように、問題は確かにEVP_BytesToKey(パスワードとソルトからのキー生成)にあります。何らかの理由で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);
}

私のために働いた。

それが役立つことを願っています(または誰かに役立つでしょう..)

ザック

于 2012-10-02T13:06:16.193 に答える
1

-nosaltOpenSSLを使用してデータを復号化する必要があります。EVP_BytesToKeyJavaの場合、OpenSSLメソッドの実装が必要になります。1つの実装は、OlaBiniのブログにあります。これをパブリックドメインに入れてくれてありがとう、オラ。

    public static byte[][] EVP_BytesToKey(int key_len, int iv_len, MessageDigest md,
            byte[] salt, byte[] data, int count) {
        byte[][] both = new byte[2][];
        byte[] key = new byte[key_len];
        int key_ix = 0;
        byte[] iv = new byte[iv_len];
        int iv_ix = 0;
        both[0] = key;
        both[1] = iv;
        byte[] md_buf = null;
        int nkey = key_len;
        int niv = iv_len;
        int i = 0;
        if (data == null) {
            return both;
        }
        int addmd = 0;
        for (;;) {
            md.reset();
            if (addmd++ > 0) {
                md.update(md_buf);
            }
            md.update(data);
            if (null != salt) {
                md.update(salt, 0, 8);
            }
            md_buf = md.digest();
            for (i = 1; i < count; i++) {
                md.reset();
                md.update(md_buf);
                md_buf = md.digest();
            }
            i = 0;
            if (nkey > 0) {
                for (;;) {
                    if (nkey == 0)
                        break;
                    if (i == md_buf.length)
                        break;
                    key[key_ix++] = md_buf[i];
                    nkey--;
                    i++;
                }
            }
            if (niv > 0 && i != md_buf.length) {
                for (;;) {
                    if (niv == 0)
                        break;
                    if (i == md_buf.length)
                        break;
                    iv[iv_ix++] = md_buf[i];
                    niv--;
                    i++;
                }
            }
            if (nkey == 0 && niv == 0) {
                break;
            }
        }
        for (i = 0; i < md_buf.length; i++) {
            md_buf[i] = 0;
        }
        return both;
    }
于 2012-09-24T18:53:14.993 に答える