2012-02-01 7 views
30

Ci sono un sacco di domande su questo argomento, la stessa soluzione, ma questo non funziona per me. Ho un semplice test con una crittografia. La crittografia/decodifica stessa funziona (finché gestisco questo test con l'array di byte stesso e non come Strings). Il problema è che non voglio gestirlo come array di byte ma come String, ma quando codifico l'array di byte in stringa e back, l'array di byte risultante differisce dall'array di byte originale, quindi la decrittografia non funziona più. Ho provato i seguenti parametri nei metodi stringa corrispondenti: UTF-8, UTF8, UTF-16, UTF8. Nessuno di loro funziona. La matrice di byte risultante differisce dall'originale. Qualche idea per cui è così?Problemi di conversione dell'array di byte in stringa e ritorno all'array di byte

Encrypter:

public class NewEncrypter 
{ 
    private String algorithm = "DESede"; 
    private Key key = null; 
    private Cipher cipher = null; 

    public NewEncrypter() throws NoSuchAlgorithmException, NoSuchPaddingException 
    { 
     key = KeyGenerator.getInstance(algorithm).generateKey(); 
     cipher = Cipher.getInstance(algorithm); 
    } 

    public byte[] encrypt(String input) throws Exception 
    { 
     cipher.init(Cipher.ENCRYPT_MODE, key); 
     byte[] inputBytes = input.getBytes("UTF-16"); 

     return cipher.doFinal(inputBytes); 
    } 

    public String decrypt(byte[] encryptionBytes) throws Exception 
    { 
     cipher.init(Cipher.DECRYPT_MODE, key); 
     byte[] recoveredBytes = cipher.doFinal(encryptionBytes); 
     String recovered = new String(recoveredBytes, "UTF-16"); 

     return recovered; 
    } 
} 

Questa è la prova in cui ho provato:

public class NewEncrypterTest 
{ 
    @Test 
    public void canEncryptAndDecrypt() throws Exception 
    { 
     String toEncrypt = "FOOBAR"; 

     NewEncrypter encrypter = new NewEncrypter(); 

     byte[] encryptedByteArray = encrypter.encrypt(toEncrypt); 
     System.out.println("encryptedByteArray:" + encryptedByteArray); 

     String decoded = new String(encryptedByteArray, "UTF-16"); 
     System.out.println("decoded:" + decoded); 

     byte[] encoded = decoded.getBytes("UTF-16"); 
     System.out.println("encoded:" + encoded); 

     String decryptedText = encrypter.decrypt(encoded); //Exception here 
     System.out.println("decryptedText:" + decryptedText); 

     assertEquals(toEncrypt, decryptedText); 
    } 
} 
+3

Prima è necessario convertire i byte in qualcosa che può essere presentato come una stringa. Di solito convertendo in hex o base64. –

+0

Qual è la differenza effettiva che si vede negli array di byte prima e dopo la conversione in una stringa? – Herms

+0

@Roger Lindsjö: grazie per il tipp. Lo proverò immediatamente. – Bevor

risposta

73

Non è una buona idea archiviare dati crittografati in stringhe perché sono per testo leggibile dall'uomo, non per dati binari arbitrari. Per i dati binari è meglio usare byte[].

Tuttavia, se necessario farlo è necessario utilizzare una codifica che ha una mappatura1-a-1 tra i byte e caratteri, cioè, dove ogni sequenza di byte può essere associata a una sequenza unica di caratteri , e ritorno. Una tale codifica è ISO-8859-1, vale a dire:

String decoded = new String(encryptedByteArray, "ISO-8859-1"); 
    System.out.println("decoded:" + decoded); 

    byte[] encoded = decoded.getBytes("ISO-8859-1"); 
    System.out.println("encoded:" + java.util.Arrays.toString(encoded)); 

    String decryptedText = encrypter.decrypt(encoded); 

Altre codifiche comuni che non perdono i dati sono esadecimale e Base64, ma purtroppo avete bisogno di un libreria di supporto per loro. L'API standard non definisce le classi per loro.

Con UTF-16 il programma sarebbe sicuro per due motivi:

  1. String.getBytes ("UTF-16") aggiunge un carattere di ordine dei byte-marcatore all'uscita per identificare l'ordine dei byte . Si consiglia di utilizzare UTF-16LE o UTF-16BE per evitare che ciò accada.
  2. Non tutte le sequenze di byte possono essere mappate ai caratteri in UTF-16. Innanzitutto, il testo codificato in UTF-16 deve avere un numero pari di byte. In secondo luogo, UTF-16 ha un meccanismo per codificare i caratteri unicode oltre U + FFFF. Ciò significa che ad es. ci sono sequenze di 4 byte che si associano a un solo carattere Unicode. Affinché ciò sia possibile, i primi 2 byte dei 4 non codificano alcun carattere in UTF-16.
+0

Oggi ho visto che ho problemi durante l'utilizzo della crittografia e della decrittografia in diverse VM. Ovviamente funziona solo la tua soluzione. – Bevor

+0

Anche il tuo approccio all'uso di Apache Commons Codec dovrebbe funzionare, ma devi distribuire la libreria di codoni comuni con la tua applicazione. – Joni

+0

Molto utile, grazie mille :) – Spl2nky

