Opération XOR avec deux chaînes en java

Comment faire l'opération XOR bitwise à deux chaînes en java.

46
demandé sur yasitha 2011-02-26 14:28:51

7 réponses

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

    }
}

le codage base64 est fait parce que xor'ing les octets d'une chaîne de caractères peut ne pas donner des octets valides pour une chaîne de caractères.

48
répondu user467257 2011-11-02 16:06:17

Note: cela ne fonctionne que pour les caractères bas, c'est-à-dire inférieurs à 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 que vous xor "A" et "æ", il vous reste une chaîne utf-8 invalide constituée d'un caractère (décimal 135, a caractère de continuation).

ce sont les valeurs char qui sont xor'ed, mais les valeurs byte et cela produit un caractère qui peut être encodé 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")));
}

imprime

135 UTF-8 encoded is [-62, -121]
27
répondu Peter Lawrey 2014-02-21 11:13:47

faites attention:

a Java char correspond à une unité de code UTF-16, et dans certains cas deux char s consécutifs (une soi-disant paire de substituts ) sont nécessaires pour un vrai caractère Unicode (codepoint).

XORing deux séquences UTF-16 valides (i.e. Java Strings char par char , ou byte par byte après encodage à UTF-16) ne vous donne pas nécessairement un autre chaîne UTF-16 valide - Vous pouvez avoir des substituts non appariés en conséquence. (Ce serait encore une chaîne Java parfaitement utilisable, juste les méthodes concernant les codépoints pourraient être confondues, et celles qui convertissent 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 et puis XOR ces octets-ici vous très probablement finira avec une séquence d'octets qui N'est pas valide UTF-8, si vos chaînes n'étaient pas déjà les deux cordes ASCII pures.

même si vous essayez de le faire correctement et itérer au-dessus de vos deux chaînes par un point de code et essayer de XOR les points de code, vous pouvez vous retrouver avec des points de code en dehors de la gamme valide (par exemple, U+FFFFF (plan 15) XOR U+10000 (plan 16) = U+1FFFFF (qui serait le dernier caractère du plan 31), bien au-dessus de la gamme des points de code existants. Et vous pourriez aussi vous retrouver de cette façon avec des codépoints 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 cordes (char-wise) XORed seront dans la même gamme, et donc certainement pas contenir des substituts. Dans les deux premiers cas, vous pouvez aussi encoder votre chaîne ASCII ou Latin-1, respectivement, et avoir le même résultat XOR pour les octets. (Vous pouvez toujours retrouver avec les caractères de contrôle, qui peut être un problème pour vous.)


ce que je dis finalement ici : ne vous attendez pas à ce que le résultat du cryptage des chaînes soit une chaîne valide à nouveau - au lieu de cela, il suffit de la stocker et de la transmettre comme un byte[] (ou un flux d'octets). (Et oui, convertissez-vous en UTF-8 avant de chiffrer, et en UTF-8 après déchiffrement).

16
répondu Paŭlo Ebermann 2011-02-26 22:04:42

en supposant (!) les chaînes sont de longueur égale, pourquoi pas convertissez les chaînes en tableaux d'octets et puis XOR les octets. Les tableaux d'octets résultants peuvent être de longueurs différentes aussi selon votre encodage (par exemple, UTF8 se développera à des longueurs d'octets différentes pour des caractères différents).

vous devez être prudent de spécifier l'encodage des caractères pour assurer la cohérence/fiabilité de la conversion chaîne/octet.

3
répondu Brian Agnew 2017-05-23 11:47:18

C'est 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
répondu yegor256 2012-12-26 16:02:57

la fonction abs est lorsque les cordes ne sont pas de la même longueur de sorte que la longueur de la jambe du résultat sera la même que la longueur minimale des deux cordes 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
répondu user3514540 2016-10-14 13:40:08

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

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

}
}
1
répondu nemesisfixx 2017-06-29 11:02:52