Симулиране на поточен шифър с AES/CTR

Пиша сървър за приложения и реших да използвам AES128/CTR/NoPadding за защита на връзките, тъй като се счита за достатъчно сигурен, без да се налага да разширявам байтовете до границата на блока и реших, че е подходящ за TCP, който логично е безпроблемен поток.

Проблемът е, че Cipher.update() не връща шифрования блок, докато няма пълен 16-байтов блок, тъй като CTR основно се основава на блоков шифър, но симулира поточен шифър. Трябва да чета данни от tcp сокет и да обработвам съобщения веднага щом пристигнат, но не мога да извлека най-новия блок, защото той все още се натрупва и размерът му е по-малък от 16 байта. И не мога просто да чакам, защото не знаем кога ще бъде изпратено следващото съобщение. Разбира се, бих могъл да извикам Cipher.doFinal(), за да получа остатъка, но това би означавало края на потока (връзката) и обектът Cipher ще бъде повторно инициализиран.

Мислех, че би било хубаво, ако има начин да надникна в преноса. CTR просто извършва XOR обикновения текст с ключовия поток, така че трябва да мога да получа криптираните данни независимо от останалите байтове в блока. Ще има ли добро решение на този проблем? Мисля да напиша обвивка, която криптира фалшив обикновен текст с нули, за да получи предварително ключовия поток и XOR ръчно, но се чудя как други хора са решили този проблем.

Актуализация

Разработвам приложение за Android и се оказа, че това е проблемът на Dalvik VM. Както Робърт и Монанд посочиха по-долу, Java SE няма този проблем поне с доставчика по подразбиране. Мисля, че ще трябва да напиша клас обвивка или да променя режима на CFB8, за да заобиколя този проблем. (CTR8 не работи) Благодаря за всички отговори!


person K J    schedule 30.04.2013    source източник
comment
Можете ли да допълвате съобщения така, че дължината им да е кратна на 16? Това ще гарантира, че винаги ще получите дешифрируема част.   -  person Chris Heald    schedule 30.04.2013
comment
@Chris: Благодаря за предложението, това може да е възможно заобиколно решение. Но бих искал да избегна това, ако е възможно, защото не е за предпочитане по отношение на ефективността на мрежата, въпреки че може да е незначително.   -  person K J    schedule 30.04.2013
comment
Можете просто да извикате това на масив, състоящ се от нули, за да получите ключовия поток. След това го xor ръчно във вашето съобщение.   -  person CodesInChaos    schedule 30.04.2013
comment
Java SE няма този проблем, тъй като изобщо не поддържа AES128/CTR/NoPadding, поне с доставчика по подразбиране. Току-що опитах Java 7 Oracle (но може би инсталацията ми се е объркала и използвам OpenJDK, всичко е възможно).   -  person maaartinus    schedule 13.10.2014


Отговори (3)


Току-що тествах AES в режим CTR с помощта на Oracle Java 1.7 и не мога да проверя вашите наблюдения:

Cipher c = Cipher.getInstance("AES/CTR/NoPadding");
KeyGenerator kg = KeyGenerator.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, kg.generateKey());
System.out.println(c.update(new byte[1]).length);  // output: 1
System.out.println(c.update(new byte[20]).length); // output: 20

Възможно е да използвате дефектна реализация на трета страна, защото „AES128/CTR/NoPadding“ не е известен шифър в моята система.

person Robert    schedule 30.04.2013
comment
Прав си, проблемът беше на Android Dalvik VM, а не на Java SE. - person K J; 01.05.2013

Имах точно същия проблем днес и го оправих току що.

Проблемът е вашият доставчик, който вероятно е Bouncy Castle. Когато извиквате getInstance(), просто въведете името на вашия алгоритъм (което в моя случай е „AES/CTR/NoPadding“). НЕ посочвайте доставчика.

Нека кодът се обясни сам:

Както каза @Robert, следният код работи правилно:

Cipher c = Cipher.getInstance("AES/CTR/NoPadding");
KeyGenerator kg = KeyGenerator.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, kg.generateKey());
System.out.println(c.update(new byte[1]).length);  // output: 1
System.out.println(c.update(new byte[20]).length); // output: 20

Въпреки това, ако вместо това посочите доставчика като "BC", това ще бъде грешно:

Cipher c = Cipher.getInstance("AES/CTR/NoPadding", "BC");
KeyGenerator kg = KeyGenerator.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, kg.generateKey());
System.out.println(c.update(new byte[20]).length); // output: 16
System.out.println(c.update(new byte[1]).length);  // null pointer exception

Може да се счита за грешка на Bouncy Castle или вид (странна, но разумна) функция.

person monnand    schedule 30.04.2013
comment
В моя случай Android Dalvik беше проблемът, но благодаря, че посочихте, че персонализираният доставчик също има същия проблем. - person K J; 01.05.2013
comment
Благодаря, че ме уведомихте, че Android има същия проблем (функция?). Това е досадно, тъй като предимството на режима на брояча е, че може да се използва без подложка. - person monnand; 07.05.2013
comment
Знам, че този въпрос стои от известно време... Проблемът все още присъства ли в съвременните източници на пр. н. е.? - person jww; 18.01.2016

Не ми се е налагало сам да решавам този проблем, но едно заобиколно решение би било да произведа ключовия поток ръчно и сам да се справя с XORing.

Тоест бихте превключили от AES/CTR/NoPadding на AES/ECB/NoPadding и многократно ще шифровате своята увеличаваща се стойност на брояча, когато имате нужда от свежи данни към XOR с шифрования текст.

Далеч от идеалното, но предполагам, че ще работи.

person Duncan Jones    schedule 30.04.2013
comment
Благодаря за предложението Мисля, че мога да използвам CTR с масив от нули, за да получа ключовия поток. - person K J; 01.05.2013