0

tuo problema è che non si può costruire una stringa UTF-16 (o qualsiasi altra codifica) da un arbitrario array di byte (vedere UTF-16 on Wikipedia). Spetta a te, tuttavia, serializzare e deserializzare l'array di byte crittografato senza alcuna perdita, al fine, per esempio, di persistere e utilizzarlo in seguito. Ecco il codice del client modificato che dovrebbe dare una certa comprensione di ciò che sta realmente accadendo con le matrici di byte:

public static void main(String[] args) throws Exception { 
    String toEncrypt = "FOOBAR"; 

    NewEncrypter encrypter = new NewEncrypter(); 

    byte[] encryptedByteArray = encrypter.encrypt(toEncrypt); 
    System.out.println("encryptedByteArray:" + Arrays.toString(encryptedByteArray)); 

    String decoded = new String(encryptedByteArray, "UTF-16"); 
    System.out.println("decoded:" + decoded); 

    byte[] encoded = decoded.getBytes("UTF-16"); 
    System.out.println("encoded:" + Arrays.toString(encoded)); 

    String decryptedText = encrypter.decrypt(encryptedByteArray); // NOT the "encoded" value! 
    System.out.println("decryptedText:" + decryptedText); 
} 

Questo è l'output:

encryptedByteArray:[90, -40, -39, -56, -90, 51, 96, 95, -65, -54, -61, 51, 6, 15, -114, 88] 
decoded:<some garbage> 
encoded:[-2, -1, 90, -40, -1, -3, 96, 95, -65, -54, -61, 51, 6, 15, -114, 88] 
decryptedText:FOOBAR 

Il decryptedText è corretto, quando ripristinato dall'originale encryptedByteArray . Si noti che il valore encoded non è uguale a encryptedByteArray, a causa della perdita di dati durante la conversione byte[] -> String("UTF-16")->byte[].

+0

Grazie, al momento ho trovato anche un'altra soluzione (vedi la mia risposta). Tuttavia accetterò la tua risposta, perché anche la tua soluzione funziona. – Bevor

+0

Oggi ho visto che decifri l'array di byte originale. Questo in realtà non è quello che volevo (e sfortunatamente ho avuto problemi a decifrarlo di nuovo quando lo uso in diverse VM), quindi funziona solo la soluzione di Joni. – Bevor

+0

Il mio codice era solo un'illustrazione dell'errore che avevi nel tuo codice, non una soluzione di serializzazione ("Sta a te, comunque, serializzare e deserializzare l'array di byte criptato senza alcuna perdita"), come potresti voler usare tonnellate di diverse tecniche di serializzazione (utilizzando DataIn/OutputStreams ecc.) –

5

Ora, ho trovato un'altra soluzione troppo ...

public class NewEncrypterTest 
    { 
     @Test 
     public void canEncryptAndDecrypt() throws Exception 
     { 
      String toEncrypt = "FOOBAR"; 

      NewEncrypter encrypter = new NewEncrypter(); 

      byte[] encryptedByteArray = encrypter.encrypt(toEncrypt); 
      String encoded = String.valueOf(Hex.encodeHex(encryptedByteArray)); 

      byte[] byteArrayToDecrypt = Hex.decodeHex(encoded.toCharArray()); 
      String decryptedText = encrypter.decrypt(byteArrayToDecrypt); 

      System.out.println("decryptedText:" + decryptedText); 

      assertEquals(toEncrypt, decryptedText); 
     } 
    } 
13

soluzione accettata non funziona se il vostro String ha alcune charcaters non tipici come š, ž, ć, Ō, ō, Ū, ecc

Seguendo codice lavorato bene per me.

byte[] myBytes = Something.getMyBytes(); 
String encodedString = Base64.encodeToString(bytes, Base64.NO_WRAP); 
byte[] decodedBytes = Base64.decode(encodedString, Base64.NO_WRAP); 
+0

l'unica soluzione funzionante, sebbene tu debba dipendere da ApacheCommons – AntJavaDev

+3

stavo usando 'android.util.Base64'. Se non vuoi includere 'ApacheCommons', suppongo che puoi sempre copiare il file nel tuo progetto. Ecco la fonte: https://github.com/android/platform_frameworks_base/blob/master/core/java/android/util/Base64.java –

+1

sì ho sottolineato che per le applicazioni java, questa classe di utilità viene fornita con JDK 1.8 e , ma per le precedenti versioni di Java devi dipendere da ApacheCommons poiché è l'unico metodo di lavoro. – AntJavaDev

Problemi correlati