Пытается расшифровать сохраненное значение attr_encrypted из Java
У меня есть приложение 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), я получаю "плохое магическое число" (openssl) или различные ошибки о длине ключа (в Java). Я потратил много времени, пытаясь расшифровать эти строки, но просто не мог найти дорогу.
Вот все данные, которые у меня есть:
Зашифрованные строки + base64 (для данных1 и данных2):
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, так что, возможно, именно этот вариант является полной ошибкой.
Когда я пытаюсь в ruby:
irb(main):069:0> Encryptor.decrypt(Base64.decode64("cyE3jDkKc99GVB8TiUlBxQ=="), ,key=>'mylittlesecret')
=> "data1-value"
Я получаю правильное значение расшифровано (как вы можете видеть).
Я также заметил, что когда я пытаюсь зашифровать ту же строку в Java и кодировать в Base64, я получаю более длинную строку (после base64). Не знаю почему, но это, вероятно, связано..
Я думал, что у меня также должна быть соль / iv с зашифрованным значением, но я не вижу, чтобы он хранился нигде.. Я пытался зашифровать одно и то же значение дважды и получил одну и ту же строку вывода, поэтому она не случайная.
Кто-нибудь знает, как attr_encrypted (он использует ruby's Encryptor) шифрует данные и как мне следует расшифровать их внешне?
2 ответа
Ну, благодаря Owlstead я смог решить это. Я публикую код на ruby и Java на случай, если кому-то понадобится в будущем:
Проблема, как отметил Олстед, действительно заключается в 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);
}
Работал на меня.
Надеюсь, это поможет (или будет, кому-то..)
Zach
Тебе понадобится -nosalt
расшифровать данные с помощью OpenSSL. Для Java вам понадобится реализация OpenSSL EVP_BytesToKey
метод. Одна реализация может быть найдена в блоге Олы Бини. Спасибо, что выложили это в общественное достояние, Ола.
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;
}