Операция XOR с двумя строками в Java - PullRequest
48 голосов
/ 26 февраля 2011

Как выполнить побитовую операцию XOR для двух строк в Java.

Ответы [ 7 ]

49 голосов
/ 02 ноября 2011

Вы хотите что-то вроде этого:

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 байтов строки может не вернуть действительные байты для строки.

27 голосов
/ 26 февраля 2011

Примечание: это работает только для младших символов, то есть ниже 0x8000, Это работает для всех символов ASCII.

Я бы сделал XOR для каждого charAt (), чтобы создать новую строку.Например,

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 и вы используете xor «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]
17 голосов
/ 26 февраля 2011

Обратите внимание:

Java char соответствует единице кода UTF-16, а в некоторых случаях два последовательных char с (так называемые суррогатная пара ) необходима для одного действительного символа Unicode (кодовая точка).

XOR для двух допустимых последовательностей UTF-16 (т. е. строки Java char на char, или байтов за байтом после кодирования в UTF-16) не обязательно дает вам другую допустимую строку UTF-16 - в результате у вас могут быть непарные суррогаты.(Это все равно будет идеально используемая строка Java, только методы, связанные с кодовыми точками, могут быть перепутаны и те, которые преобразуются в другие кодировки для вывода и тому подобное.)

То же самое верно, если вы сначала преобразуете вашиСтроки в UTF-8, а затем XOR эти байты - здесь вы вполне вероятно, получат последовательность байтов, которая не является допустимой UTF-8, если ваши строки не были уже обеими чистыми строками ASCII.

Даже если вы попытаетесь сделать это правильно и перебрать две строки по кодам и попытаться XOR кодов, вы можете получить кодовые точки вне допустимого диапазона (например, U+FFFFF (плоскость 15) XOR U+10000(плоскость 16) = U+1FFFFF (который будет последним символом плоскости 31), намного выше диапазона существующих кодовых точек, и вы также можете получить таким образом кодовые точки, зарезервированные для суррогатов (= недействительных).

Если в ваших строках есть только символы <128, 256, 512, 1024, 2048, 4096, 8192, 16384 или 32768, то строки XORed (за исключением символов) будут находиться втот же диапазон, и, следовательно, конечно, не содержат никаких суррогатов.В первых двух случаях вы также можете кодировать свою строку как ASCII или Latin-1, соответственно, и иметь один и тот же XOR-результат для байтов.(Вы все еще можете получить контрольные символы, что может быть проблемой для вас.) </p>


То, что я, наконец, говорю здесь : не ожидайте результата шифрованияСтроки снова должны быть корректными - вместо этого просто сохраните и передайте их как byte[] (или поток байтов).(И да, преобразовать в UTF-8 перед шифрованием и из UTF-8 после расшифровки).

3 голосов
/ 26 декабря 2012

Это код, который я использую:

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;
}
3 голосов
/ 26 февраля 2011

Если (!) Строки имеют одинаковую длину, почему бы не преобразовать строки в байтовые массивы , а затем XOR байтов. Результирующие байтовые массивы могут также иметь разную длину в зависимости от вашей кодировки (например, UTF8 будет расширяться до разных длин байтов для разных символов).

Следует соблюдать осторожность при указании кодировки символов, чтобы обеспечить согласованное / надежное преобразование строки / байта.

2 голосов
/ 29 июня 2017

Это решение совместимо с 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));

}
}
1 голос
/ 22 мая 2014

Функция 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();
}
...