为一个类项目提供了一个自定义的异或加密函数。我们的目标是获取这个函数并创建一个解密函数。这几天我都快被它逼疯了。有人能帮帮我吗?
如果我输入hello并使用key password,我的输出是:35266a676e6e6d
希望扭转这一局面。
class StringUtils {
private static final char[] hexArray = "0123456789abcdef".toCharArray();
StringUtils() {
}
private static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[(bytes.length * 2)];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 255;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[(j * 2) + 1] = hexArray[v & 15];
}
return new String(hexChars);
}
static String xor(String input, String key) throws InterruptedException {
int index;
String lengthString = input.length() + "&";
byte[] buffer = new byte[(input.length() + lengthString.length())];
int k_len = key.length();
int index2 = 0;
int c = 0;
int i = 0;
while (i < lengthString.length()) {
int c2 = c + 1;
int index3 = index2 + 1;
buffer[c] = (byte) (((byte) lengthString.charAt(i)) ^ (((byte) key.charAt(index2)) & 10));
if (index3 >= k_len) {
index2 = 0;
} else {
index2 = index3;
}
i++;
c = c2;
}
int i2 = 0;
while (i2 < input.length()) {
int c3 = c + 1;
int index4 = index2 + 1;
buffer[c] = (byte) (((byte) input.charAt(i2)) ^ (((byte) key.charAt(index2)) & 10));
if (index4 >= k_len) {
index = 0;
} else {
index = index4;
}
i2++;
c = c3;
}
return bytesToHex(buffer);
}
}
逻辑的重构版本:
static String encrypt(String input, String key) {
String plain = (input.length() + "&" + input); // add length
return DatatypeConverter.printHexBinary(xor2(plain.getBytes(StandardCharsets.UTF_8), key.getBytes(StandardCharsets.UTF_8)));
}
static String decrypt(String encrypted, String key) {
String plain = new String(xor2(DatatypeConverter.parseHexBinary(encrypted), key.getBytes(StandardCharsets.UTF_8)));
return plain.substring(plain.indexOf('&') + 1); // remove length
}
static byte[] xor2(byte[] in, byte[] key) {
byte[] out = new byte[in.length];
for (int i = 0; i < in.length; i++) {
out[i] = (byte)(in[i] ^ (key[i % key.length] & 10));
}
return out;
}
…和一个测试用例:
@Test
public void xorTest() {
String message = "hello";
String key = "password";
String encrypted = StringUtils.encrypt(message, key);
System.out.println("encrypted: " + encrypted);
String plain = StringUtils.decrypt(encrypted, key);
System.out.println("plain: " + plain);
}
这产生:
encrypted: 35266A676E666D
plain: hello
在不给你的家庭作业给出答案的情况下,这里有一个提示来帮助你开始。
提示1:
稍微重构后,xor
函数是这样的
static String xor(String input, String key) {
String lengthString = input.length() + "&";
byte[] buffer = new byte[(input.length() + lengthString.length())];
int keyIndex = 0;
int bufferIndex = 0;
for (int i = 0; i < lengthString.length(); i++) {
buffer[bufferIndex] = (byte) (((byte) lengthString.charAt(i)) ^ (((byte) key.charAt(keyIndex)) & 10));
keyIndex = (keyIndex + 1) % key.length();
bufferIndex++;
}
for (int i = 0; i < input.length(); i++) {
buffer[bufferIndex] = (byte) (((byte) input.charAt(i)) ^ (((byte) key.charAt(keyIndex)) & 10));
bufferIndex++;
}
return bytesToHex(buffer);
}
提示2:
输入字符串的长度可以先取一个较大的估定值,然后通过反复试验来减少长度,直到找到正确的值。
// Move input length down until it matches the calculation for buffer length
int inputLength = buffer.length - 1;
while (inputLength + (inputLength + "&").length() > buffer.length) {
inputLength--;
}
// So now we know the length string
String lengthString = inputLength + "&";
提示3:
有三种情况你需要考虑。
lengthString.length() > key.length()
:这种情况要求我们找到键重复的地方并抓取下一个字符。这是可行的,但可能需要额外的工作。lengthString.length() == key.length()
:这是最容易解决的情况,因为它意味着输入的字符是键中的第一个字符。lengthString.length() < key.length()
:如果是这种情况,您将无法解决这个问题,因为用于xor输入的字节不在缓冲区的长度部分。所以为了讨论起见,请完全忽略这种情况。
lengthString
部分有多长。之后,您可以尝试通过检查模式或仅列出所有4种可能的解决方案来进行智能操作,因为我们知道输入只能用0,2,8或10进行xor(因为之前的& 10
)。
static String extractSolution(byte[] buffer, int start, byte xor) {
char[] solution = new char[buffer.length - start];
for (int i = start; i < buffer.length; i++) {
solution[i - start] = (char)(buffer[i] ^ xor);
}
return new String(solution);
}
static void decode(byte[] buffer) {
// Move input length down until it matches the calculation for buffer length
int inputLength = buffer.length - 1;
while (inputLength + (inputLength + "&").length() > buffer.length) {
inputLength--;
}
String lengthString = inputLength + "&";
// Extract part of the key used in the first section
byte[] keyFragment = new byte[lengthString.length()];
for (int i = 0; i < keyFragment.length; i++) {
keyFragment[i] = (byte) (buffer[i] ^ (byte) lengthString.charAt(i));
}
System.out.println("Most likely solutions:");
// Look for repeating patterns to find solution
periodCheck:
for (int i = 1; i < keyFragment.length + 1; i++) {
for (int j = i; j < keyFragment.length; j++) {
if (keyFragment[j] != keyFragment[j % i])
continue periodCheck;
}
// A possible period has been found
byte specialByte = keyFragment[keyFragment.length % i];
String possibleSolution = extractSolution(buffer, keyFragment.length, specialByte);
System.out.println(""" + possibleSolution + """);
}
System.out.println("All Possible Solutions:");
// List out all possible inputs that could have given this buffer
// In reality key can only be one of [0, 2, 8, 10], but go through all bytes for verbosity
for (int key = Byte.MIN_VALUE; key <= Byte.MAX_VALUE; key ++) {
if ((key & 10) != key)
continue;
String possibleSolution = extractSolution(buffer, keyFragment.length, (byte) key);
System.out.println(""" + possibleSolution + """);
}
}
我如何提问和回答作业问题?