Unità di test di crittografia e decrittografia in Java

Disclaimer, sono uno sviluppatore iOS che ha giocato con la crittografia su Android. Così com’è, sono riuscito a ottenere la crittografia in Android, ma mi chiedo come farebbe una unità di test per la crittografia e la decrittografia dei dati?

Ora la prima idea che viene in mente sarebbe qualcosa di simile:

String encryptedInputData = encryptedInputData("Hello"); String decryptedData = decryptData(encryptedInputData); Assert.assertEquals(decryptedData,"Hello"); 

Questo test tuttavia pone un difetto … Se qualcosa è cambiato nei metodi encryptedInputData e decryptData , questo test non dice cosa è cambiato e perché si sta rompendo. Quindi mi piacerebbe scrivere test molto più granulari. Ad esempio, dato questo codice:

 Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] data = cipher.doFinal(message); 

Mi piacerebbe essere sicuro che la variabile cipher stia usando l’algoritmo RSA in modalità ECB senza padding. Mi piacerebbe testare che il message in .doFinal(message) segua un particolare formato, ecc.

Ora immagino che sarei in grado di prendere in giro la class Cipher , il problema qui è che la crittografia e la decifratura che è stata scritta, serve solo come una class Util e per poterlo testare unitamente, dovrei passare il simulato Cipher nel codice, che dato che questa è una class Util sembra come se fosse disordinata, cioè dovrei creare un metodo init solo per scopi di testing unitario o creare metodi setter solo per testare l’unità. Il che mi permetterebbe di testare il codice unitario, ma poi la class Util diventa goffo con codice che in realtà non ho bisogno per scopi di produzione.

Esistono modi eleganti per essere in grado di testare scenari come questo? cioè encryptedInputData e decryptData sono metodi pubblici ma questi metodi usano vari metodi privati ​​che devono essere testati decryptData , quindi il problema è come?

La vera risposta è che non dovresti. Non dovresti mai implementare le tue routine di crittografia. Non solo è molto probabile che tu ti sbagli, ci sono cose estremamente complesse che devi fare per assicurarti che non sia effettivamente hackerabile a causa di problemi di implementazione (per esempio, se un ramo di un’istruzione if impiega più tempo di un altro , puoi capire qual è il valore del controllo). Dovresti sempre usare una libreria open source ben recensita.

Dal momento che non lo stai implementando da solo, non è necessario testarlo unitamente. Gli scrittori della biblioteca dovrebbero essere. Se ne hai voglia, esegui la loro suite di test come parte della tua, ma ritengo che sia una perdita di tempo – l’hanno fatto prima del rilascio, e hai solo bisogno di eseguirlo una volta al massimo.

Gabe ha ragione nella sua risposta sull’essere veramente conservatore quando reinventa la ruota; specialmente quando quella ruota riguarda la crittografia / sicurezza / … cose del genere. Le probabilità sono: ti sbagli. E gli hacker amano le persone che cercano di venire con la loro “crittografia sicura”.

Ma per rispondere alla tua domanda attuale: prova ad andare con TDD (sviluppo guidato da test). Il problema è che hai creato alcune API difficili da testare.

Hai ragione, vuoi quel test “end to end” come quello che fa in modo che decrypt (encrypt (“qualcosa”)) venga fornito con “qualcosa”. Ora il tuo problema è: se scrivi solo il tuo codice con questi due metodi in mente; quindi il test unitario è difficile.

Quindi: fin dall’inizio, quando consideri “quali classi ho bisogno”; e “quali metodi vanno in quale class” devi concentrarti su “e come posso testarlo”.

In altre parole: questa è una delle occasioni in cui TDD è molto importante – perché vuoi davvero progettare unità che possano essere testate. E la cosa migliore da fare è: scrivi prima i test. Se progetti le tue unità in modo che possano essere testate (perché prima hai scritto i test); sorpresa – possono essere testati. Rendendoli “testabili” dopo che il fatto è sempre macchinoso; e più spesso: quasi imansible.

Per quanto posso vedere, Cipher è nella class javax.crypto . Quindi non hai bisogno di deriderlo.

È ansible scrivere test unitari per verificare che: 1. La Key sia generata correttamente e soddisfi i requisiti. 2. CipherInputStream è stato creato correttamente. 3. Copiare testo su file e leggerlo dopo la decodifica corrisponde a:

 @Test public void streamEncryptionDecryptionTest() { try { File file = tempFolder.newFile("test.txt"); String content = "This is the text content"; System.out.println("Original content for file = " + content); OutputStream fop = new FileOutputStream(file); byte[] key = EncryptionHelper.createKey(); fop = EncryptionHelper.getInstance(key).getEncryptedOutputStream(fop); // if file doesn't exists, then create it if (!file.exists()) { file.createNewFile(); } // get the content in bytes byte[] contentInBytes = content.getBytes(); fop.write(contentInBytes); fop.flush(); fop.close(); System.out.println("Encryption Done"); { // Read content w/o decryption InputStream fis = new FileInputStream(file); System.out.println("Content w/o decryption"); int contentChars; while ((contentChars = fis.read()) != -1) { // convert to char and display it System.out.print((char) contentChars); } } { // Decrypted content InputStream fis = new FileInputStream(file); fis = EncryptionHelper.getInstance(key).getDecryptedInputStream(fis); System.out.println("Total file size to read (in bytes) : " + fis.available()); System.out.println("Decrypted content"); int contentChars; while ((contentChars = fis.read()) != -1) { // convert to char and display it System.out.print((char) contentChars); } } } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); Assert.assertTrue("security exception", false); } } 

Sebbene questo test non abbia un controllo esplicito delle asserzioni, puoi aggiungere:

 for (int i = 0; i < buffer.length; i++) { byte b = buffer[i]; System.out.print(b); decryptedContent[i] = b; } 

e quindi controlla gli array:

 Assert.assertArrayEquals(bytesEncoded, decryptedContent); 

Tieni presente che non devi confrontare gli array di byte convertendoli in string, poiché ti darà un'implementazione errata.

  1. Crittografia e decrittografia parziale del buffer funziona correttamente. A seconda delle esigenze.