在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序列(即Javastringchar
或char
逐字节后编码为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)); } }