2013-04-30 13 views
7

Piszę serwer aplikacji i zdecydowałem się użyć AES128/CTR/NoPadding do zabezpieczenia połączeń, ponieważ jest uważany za wystarczająco bezpieczny bez konieczności rozszerzania bajtów do bloku Granica i pomyślałem, że jest to dobre dopasowanie do TCP, który logicznie jest płynnym strumieniem.Symulacja szyfru strumieniowego z AES/CTR

Problem polega na tym, że Cipher.update() nie zwraca zaszyfrowanego bloku, dopóki nie ma pełnego 16-bajtowego bloku, ponieważ CTR jest zasadniczo oparty na szyfrowaniu blokowym, chociaż symuluje szyfr strumienia. Powinienem czytać dane z gniazda TCP i przetwarzać komunikaty zaraz po ich dostarczeniu, ale nie mogę pobrać najnowszego bloku, ponieważ wciąż się buduje, a jego rozmiar jest mniejszy niż 16 bajtów. I nie mogę po prostu czekać, ponieważ nie wiemy, kiedy zostanie wysłana następna wiadomość. Oczywiście mógłbym wywołać Cipher.doFinal(), aby uzyskać resztki, ale to oznaczałoby koniec strumienia (połączenia) i obiekt Cipher zostałby ponownie zainicjowany.

Pomyślałem, że byłoby miło, gdyby można było zajrzeć do przeniesienia. CTR po prostu XORs zwykły tekst ze strumieniem klucza, więc powinienem być w stanie uzyskać zaszyfrowane dane bez względu na resztę bajtów w bloku. Czy byłby to dobry sposób obejścia tego problemu? Zastanawiam się nad napisaniem owijki, która szyfruje fałszywy zwykły tekst z zerami, aby uzyskać strumień kluczy z wyprzedzeniem i XOR ręcznie, ale zastanawiam się, jak inni rozwiązali ten problem.

Aktualizacja

Zajmuję się tworzeniem aplikacji na Androida i okazało się, że jest to problem Dalvik VM. Jak zauważyli Robert i Monnand poniżej, Java SE nie ma tego problemu przynajmniej z domyślnym dostawcą. Myślę, że będę musiał napisać klasę opakowania lub zmienić tryb na CFB8, aby obejść ten problem. (CTR8 nie zadziałało) Dzięki za wszystkie odpowiedzi!

+0

Czy można podkładać wiadomości tak, aby ich długość była wielokrotnością liczby 16? Zapewni to, że zawsze otrzymasz odszyfrowany fragment. –

+0

@Chris: Dzięki za sugestię, to może być możliwe obejście problemu. Ale chciałbym tego uniknąć, jeśli to możliwe, ponieważ nie jest to lepsze z punktu widzenia wydajności sieci, chociaż może być pomijalne. –

+0

Możesz po prostu wywołać to na tablicy składającej się z zer, aby uzyskać strumień klucza. Następnie lub ręcznie lub do wiadomości. – CodesInChaos

Odpowiedz

6

właśnie testowane AES w trybie CTR użyciu Oracle Java 1.7 i nie mogę zweryfikować swoje obserwacje:

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 

Może użyć realizacji wada osób trzecich, ponieważ „AES128/CTR/NoPadding” nie jest znany szyfr w moim systemie.

+0

Masz rację, to był problem z Androidem Dalvik VM, a nie z Java SE. –

0

Ja sam nie musiałem rozwiązywać tego problemu, ale jednym z rozwiązań byłoby ręczne wygenerowanie strumienia klucza i samodzielne obsłużenie XORing.

Oznacza to, że należy przełączyć z AES/CTR/NoPadding na AES/ECB/NoPadding i wielokrotnie szyfrować swoją incrementing counter value, gdy potrzebujesz nowych danych do XOR z zaszyfrowanym tekstem.

Daleko od ideału, ale przypuszczam, że zadziała.

+2

Dzięki za sugestię. Myślę, że mogę użyć CTR z tablicą zer, aby uzyskać strumień klucza. –

4

Miałem dokładnie ten sam problem i już go naprawiłem.

Problem stanowi Twój dostawca, prawdopodobnie Bouncy Castle. Kiedy zadzwonisz pod numer getInstance(), podaj nazwę swojego algorytmu (w moim przypadku "AES/CTR/NoPadding"). DO NOT podać dostawcę.

Niech kod wyjaśnić sobie:

Jak powiedział @Robert poniższy kod działa poprawnie:

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 

Jeśli jednak określić dostawcę jako „BC”, a nie, to będzie źle:

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 

Może to być uznane za błąd Bouncy Castle, lub rodzaj (dziwne, ale uzasadnione) funkcji.

+1

W moim przypadku problemem był Android Dalvik, ale dziękuję za to, że niestandardowy dostawca ma ten sam problem. –

+0

Dziękujemy za poinformowanie mnie, że Android ma ten sam problem (funkcja?). Jest to denerwujące, ponieważ zaletą trybu licznika jest to, że można go używać bez wypełnienia. – monnand

+0

Wiem, że to pytanie było już za chwilę ... Czy problem nadal występuje w nowoczesnych źródłach BC? – jww