wake-up-neo.com

Probleme beim Konvertieren von Byte-Arrays in Zeichenfolgen und zurück in Byte-Arrays

Es gibt viele Fragen zu diesem Thema, dieselbe Lösung, aber das funktioniert nicht für mich. Ich habe einen einfachen Test mit einer Verschlüsselung. Die Verschlüsselung/Entschlüsselung selbst funktioniert (solange ich diesen Test mit dem Byte-Array selbst und nicht als Strings erledige). Das Problem ist, dass es nicht als Byte-Array, sondern als String behandelt werden soll. Wenn ich das Byte-Array jedoch in Zeichenfolge und zurück codiere, unterscheidet sich das resultierende Byte-Array vom ursprünglichen Byte-Array, sodass die Entschlüsselung nicht mehr funktioniert. Ich habe die folgenden Parameter in den entsprechenden String-Methoden ausprobiert: UTF-8, UTF8, UTF-16, UTF8. Keiner von ihnen arbeitet. Das resultierende Byte-Array unterscheidet sich vom Original. Irgendwelche Ideen, warum das so ist?

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;
    }
}

Dies ist der Test, bei dem ich es versuche:

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);
    }
}
39
Bevor

Es ist keine gute Idee, verschlüsselte Daten in Strings zu speichern, da sie für den Menschen lesbaren Text und nicht für beliebige binäre Daten sind. Für binäre Daten verwenden Sie am besten byte[].

Wenn Sie müssen tun, sollten Sie jedoch eine Kodierung verwenden, die eine 1-zu-1-Zuordnung zwischen Byte und Zeichen aufweist, d. H. Jede Bytefolge kann einer eindeutigen Zeichenfolge zugeordnet werden , und zurück. Eine solche Kodierung ist ISO-8859-1 , das heißt:

    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);

Andere häufige Kodierungen, bei denen keine Daten verloren gehen, sind hexadezimal und base64 . Leider benötigen Sie dazu eine Hilfsbibliothek. Die Standard-API definiert keine Klassen für sie.

Bei UTF-16 würde das Programm aus zwei Gründen fehlschlagen:

  1. String.getBytes ("UTF-16") fügt der Ausgabe ein Zeichen zur Markierung der Byte-Reihenfolge hinzu, um die Reihenfolge der Bytes zu bestimmen. Sie sollten UTF-16LE oder UTF-16BE verwenden, damit dies nicht geschieht.
  2. In UTF-16 können nicht alle Sequenzen von Bytes Zeichen zugeordnet werden. Zunächst muss in UTF-16 codierter Text eine gerade Anzahl von Bytes haben. Zweitens verfügt UTF-16 über einen Mechanismus zum Codieren von Unicode-Zeichen nach U + FFFF. Dies bedeutet, dass z. Es gibt Sequenzen von 4 Bytes, die nur einem Unicode-Zeichen zugeordnet sind. Damit dies möglich ist, codieren die ersten 2 Bytes der 4 keine Zeichen in UTF-16.
89
Joni

Die akzeptierte Lösung funktioniert nicht, wenn Ihre String nicht typische Zeichen wie š, ž, ć, Ō, ō, Ū usw. enthält.

Der folgende Code hat für mich gut funktioniert.

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

Nun habe ich auch eine andere Lösung gefunden ...

    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);
        }
    }
5
Bevor

Ihr Problem ist, dass Sie keinen UTF-16-String (oder eine andere Codierung) aus einem beliebigen Byte-Array erstellen können (siehe UTF-16 in Wikipedia ). Es liegt jedoch an Ihnen, das verschlüsselte Byte-Array verlustfrei zu serialisieren und zu deserialisieren, um es beispielsweise zu persistieren und später zu nutzen. Hier ist der modifizierte Client-Code, der Ihnen einen Einblick geben soll, was mit den Byte-Arrays tatsächlich passiert:

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);
}

Dies ist die Ausgabe:

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

Die decryptedText ist korrekt, wenn sie von der ursprünglichen encryptedByteArray wiederhergestellt wird. Bitte beachten Sie, dass der encoded-Wert aufgrund des Datenverlusts während der byte[] -> String("UTF-16")->byte[]-Konvertierung nicht mit encryptedByteArray identisch ist.

0