Opération XOR avec deux chaînes en java
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.
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]
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).
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.
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 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();
}
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));
}
}