Реализация OneTimePad не выполняется. Может быть, проблема с потоком? - PullRequest
2 голосов
/ 16 сентября 2010

У меня было немного времени, и я решил создать одноразовый блокнот только для развлечения и самообразования. Теперь я получаю странное поведение данных. Это сводит меня с ума ^^. Не могли бы вы помочь мне? Заранее спасибо.

Существует метод шифрования, который принимает в качестве аргументов:

  • и InputStream для простого текста
  • и OutputStreams для зашифрованного текста
  • и OutputStreams для ключа.

Существует метод расшифровки, который принимает в качестве аргументов:

  • и InputStream для зашифрованного текста
  • и InputStream для ключа
  • и OutputStreams для простого текста.

Существует основной метод для тестирования и отладки кода. Вот класс:

import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.security.SecureRandom;

 public class MyPad {

  public static void encrypt(InputStream plainTextInputStream, OutputStream cipherTextOutputStream, OutputStream keyOutputStream) {
   int plainTextByte;
   SecureRandom random = new SecureRandom(); 
   System.out.println("plain\tkey\tcipher");
   try {
    while((plainTextByte = plainTextInputStream.read()) != -1){
     int keyByte = random.nextInt(256);
     int cipherTextByte = (plainTextByte + keyByte) % 256;
     System.out.println(plainTextByte + "\t" + keyByte + "\t" + cipherTextByte);
     cipherTextOutputStream.write(cipherTextByte);
     keyOutputStream.write(keyByte);
    }
    plainTextInputStream.close();
    cipherTextOutputStream.close();
    keyOutputStream.close();
   } catch (IOException e) {
    e.printStackTrace();
   }
  }

  public static void decrypt(InputStream cipherTextInputStream, InputStream keyInputStream, OutputStream plainTextOutputStream) {
   int cipherTextByte;
   System.out.println("plain\tkey\tcipher");
   try {
    while((cipherTextByte = cipherTextInputStream.read()) != -1){
     int keyByte = keyInputStream.read();
     int plainTextByte = Math.abs(cipherTextByte - keyByte) % 256;
     System.out.println(plainTextByte + "\t" + keyByte + "\t" + cipherTextByte);
     plainTextOutputStream.write(plainTextByte);
    }
    cipherTextInputStream.close();
    keyInputStream.close();
    plainTextOutputStream.close();
   } catch (IOException e) {
    e.printStackTrace();
   }
  }

  public static void main(String[] args) {
   String plainText = "This is my plain text.";
   InputStream plainTextInputStream = new ByteArrayInputStream(plainText.getBytes());
   ByteArrayOutputStream cipherTextOutputStream = new ByteArrayOutputStream();
   ByteArrayOutputStream keyOutputStream = new ByteArrayOutputStream();

   System.out.println("------------------------------------ encrypting");
   encrypt(plainTextInputStream, cipherTextOutputStream, keyOutputStream);

   String cipherText = cipherTextOutputStream.toString();
   String key = keyOutputStream.toString();
   System.out.println("plaintext:\t" + plainText);
   System.out.println("ciphertext:\t" + cipherText);
   System.out.println("key:\t" + key);
   InputStream cipherTextInputStream = new ByteArrayInputStream(cipherText.getBytes());
   InputStream keyInputStream = new ByteArrayInputStream(key.getBytes());
   ByteArrayOutputStream plainTextOutputStream = new ByteArrayOutputStream();

   System.out.println("------------------------------------ decrypting");
   decrypt(cipherTextInputStream, keyInputStream, plainTextOutputStream);

   plainText = plainTextOutputStream.toString();
   System.out.println("plaintext:\t" + plainText);
  }

 }

