在java中使用两个string进行XOR操作

如何在java中对两个string进行按位异或操作。

你想要这样的东西:

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编码完成是因为异或string的字节可能不会为string返回有效字节。

注意:这只适用于低于0x8000的低字符,这适用于所有ASCII字符。

我会做一个异或每个charAt()来创build一个新的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的评论

如果你的input/输出是utf-8,而你的xor是“a”和“æ”,那么你的utf-8string是由一个字符(十进制135,一个延续字符)组成的。

这是正在被异或的char值,但字节值,这产生了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] 

请注意:

Java char对应于UTF-16代码单元,在某些情况下,对于一个真正的Unicode字符(代码点),需要两个连续的char (所谓的代理对 )。

XORing两个有效的UTF-16序列(即Javastringcharchar逐字节后编码为UTF-16)不一定会给你另一个有效的UTF-16string – 你可能有不配对的代理作为结果。 (它仍然是一个完全可用的Javastring,只是与代码点有关的方法可能会混淆,而转换为其他编码的输出和类似方法)。

如果你首先把你的string转换为UTF-8,然后再对这些字节进行XOR,那么这个结果也是一样的 – 如果你的string不是纯ASCIIstring的话,那么在这里你很可能会得到一个不合法的UTF-8字节序列。

即使您尝试正确地做,并通过代码来遍历两个string,并尝试XOR码点,您可能会得到有效范围外的码点(例如, U+FFFFF (平面15) XOR U+10000 16) = U+1FFFFF (这将是平面31的最后一个字符),高于现有码位的范围,而且你也可以用代码保留给代理(=无效)。

如果您的string只包含字符<128,256,512,1024,2048,4096,8192,16384或32768,那么(char-wise)XORedstring将在相同的范围内,因此肯定不包含任何代理。 在前两种情况下,您也可以分别将您的string编码为ASCII或Latin-1,并且对于字节具有相同的异或结果。 (你仍然可以结束控制字符,这可能是你的问题。)


我在这里最后说的是 :不要期望encryptionstring的结果再次成为一个有效的string,而只是简单地将它存储和传输为byte[] (或字节stream)。 (是的,encryption前转换为UTF-8,解密后是UTF-8)。

假设(!)string长度相等,为什么不将string转换为字节数组 ,然后异或字节。 由此产生的字节数组也可能具有不同的长度,具体取决于您的编码(例如UTF8将扩展为不同字符的不同字节长度)。

您应该小心指定字符编码以确保一致/可靠的string/字节转换。

这是我正在使用的代码:

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

abs函数是当string的长度不相同时,结果的长度将与两个stringa和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(); } 

这个解决scheme与Android兼容(我已经testing和使用它自己)。 感谢@ user467257,我的解决scheme适应了这一点。

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