Подтвердить что ты не робот

Операция XOR с двумя строками в java

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

Я бы сделал XOR каждый charAt(), чтобы создать новую 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

Если ваш ввод/вывод - utf-8, а вы xor "a" и "æ", вы остаетесь с недопустимой строкой utf-8, состоящей из одного символа (десятичный 135, символ продолжения).

Это значения char, которые являются xor'ed, но байтовые значения, и это создает символ, который a кодируется 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]

Ответ 3

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

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

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

То же самое верно, если вы сначала конвертируете свои строки в 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, то строки (char -wise) XORed будут находиться в одном диапазоне и, следовательно, не будут содержать никаких суррогатов. В первых двух случаях вы также можете кодировать свою строку как ASCII или Latin-1, соответственно, и иметь тот же XOR-результат для байтов. (Вы все еще можете получить контрольные символы, что может быть проблемой для вас.)


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

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

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

}
}