Операция XOR с двумя строками в java
Как выполнить побитовую операцию XOR с двумя строками в java.
- Как запустить TestNG из командной строки
- Utils для чтения текстового файла ресурсов в String (Java)
- Неизменяемый class?
- Эквивалент Sprintf в Java
- C / C ++: оптимизация указателей на строковые константы
- p: commandbutton action не работает внутри p: dialog
- Байт Java-массива с массивом строк в байт
- Java: String split (): Я хочу, чтобы он включал пустые строки в конце
Вы хотите что-то вроде этого:
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 байтов строки может не возвращать действительные байты для строки.
Примечание: это работает только для низких символов, т.е. ниже 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, но байтовые значения, и это создает символ, который кодируется 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 (т. Е. char
Java Strings 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, то строки (char-wise) XORed будут находиться в одном диапазоне и, следовательно, не будут содержать никаких суррогатов. В первых двух случаях вы также можете кодировать свою строку как ASCII или Latin-1, соответственно, и иметь тот же XOR-результат для байтов. (Вы все еще можете получить контрольные символы, что может быть проблемой для вас.)
Что я, наконец, говорю здесь : не ожидайте, что результат шифрования строк будет снова корректной строкой – вместо этого просто сохраните и передайте его в виде byte[]
(или streamа байтов). (И да, конвертировать в UTF-8 до шифрования и из UTF-8 после дешифрования).
Предполагая (!) Строки одинаковой длины, почему бы не преобразовать строки в байтовые массивы, а затем XOR байты. Результирующие байт-массивы могут быть различной длины также в зависимости от вашего кодирования (например, 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(); }
Это решение совместимо с 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)); } }