java中如何对两个字符串进行按位异或运算。
7 回答
你想要这样的东西:
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 编码已完成,因为对字符串的字节进行异或可能无法为字符串返回有效字节。
注意:这仅适用于低字符,即低于 0x8000,这适用于所有 ASCII 字符。
我会对每个 charAt() 进行 XOR 以创建一个新字符串。喜欢
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”和“æ”进行异或运算,则会留下一个由一个字符(十进制 135,一个连续字符)组成的无效 utf-8 字符串。
它是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]
注意:
Javachar
对应一个 UTF-16 代码单元,在某些情况下,一个真正的 Unicode 字符(代码点)需要两个连续char
的 s(所谓的代理对)。
异或两个有效的 UTF-16 序列(即 Java 字符串char
,char
或编码为 UTF-16 后的字节)并不一定会给您另一个有效的 UTF-16 字符串 - 结果您可能有不成对的代理项。(它仍然是一个完全可用的 Java 字符串,只是与代码点有关的方法可能会混淆,以及转换为其他编码以进行输出和类似的方法。)
如果您首先将字符串转换为 UTF-8,然后对这些字节进行异或运算,则同样有效 - 如果您的字符串还不是纯 ASCII 字符串,那么您很可能会得到一个无效的 UTF-8 字节序列。
即使您尝试正确执行此操作并按代码点迭代您的两个字符串并尝试对代码点进行异或,您最终可能会得到有效范围之外的代码点(例如,U+FFFFF
(平面 15)XOR U+10000
(平面 16)= U+1FFFFF
(这将是最后一个平面 31 的字符),远高于现有代码点的范围。而且您也可以以这种方式结束为代理保留的代码点(= 无效的代码点)。
如果您的字符串仅包含 < 128、256、512、1024、2048、4096、8192、16384 或 32768 的字符,那么(按字符)异或字符串将在同一范围内,因此肯定不包含任何代理项。在前两种情况下,您还可以分别将您的字符串编码为 ASCII 或 Latin-1,并对字节具有相同的 XOR 结果。(您仍然可以使用控制字符,这对您来说可能是个问题。)
我最后在这里要说的是:不要期望加密字符串的结果再次成为有效字符串 - 相反,只需将其存储并作为byte[]
(或字节流)传输。(是的,加密前转换为 UTF-8,解密后转换为 UTF-8)。
此解决方案与 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));
}
}
假设(!)字符串长度相等,为什么不将字符串转换为字节数组,然后对字节进行异或。根据您的编码,生成的字节数组也可能具有不同的长度(例如,UTF8 将针对不同的字符扩展为不同的字节长度)。
您应该小心指定字符编码以确保一致/可靠的字符串/字节转换。
这是我正在使用的代码:
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 函数是当字符串长度不同时,结果的长度将与两个字符串 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();
}