Opération XOR avec deux chaînes en Java

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

Vous voulez quelque chose comme ça:

import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import java.io.IOException; public class SsortingngXORer { public Ssortingng encode(Ssortingng s, Ssortingng key) { return base64Encode(xorWithKey(s.getBytes(), key.getBytes())); } public Ssortingng decode(Ssortingng s, Ssortingng key) { return new Ssortingng(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 effectué car le fait de xoriser les octets d'une chaîne peut ne pas donner d'octets valides à une chaîne.

Remarque: cela ne fonctionne que pour les caractères faibles, 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

 Ssortingng s, key; SsortingngBuilder sb = new SsortingngBuilder(); 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 "æ", vous avez une chaîne utf-8 invalide composée d'un caractère (décimal 135, un caractère de continuation).

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

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

estampes

 135 UTF-8 encoded is [-62, -121] 

Faites attention:

Un caractère Java correspond à une unité de code UTF-16 et, dans certains cas, deux caractères consécutifs (une paire de substitution ) sont nécessaires pour un caractère Unicode réel (sharepoint code).

Le fait de XORer deux séquences UTF-16 valides (par exemple, les chaînes Java char par caractère ou octet par octet après encodage en UTF-16) ne vous donne pas nécessairement une autre chaîne UTF-16 valide. (Il s’agirait toujours d’une chaîne de caractères Java parfaitement utilisable, seules les méthodes concernant les points de code pourraient être confondues, et celles qui sont converties en autres codages pour la sortie et similaires).

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

Même si vous essayez de le faire correctement et que vous parcourez vos deux chaînes par un sharepoint code et essayez de XOR les codes, vous pouvez vous retrouver avec des points de code en dehors de la plage valide (par exemple, U+FFFFF (plan 15) XOR U+10000 16) = U+1FFFFF (qui serait le dernier caractère du plan 31), bien au-dessus de la plage des points de code existants, et vous pourriez également vous retrouver 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, les chaînes XOR (en mode char) seront dans la même plage et ne contiendront donc certainement aucun substitut. Dans les deux premiers cas, vous pouvez également encoder votre chaîne en 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 finalement 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-le simplement et transmettez-le sous la byte[] un byte[] (ou d’un stream d’octets). (Et oui, convertir en UTF-8 avant le chiffrement et à partir d’UTF-8 après le déchiffrement).

En supposant (!) Que les chaînes sont de même longueur, pourquoi ne pas convertir les chaînes en tableaux d’octets , puis XOR les octets. Les tableaux d’octets résultants peuvent également avoir des longueurs différentes en fonction de votre codage (par exemple, UTF8 étendra la longueur en octets à différents caractères).

Vous devez faire attention à spécifier le codage des caractères pour garantir une conversion cohérente / fiable des chaînes / octets.

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

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

 public Ssortingng xor(Ssortingng a, Ssortingng b){ SsortingngBuilder sb = new SsortingngBuilder(); 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(); } 

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

 import android.util.Base64; public class SsortingngXORer { public Ssortingng encode(Ssortingng s, Ssortingng key) { return new Ssortingng(Base64.encode(xorWithKey(s.getBytes(), key.getBytes()), Base64.DEFAULT)); } public Ssortingng decode(Ssortingng s, Ssortingng key) { return new Ssortingng(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)); } }