Алгоритм одноразового блокнота на Java

Введение
В этой статье будет показан пример реализации алгоритма одноразового блокнота на чистой Java, без сторонних библиотек. В конце статьи есть ссылка на исходный код проекта.
Начало работы
Начнем с того, что создадим самый главный класс для этого проекта «Crypto». Код класса с комментариями показан ниже:
package ru.example.crypto;
import java.util.ArrayList;
import java.util.List;
public class Crypto {
// метод для получения массива байтов из входящей строки
private static List strToBytes(String str) {
List bytes = new ArrayList<>();
// получаем каждый символ входящей строки и переводим его в byte
// после чего добавляем в массив
for (char character : str.toCharArray())
bytes.add(
(byte) character
);
return bytes;
}
// метод для получения бинарной строки из массива байтов
private static String bytesToBinary(List bytes) {
String result = "";
for (Byte b : bytes)
result += ' ' + Integer.toBinaryString(b) + ' ';
return result;
}
// метод для реализации алгоритма одноразового блокнота
// принимает текст, который будет шифровать/расшифровывать и ключ
// по которому будет происходить шифрование/расшифровка
// operation влияет только на текст логирования
public static String disposableNotebook(String text, String key, Operation operation) throws Exception {
if(text.length() != key.length())
throw new Exception("[ERROR] Текст и ключ должны быть одинаковой длинны");
System.out.println(
(
operation == Operation.ENCODE
? "[INFO] Шифрование текста: "
: "[INFO] Декодирование текста: "
)
+ text
);
// получаем байты текста
List bytesText = strToBytes(text);
System.out.println(
"[INFO] Байты текста: " + bytesText
);
System.out.println(
"[INFO] Бинарная строка текста: " + bytesToBinary(bytesText)
);
// получаем байты ключа
List bytesKey = strToBytes(key);
System.out.println(
"[INFO] Байты ключа: " + bytesKey
);
System.out.println(
"[INFO] Бинарная строка ключа: " + bytesToBinary(bytesKey)
);
// будущий открытый текст или же шифротекст
String result = "";
for (int i = 0; i < text.length(); i++)
// берем каждый байт текста и ключа и проводим между ними операцию XOR
// ^ — побитовое исключающее "ИЛИ" (операция XOR)
result += (char) (bytesText.get(i) ^ bytesKey.get(i));
return result;
}
}
Метод «disposableNotebook» играет ключевую роль, т.к. в нем реализован сам алгоритм. Важным является то, что в данном алгоритме длинна текста и ключа должны совпадать, поэтому в начале метода прописана небольшая валидация. Параметр operation влияет только на текст логирования, для него используется следующее перечисление:
package ru.example.crypto;
public enum Operation {
ENCODE,
DECODE
}
По большому счету, это все. Для реализации данного алгоритма этого кода будет достаточно. Теперь напишем код, который будет все это добро использовать:
package ru.example.crypto;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class App {
private static BufferedReader reader = new BufferedReader(
new InputStreamReader(System.in)
);
public static void main(String[] args) {
String text = "", key = "", result = "";
int decision = 0;
while (decision != 3) {
System.out.print(
"___Меню___\n"
+ "1. Зашифровать\n"
+ "2. Расшифровать\n"
+ "3. Выход\n"
+ "| "
);
try {
decision = Integer.parseInt(
reader.readLine()
);
} catch (IOException e) {
System.out.println("[ERROR] Че-то пошло не так, давай еще раз");
continue;
} catch (NumberFormatException e) {
System.out.println("[ERROR] Будь добр, введи цифру");
continue;
} catch (Exception e) {
System.out.println("[ERROR] Ошибка");
continue;
}
switch (decision) {
case 1:
{
try {
System.out.print("Введите текст: ");
text = reader.readLine();
System.out.print("Введите ключ: ");
key = reader.readLine();
// Operation.ENCODE влияет только на текст логирования функции disposableNotebook
result = Crypto.disposableNotebook(text, key, Operation.ENCODE);
} catch (IOException e) {
System.out.println("[ERROR] Че-то пошло не так, давай еще раз");
break;
} catch (Exception e) {
System.out.println(e.getMessage());
break;
}
System.out.println(
"Результат шифрования:\n"
+ "[ "
+ result
+ " ]"
);
}
break;
case 2:
{
try {
System.out.print("Введите шифротекст: ");
text = reader.readLine();
System.out.print("Введите ключ: ");
key = reader.readLine();
// Operation.DECODE влияет только на текст логирования функции disposableNotebook
result = Crypto.disposableNotebook(text, key, Operation.DECODE);
} catch (IOException e) {
System.out.println("[ERROR] Че-то пошло не так, давай еще раз");
break;
} catch (Exception e) {
System.out.println(e.getMessage());
break;
}
System.out.println(
"Результат расшифровки:\n"
+ "[ "
+ result
+ " ]"
);
}
break;
case 3:
System.out.println("Всего доброго!");
break;
default:
System.out.println("[ERROR] Моя твоя не понимать");
break;
}
}
}
}
Пример использования
Попробуем зашифровать открытый текст «SECRET» ключом «po7suq»:

Как мы видим, в процессе шифрования выводятся некоторые логи, такие как байты текста, которые представляют из себя целочисленное представление каждого символа этого текста, а также бинарная строка текста и т.д.
В результате шифрования получился шифротекст:»#*t!0%». Теперь мы можем попробовать расшифровать данный шифротекст, используя тот же ключ:

В результате получаем ту же строку, которую изначально шифровали, а именно «SECRET».
Заключение
Надеюсь у меня получилось показать вам реализацию алгоритма одноразового блокнота в этой короткой статье.
Ссылка на GitHub — https://github.com/ZhadanD/example-crypto