如何对java中的两个字符串执行按位XOR运算。
答案 0 :(得分:49)
你想要这样的东西:
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", "");
}
}
完成base64编码是因为xor'ing字符串的字节可能不会为字符串返回有效字节。
答案 1 :(得分:27)
注意:这仅适用于低字符,即低于0x8000,这适用于所有ASCII字符。
我会对每个charAt()执行XOR以创建新的String。像
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();
回应@ user467257的评论
如果您的输入/输出是utf-8而你是“a”和“æ”,则会留下一个无效的utf-8字符串,其中包含一个字符(十进制135,一个连续字符)。
正是char
值是xor'ed,但是字节值,这会产生一个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")));
}
打印
135 UTF-8 encoded is [-62, -121]
答案 2 :(得分:17)
注意:
Java char
对应于UTF-16代码单元,在某些情况下,需要两个连续的char
s(所谓的代理对)真正的Unicode字符(代码点)。
对两个有效的UTF-16序列进行异或(即char
的Java字符串char
,或者在编码为UTF-16后逐个字节)并不一定会给你另一个有效的UTF-16字符串 - 你结果可能有未成对的代理人。 (它仍然是一个完全可用的Java字符串,只是与代码点相关的方法可能会混淆,以及转换为其他编码的输出和类似的。)
如果您首先将字符串转换为UTF-8然后将这些字节转换为XOR,则同样有效 - 此处很可能最终会出现一个无效的UTF-8字节序列,如果你的字符串不是纯ASCII字符串。
即使您尝试正确执行并通过代码点迭代两个字符串并尝试对代码点进行异或,您最终也会得到有效范围之外的代码点(例如,U+FFFFF
(平面15){ {1}}(平面16)XOR U+10000
(这将是平面31的最后一个字符),超出现有代码点的范围。你也可以用代理点保留的代码点结束这种方式(=无效的代码点) )。
如果你的字符串只包含字符&lt; 128,256,512,1024,2048,4096,8192,16384或32768,那么(char-wise)XORed字符串将在相同的范围内,因此当然不包含任何代理。在前两种情况下,您还可以将String编码为ASCII或Latin-1,并对字节具有相同的XOR结果。 (你仍然可以使用控制字符,这对你来说可能是一个问题。)
我最后在这里说的是:不要指望加密字符串的结果再次成为有效的字符串 - 而只是将其存储并传输为= U+1FFFFF
(或一串字节)。 (是的,在加密前转换为UTF-8,在解密后转换为UTF-8)。
答案 3 :(得分:3)
假设(!)字符串长度相等,为什么不convert the strings to byte arrays然后对字节进行异或。结果字节数组也可能具有不同的长度,具体取决于您的编码(例如,UTF8将扩展为不同字符的不同字节长度)。
您应该注意指定字符编码以确保一致/可靠的字符串/字节转换。
答案 4 :(得分:3)
这是我正在使用的代码:
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;
}
答案 5 :(得分:2)
此解决方案与Android兼容(我自己测试并使用过它)。感谢@ user467257我的解决方案我改编了它。
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));
}
}
答案 6 :(得分:1)
abs函数是当Strings的长度不同时,结果的legth将与两个字符串a和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();
}