Я пишу приложение для Android, которое выполняет шифрование / дешифрование файлов AES. Я хочу иметь возможность определять, указан ли неправильный пароль и, следовательно, для расшифровки получен несоответствующий ключ. Я использую AES / CBC / PKCS7Padding с 256-битным ключом. Если я сделаю cipher.doFinal (), я могу попробовать / поймать BadPaddingException, и он говорит мне, что что-то не так и, вероятно, ключ был неправильным. Но если я использую CipherInputStream для чтения зашифрованного файла, я не получаю отзывов о правильности заполнения. Поэтому, если я намеренно указываю неверный пароль, он расшифровывает файл, а затем сообщает, что все в порядке, однако расшифрованный файл является полным мусором. Итак, мой вопрос: как определить плохое заполнение при использовании CipherInputStream?
Расшифровка Java AES обнаруживает неверный ключ
Ответы (6)
Добавьте к своим данным какой-нибудь известный заголовок. Сначала расшифруйте его, и если он не соответствует тому, что вы ожидали, остановите и верните ошибку.
Попробуйте вместо этого использовать режим GCM (поставщик Java 7 или Bouncy Castle). Уловка с заполнением заключается в том, что иногда оно оказывается правильным после того, как сообщение было изменено (примерно раз в 256). В режиме GCM будет добавлена защита целостности, поэтому любое изменение приведет к исключению, производному от BadPaddingException.
Одна вещь: вы должны добавить (случайный) одноразовый номер при шифровании с помощью GCM (на самом деле это правило также и для режима CBC, но последствия использования неслучайного IV в CBC менее серьезны).
Обратите внимание, что вам нужно выполнить окончательный расчет, чтобы получить исключение badpaddingexception, поэтому не забудьте закрыть или завершить базовый поток. Вероятно, это ваша текущая проблема.
[РЕДАКТИРОВАТЬ]: это не ответ, но ввод можно использовать для создания лучшего CipherInputStream
, см. Мой другой ответ по этому вопросу.
CipherInputStream
проводит это исключение под таблицей (что для меня пахнет довольно плохим дизайном), посмотрите на мой другой ответ.
- person Maarten Bodewes; 22.07.2012
Я думаю, что плохой padding по какой-то причине пойман, это из CipherInputStream
источника:
private int getMoreData() throws IOException {
if (done) return -1;
int readin = input.read(ibuffer);
if (readin == -1) {
done = true;
try {
obuffer = cipher.doFinal();
}
catch (IllegalBlockSizeException e) {obuffer = null;}
catch (BadPaddingException e) {obuffer = null;}
if (obuffer == null)
return -1;
else {
ostart = 0;
ofinish = obuffer.length;
return ofinish;
}
}
try {
obuffer = cipher.update(ibuffer, 0, readin);
} catch (IllegalStateException e) {obuffer = null;};
ostart = 0;
if (obuffer == null)
ofinish = 0;
else ofinish = obuffer.length;
return ofinish;
}
BadPaddingException
. Может быть, возьмите CipherInputStream и перепишите его - вместо этого оберните BadPaddingException в IOException, чтобы выполнить контракт.
- person Maarten Bodewes; 21.07.2012
Вот модифицированная версия метода getMoreData () в CipherInputStream, она может быть полезна тем, кто столкнулся с моей проблемой:
private int getMoreData() throws IOException {
if (done) return -1;
int readin = input.read(ibuffer);
if (readin == -1) {
done = true;
try {
obuffer = cipher.doFinal();
}
catch (IllegalBlockSizeException e) {
throw new IOException(e);
}
catch (BadPaddingException e) {
throw new IOException(e);
}
if (obuffer == null)
return -1;
else {
ostart = 0;
ofinish = obuffer.length;
return ofinish;
}
}
try {
obuffer = cipher.update(ibuffer, 0, readin);
} catch (IllegalStateException e) {obuffer = null;};
ostart = 0;
if (obuffer == null)
ofinish = 0;
else ofinish = obuffer.length;
return ofinish;
}
У меня была такая же проблема, как узнать, является ли ключ, используемый для шифрования, тем же, что использовался для дешифрования, потому что в моем случае я мог расшифровать строки, но он вернул некоторый мусор, и мне нужно знать зашифрованную строку (ее случайную) чтобы получить правильное значение.
Итак, что я сделал:
Расшифруйте зашифрованную строку.
Зашифруйте строку еще раз, используя правильный ключ.
Расшифруйте предыдущую зашифрованную строку.
Сопоставьте, если исходный расшифрованный ключ равен новому расшифрованному ключу.
Cipher c = Cipher.getInstance(algorithm);
c.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] decValue = c.doFinal(encryptedData.getBytes());
decryptedValue = new String(decValue,"UTF-8");
//now i have the string decrypted in decryptedValue
byte[] encryptAgain = encrypt(decryptedValue);
String encryptAgaindecripted = new String(c.doFinal(encryptAgain),"UTF-8");
//if keys match then it uses the same key and string is valid
if (decryptedValue.equals(encryptAgaindecripted)){
//return valid
}
надеюсь, это кому-то поможет.
Я тоже столкнулся с этим. У меня был тест, который надежно терпел неудачу пару раз из тысячи запусков с AES / CBC / PKCS5Padding, который поддерживается в Java.
Чтобы исправить это, вы можете сделать, как было предложено выше, и использовать надувной замок.
Однако я сделал другое исправление и просто добавил хэш содержимого md5 к простому тексту перед шифрованием, который я проверяю при расшифровке. Просто добавьте содержимое к хешу md5 и при расшифровке возьмите первые 22 символа хеша md5 и убедитесь, что остальная часть строки имеет тот же хеш, и вызовите исключение, если это не так, или верните открытый текст (без хеша md5) если он совпадает. Это будет работать независимо от алгоритма шифрования. Вероятно, в режиме GCM это действительно не нужно. В любом случае, таким образом вы сможете избежать дополнительных зависимостей от надувного замка.