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

cd4f7a1053ce147d048518c22e83916e.png

Введение

В этой статье будет показан пример реализации алгоритма одноразового блокнота на чистой 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»:

Шифрование текста
Шифрование текста «SECRET»

Как мы видим, в процессе шифрования выводятся некоторые логи, такие как байты текста, которые представляют из себя целочисленное представление каждого символа этого текста, а также бинарная строка текста и т.д.

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

Расшифровка шифротекста
Расшифровка шифротекста »#*t!0%»

В результате получаем ту же строку, которую изначально шифровали, а именно «SECRET».

Заключение

Надеюсь у меня получилось показать вам реализацию алгоритма одноразового блокнота в этой короткой статье.

Ссылка на GitHub — https://github.com/ZhadanD/example-crypto

© Habrahabr.ru