Теперь вот проблема, которая у меня есть. Я зашифровал простой текст и расшифровал его немедленно, но зашифрованный простой текст не совпадает с оригиналом. Я сделал некоторый вывод для отладки, и кажется, что данные, которые я написал во время шифрования, не совпадают с данными, которые я читал во время дешифрования. Посмотрите на вывод самостоятельно:

 ------------------------------------ encrypting
 plain key cipher
 84 25 109
 104 239 87
 105 86 191
 115 74 189
 32 100 132
 105 17 122
 115 211 70
 32 147 179
 109 104 213
 121 118 239
 32 139 171
 112 244 100
 108 196 48
 97 181 22
 105 226 75
 110 94 204
 32 156 188
 116 92 208
 101 91 192
 120 165 29
 116 177 37
 46 49 95
 plaintext: This is my plain text.
 ciphertext: mW���zF���d0K̼��%_
 key: �VJdӓhv��ĵ�^�\[��1
 ------------------------------------ decrypting
 plain key cipher
 84 25 109
 152 239 87
 48 191 239
 2 189 191
 103 86 189
 165 74 239
 91 100 191
 172 17 189
 28 211 239
 44 147 191
 85 104 189
 4 118 122
 169 239 70
 48 191 239
 2 189 191
 50 239 189
 48 191 239
 2 189 191
 7 196 189
 58 181 239
 48 239 191
 2 191 189
 89 189 100
 46 94 48
 217 239 22
 116 191 75
 15 189 204
 96 92 188
 148 91 239
 48 239 191
 2 191 189
 50 189 239
 48 239 191
 2 191 189
 160 189 29
 12 49 37
 96 -1 95
 plaintext: T�0g�[�,U�020:0Y.�t`�020�`

Это выглядит очень странно для меня. Любые предложения, откуда эта разница?

Заранее спасибо.

Ответы [ 3 ]

2 голосов
/ 16 сентября 2010

Есть две проблемы:

  1. Вы используете String # getBytes, чтобы получить байты для обратной связи. Это означает, что они прошли цикл декодирования и кодирования строк.Обратите внимание, что последовательность байтов ключа не одинакова при шифровании и дешифровании.Вместо этого вы должны использовать ByteArrayOutputStream # toByteArray
  2. Math.abs(cipherTextByte - keyByte) % 256 неправильно.(0 - 1) (мод 256) = 255, а не 1. Вместо этого следует использовать (256 + cipherTextByte - keyByte) % 256
1 голос
/ 16 сентября 2010

Вместо сложения и вычитания, а затем% 256 можно использовать xor. Это решило бы ту же задачу с меньшей математической странностью. a ^ b не заботится о знаках или остатках или любом другом мусоре, никогда не выходит за пределы своих аргументов (byte1 ^ byte2 всегда будет иметь размер в байтах) и легко обратим.

А вот почему у вас проблемы: когда вы используете abs, чтобы обернуть свои числа, вы в конечном итоге нарушаете определенные допущения, которые совпадают с числами из двух дополнений. (abs(-x) и 256-x не равны в большинстве случаев.) Это делает вашу математику шаткой, поскольку то, что должно быть, знаковый бит теряется в случайном порядке - и другие биты ошибочно переворачиваются из-за этого и могут ' его нельзя перевернуть, потому что вы выбросили знак с abs.

Например, предположим, что ваш байт открытого текста равен 65 («А», если вам не безразлично), и ваш ГСЧ получает 192 для байта пэда. abs(65+192) % 256 даст вам 1 в качестве вашего "зашифрованного текста". Когда вы расшифровываете, однако, 1-192 == -191. В дополнении до двух в 8 битах это соответствует 65, но abs(1-192) дает вам 191.

Вы бы лучше использовать (cipherTextByte + (256 - keyByte)) % 256. В этом примере 1 + (256-192) == 1 + 64 == 65.

Кроме того, как уже упоминалось, после того, как вы зашифровали байты, они должны рассматриваться как просто - байтов . Они больше не образуют строку; они есть и должны всегда (точнее, до дешифрования) быть последовательностью байтов. Кодировки и тому подобное могут тонко изменить данные, добавив сюда байт или преобразовав что-то там, и вы можете получить мусор при попытке расшифровать его. Но ваша самая большая проблема в вашем примере - это математика, а не проблемы кодирования.

0 голосов
/ 10 августа 2013

Может быть проще, с точки зрения кодирования, использовать одноразовую производную от пэдов, где отдельные символы Unicode используются вместо одиночных битов?

Спецификация находится на http://longterm.softf1.com/specifications/txor/index.html

Безопасность и доказательства почти одинаковы, но нет необходимости обрабатывать какие-либо части кодировки символов, UTF-8 и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...