web-dev-qa-db-fra.com

XOR opération avec deux chaînes dans java

Comment faire au niveau du bit XOR opération sur deux chaînes en Java.

50
yasitha

Vous voulez quelque chose comme ça:

import Sun.misc.BASE64Decoder;
import Sun.misc.BASE64Encoder;
import Java.io.IOException;

public class StringXORer {

    public String encode(String s, String key) {
        return base64Encode(xorWithKey(s.getBytes(), key.getBytes()));
    }

    public String decode(String s, String key) {
        return new String(xorWithKey(base64Decode(s), key.getBytes()));
    }

    private byte[] xorWithKey(byte[] a, byte[] key) {
        byte[] out = new byte[a.length];
        for (int i = 0; i < a.length; i++) {
            out[i] = (byte) (a[i] ^ key[i%key.length]);
        }
        return out;
    }

    private byte[] base64Decode(String s) {
        try {
            BASE64Decoder d = new BASE64Decoder();
            return d.decodeBuffer(s);
        } catch (IOException e) {throw new RuntimeException(e);}
    }

    private String base64Encode(byte[] bytes) {
        BASE64Encoder enc = new BASE64Encoder();
        return enc.encode(bytes).replaceAll("\\s", "");

    }
}

L'encodage base64 est effectué car le xor'ing des octets d'une chaîne peut ne pas restituer des octets valides pour une chaîne.

49
user467257

Remarque: cela ne fonctionne que pour les caractères faibles, c'est-à-dire en dessous de 0x8000, cela fonctionne pour tous les caractères ASCII.

Je ferais un XOR chaque charAt () pour créer une nouvelle chaîne. Comme

String s, key;

StringBuilder sb = new StringBuilder();
for(int i = 0; i < s.length(); i++)
    sb.append((char)(s.charAt(i) ^ key.charAt(i % key.length())));
String result = sb.toString();

En réponse au commentaire de @ user467257

Si votre entrée/sortie est utf-8 et vous xor "a" et "æ", vous vous retrouvez avec une chaîne utf-8 invalide composée d'un caractère (décimal 135, un caractère de continuation).

Ce sont les valeurs char qui sont xor'ed, mais les valeurs d'octet et cela produit un caractère qui peut être encodé en UTF-8.

public static void main(String... args) throws UnsupportedEncodingException {
    char ch1 = 'a';
    char ch2 = 'æ';
    char ch3 = (char) (ch1 ^ ch2);
    System.out.println((int) ch3 + " UTF-8 encoded is " + Arrays.toString(String.valueOf(ch3).getBytes("UTF-8")));
}

impressions

135 UTF-8 encoded is [-62, -121]
27
Peter Lawrey

Faites attention:

Un Java char correspond à une unité de code UTF-16 et, dans certains cas, deux chars consécutifs (un soi-disant une paire de substitution ) est nécessaire pour un caractère Unicode réel (point de code).

XORing deux séquences UTF-16 valides (ie Java Strings char by char, ou octet par octet après encodage en UTF-16) ne vous donne pas nécessairement une autre chaîne UTF-16 valide - vous pouvez avoir des substituts non appariés en conséquence. (Ce serait toujours une chaîne parfaitement utilisable Java String, seules les méthodes concernant le point de code pourraient se confondre, et celles qui convertir en d'autres encodages pour la sortie et similaire.)

La même chose est valable si vous convertissez d'abord vos chaînes en UTF-8 puis XOR ces octets - ici vous très probablement se retrouvera avec une séquence d'octets qui n'est pas UTF-8 valide, si vos chaînes n'étaient pas déjà toutes les deux pures ASCII chaînes.

Même si vous essayez de le faire correctement et d'itérer sur vos deux chaînes par point de code et essayez de XOR les points de code, vous pouvez vous retrouver avec des points de code en dehors de la plage valide (par exemple, U+FFFFF (avion 15) XOR U+10000 (avion 16) = U+1FFFFF (qui serait le dernier caractère de l'avion 31), bien au-dessus de la plage des points de code existants. Et vous pourriez également vous retrouver ainsi avec des points de code réservés aux substituts (= non valides).

Si vos chaînes ne contiennent que des caractères <128, 256, 512, 1024, 2048, 4096, 8192, 16384 ou 32768, alors les chaînes XOR (en termes de caractères) seront dans la même plage et ne contiendront donc certainement aucun substitut. Dans les deux premiers cas, vous pouvez également coder votre chaîne en tant que ASCII ou Latin-1, respectivement, et avoir le même résultat XOR pour les octets. (Vous pouvez toujours vous retrouver avec des caractères de contrôle, ce qui peut être un problème pour vous.)


Ce que je dis enfin ici: ne vous attendez pas à ce que le résultat du cryptage des chaînes soit à nouveau une chaîne valide - au lieu de cela, stockez-la simplement et transmettez-la en tant que byte[] (ou un flux d'octets). (Et oui, convertissez-vous en UTF-8 avant le cryptage, et en UTF-8 après le décryptage).

17
Paŭlo Ebermann

Voici le code que j'utilise:

private static byte[] xor(final byte[] input, final byte[] secret) {
    final byte[] output = new byte[input.length];
    if (secret.length == 0) {
        throw new IllegalArgumentException("empty security key");
    }
    int spos = 0;
    for (int pos = 0; pos < input.length; ++pos) {
        output[pos] = (byte) (input[pos] ^ secret[spos]);
        ++spos;
        if (spos >= secret.length) {
            spos = 0;
        }
    }
    return output;
}
3
yegor256

En supposant (!) Que les chaînes sont de longueur égale, pourquoi pas convertir les chaînes en tableaux d'octets puis XOR les octets. Les tableaux d'octets résultants peuvent être de longueurs différentes également en fonction de votre encodage (par exemple, UTF8 s'étendra à différentes longueurs d'octets pour différents caractères).

Vous devez faire attention à spécifier l'encodage des caractères pour assurer une conversion chaîne/octet cohérente/fiable.

3
Brian Agnew

Cette solution est compatible avec Android (je l'ai testé et utilisé moi-même). Merci à @ user467257 dont j'ai adapté la solution depuis.

import Android.util.Base64;

public class StringXORer {

public String encode(String s, String key) {
    return new String(Base64.encode(xorWithKey(s.getBytes(), key.getBytes()), Base64.DEFAULT));
}

public String decode(String s, String key) {
    return new String(xorWithKey(base64Decode(s), key.getBytes()));
}

private byte[] xorWithKey(byte[] a, byte[] key) {
    byte[] out = new byte[a.length];
    for (int i = 0; i < a.length; i++) {
        out[i] = (byte) (a[i] ^ key[i%key.length]);
    }
    return out;
}

private byte[] base64Decode(String s) {
    return Base64.decode(s,Base64.DEFAULT);
}

private String base64Encode(byte[] bytes) {
    return new String(Base64.encode(bytes,Base64.DEFAULT));

}
}
2
nemesisfixx

la fonction abs est lorsque les chaînes n'ont pas la même longueur, la longueur du résultat sera donc la même que la longueur minimale des deux chaînes a et b

public String xor(String a, String b){
    StringBuilder sb = new StringBuilder();
    for(int k=0; k < a.length(); k++)
       sb.append((a.charAt(k) ^ b.charAt(k + (Math.abs(a.length() - b.length()))))) ;
       return sb.toString();
}
1
user3514540