Не удается расшифровать файл, зашифрованный с помощью openssl AES_ctr128_encrypt - PullRequest
1 голос
/ 20 июля 2011

У меня есть файл, зашифрованный с использованием следующего кода в c:

unsigned char ckey[] =  "0123456789ABCDEF"; 
unsigned char iv[8] = {0};
AES_set_encrypt_key(ckey, 128, &key);
AES_ctr128_encrypt(indata, outdata, 16, &key, aesstate.ivec, aesstate.ecount, &aesstate.num);

Я должен расшифровать этот файл, используя Java, поэтому я использовал код ниже, чтобы сделать это:

private static final byte[] encryptionKey = new byte[]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };

byte[] iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

IvParameterSpec ips = new IvParameterSpec(iv);
Cipher aesCipher = Cipher.getInstance("AES/CTR/NoPadding");
SecretKeySpec aeskeySpec = new SecretKeySpec(encryptionKey, "AES");
aesCipher.init(Cipher.DECRYPT_MODE, aeskeySpec, ips);
FileInputStream is = new FileInputStream(in);
CipherOutputStream os = new CipherOutputStream(new FileOutputStream(out), aesCipher);       
copy(is, os);       
os.close();

Код JAVA не дает мне никакой ошибки, но вывод неправильный.

Что я делаю не так?

Мои главные сомнения в том, правильно ли я использую заполнение (также безуспешно пробовал PKCS5Padding) и правильны ли ключ и iv (не знаючто действительно делает функция AES_set_encrypt_key ...).

** РЕДАКТИРОВАТЬ **

Я думаю, что у меня есть ответ на мой собственный вопрос, но у меня все еще есть некоторые сомнения.

CTR означает режим счетчика.Функция AES_ctr128_encrypt получает в качестве параметров фактический счетчик (ecount) и количество используемых блоков (num).

Файл шифруется блоками по 16 байт, например:

for(int i = 0; i < length; i+=16)
{
   // .. buffer processing here
   init_ctr(&aesstate, iv); //Counter call
   AES_ctr128_encrypt(indata, outdata, 16, &key, aesstate.ivec, aesstate.ecount, &aesstate.num);
}

функция init_ctr делает это:

int init_ctr(struct ctr_state *state, const unsigned char iv[8])
{
    state->num = 0;
    memset(state->ecount, 0, 16);
    memset(state->ivec + 8, 0, 8);
    memcpy(state->ivec, iv, 8);
    return 0;
}

Это означает, что перед каждым шифрованием / дешифрованием код С сбрасывает счетчик и ivec.

Я пытаюсь расшифровать файл какцелое в яве.Это, вероятно, означает, что Java использует счетчик правильно, но код C не так, как он сбрасывает счетчик в каждом блоке.

Правильно ли мое расследование?

У меня нет абсолютно никакого контроля над Cкод, который вызывает openssl.Есть ли способ сделать то же самое в JAVA, то есть сброс счетчика в каждом блоке из 16?(API запрашивает только ключ, алгоритм, режим и IV)

Мой единственный другой вариант - использовать openssl через JNI, но я пытался этого избежать ...

Спасибо!

Ответы [ 3 ]

4 голосов
/ 21 июля 2011

Я не пробовал, но вы должны быть в состоянии эффективно эмулировать то, что там делается на стороне C - расшифровывать каждый 16-байтовый (= 128-битный) блок отдельно и перезагружать шифр между двумя вызовами.


Обратите внимание, что использование режима CTR только для одного блока с нулевым вектором инициализации и счетчиком побеждает цель режима CTR - это хуже, чем ECB .

Если я правильно понял, вы можете попытаться зашифровать несколько блоков нулей с помощью вашей функции C (или эквивалентной версии Java) - каждый раз они должны появляться как один и тот же блок.XOR этот блок с любым зашифрованным текстом, чтобы вернуть ваш открытый текст.

Это эквивалентно шифру Цезаря на 128-битном алфавите (например, 16-байтовых блоках), блочный шифр не добавляет здесь никакой защиты простой 128-битный XOR-шифр .Если угадать один блок открытого текста (или, в более общем смысле, угадать 128 бит в правильных позициях, не обязательно все в одном и том же блоке), можно получить эффективный ключ, который позволяет получить все оставшиеся блоки открытого текста.

2 голосов
/ 20 июля 2011

Ваши ключи шифрования разные.

Код C использует коды символов ASCII для 0 до F, тогда как Javacode использует действительные байты 0 до 16.

0 голосов
/ 28 июля 2011

Есть много серьезных проблем с этим кодом C:

  • Как уже отмечалось, повторная инициализация счетчика в каждом блоке. Это делает шифрование совершенно небезопасным. Это можно исправить, вызвав init_ctr() только один раз перед шифрованием первого блока.
  • Статически устанавливает IV на ноль. Свежий IV должен генерироваться случайным образом, например if (!RAND_bytes(iv, 8)) { /* handle error */ }.
  • Код, по-видимому, напрямую использует строку пароля в качестве ключа. Вместо этого ключ должен быть сгенерирован из пароля с использованием функции получения ключа, такой как PBKDF2 (реализована в OpenSSL PKCS5_PBKDF2_HMAC_SHA1()).
...