2013-04-30 12 views
7

Sto scrivendo un server applicazioni e ho deciso di utilizzare AES128/CTR/NoPadding per proteggere le connessioni, poiché è considerato abbastanza sicuro senza dover espandere i byte al blocco confine e ho pensato che sia una buona misura per TCP che è logicamente un flusso continuo.Simulazione di un codice di flusso con AES/CTR

Il problema è che Cipher.update() non restituisce il blocco crittografato fino a quando non ha un blocco completo di 16 byte poiché il CTR è fondamentalmente basato su un codice a blocchi tramite simulazione di un codice di flusso. Dovrei leggere i dati da un socket TCP ed elaborare i messaggi non appena arrivano, ma non riesco a recuperare il blocco più recente perché si sta ancora accumulando e la sua dimensione è inferiore a 16 byte. E non posso semplicemente aspettare perché non sappiamo quando verrà inviato il prossimo messaggio. Ovviamente potrei chiamare Cipher.doFinal() per ottenere il rimanente, ma ciò significherebbe la fine del flusso (connessione) e l'oggetto Cipher verrà reinizializzato.

Ho pensato che sarebbe bello se c'è un modo per sbirciare il riporto. CTR semplicemente XORs il testo normale con il keystream quindi dovrei essere in grado di ottenere i dati crittografati indipendentemente dal resto dei byte nel blocco. Ci sarebbe una soluzione alternativa a questo problema? Sto pensando di scrivere un wrapper che crittografa il testo in chiaro con zero per ottenere il keytream in anticipo e gli XOR manualmente, ma mi chiedo come gli altri abbiano risolto questo problema.

Aggiornamento

Sto sviluppando un'applicazione Android e si è scoperto che questo è il problema della Dalvik VM. Come hanno sottolineato Robert e Monnand, Java SE non presenta questo problema almeno con il provider predefinito. Penso che dovrò scrivere una classe wrapper o cambiare la modalità in CFB8 per ovviare a questo problema. (CTR8 non ha funzionato) Grazie per tutte le risposte!

+0

È possibile eseguire il rilievo dei messaggi in modo che la loro lunghezza sia un multiplo di 16? Questo assicurerebbe che tu finisca sempre con un pezzo decifrabile. –

+0

@Chris: Grazie per il suggerimento, potrebbe essere una soluzione possibile. Ma vorrei evitare questo, se possibile, perché non è preferibile in termini di efficienza della rete, anche se potrebbe essere trascurabile. –

+0

Si può semplicemente chiamare questo su un array costituito da zeri per ottenere il keystream. Quindi eseguilo manualmente nel tuo messaggio. – CodesInChaos

risposta

6

Ho appena testato AES in modalità CTR utilizzando Oracle Java 1.7 e non riesco a verificare il tuo osservazioni:

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 

può essere che si utilizza un'implementazione difetto di terze parti perché "AES128/CTR/NoPadding" non è un codice noto sul mio sistema.

+0

Hai ragione, era il problema di Android Dalvik VM, non di Java SE. –

0

Non ho dovuto risolvere questo problema da solo, ma una soluzione alternativa sarebbe quella di produrre manualmente il flusso di chiavi e gestire lo XORing da solo.

Che è, si potrebbe passare da AES/CTR/NoPadding a AES/ECB/NoPadding e ripetutamente crittografare il incrementing counter value, ogni volta che avete bisogno di dati freschi a XOR con il testo cifrato.

Lontano dall'ideale, ma suppongo che funzionerebbe.

+2

Grazie per il suggerimento. Penso di poter usare CTR con un array di zeri per ottenere il keystream. –

4

Ho avuto esattamente lo stesso problema oggi e l'ho risolto subito.

Il problema è il provider, che è probabilmente Bouncy Castle. Quando chiami getInstance(), fornisci semplicemente il nome dell'algoritmo (che nel mio caso è "AES/CTR/NoPadding"). NON specificare specificare il provider.

Lasciate che il codice di spiegare se stesso:

Come ha detto @ Robert, il seguente codice funziona correttamente:

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 

Tuttavia, se si specifica il provider come "BC", invece, sarà sbagliato:

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 

Potrebbe essere considerato un errore di Bouncy Castle o una specie di (strana ma ragionevole) funzionalità.

+1

Nel mio caso Android Dalvik era il problema, ma grazie per aver sottolineato che anche un provider personalizzato ha lo stesso problema. –

+0

Grazie per avermi fatto sapere che Android ha lo stesso problema (funzionalità?). Questo è fastidioso dal momento che un vantaggio della modalità contatore è che può essere utilizzato senza padding. – monnand

+0

So che questa domanda è stata intorno un po '... Il problema è ancora presente in una moderna fonte BC? – jww