Генерация числа при шифровании строки

#scala #encryption

#scala #шифрование

Вопрос:

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

Я много искал в Интернете, но не смог найти никого, кто бы это делал. Поэтому я хотел бы воспользоваться помощью экспертов по StackOverflow.

Насколько мне известно, шифрование строки в число немного сложно, но проект, в котором я работаю, требует этого.

Любые библиотеки, которые делают это, или любые алгоритмы решат мою проблему.

Вот код, который у меня есть до сих пор

 import java.security.MessageDigest
import java.util
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec
import org.apache.commons.codec.binary.Base64


    object DataMaskUtil {

        def encrypt(key: String, value: String): String = {
          val cipher: Cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
          cipher.init(Cipher.ENCRYPT_MODE, keyToSpec(key))
           Base64.encodeBase64URLSafeString(cipher.doFinal(value.getBytes("UTF-8")))
        }

        def decrypt(key: String, encryptedValue: String): String = {
          val cipher: Cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING")
          cipher.init(Cipher.DECRYPT_MODE, keyToSpec(key))
          new String(cipher.doFinal(Base64.decodeBase64(encryptedValue)))
        }

        def keyToSpec(key: String): SecretKeySpec = {
          var keyBytes: Array[Byte] = (SALT   key).getBytes("UTF-8")
          val sha: MessageDigest = MessageDigest.getInstance("SHA-1")
          keyBytes = sha.digest(keyBytes)
          keyBytes = util.Arrays.copyOf(keyBytes, 16)
          new SecretKeySpec(keyBytes, "AES")
        }

         private val SALT: String =
        "jMhKlOuJnM34G6NHkqo9V010GhLAqOpF0BePojHgh1HgNg8^72k"

    }
  

Используя идею, предложенную Маартеном Бодевесом

 object Util2 {

  def encrypt(value: String): BigInteger = {
    val ct = value.getBytes()
    val abyte = 0x4D.toByte
    val byteBuffer = ByteBuffer.allocate(2 ct.length)
    byteBuffer.put(abyte).put(abyte)
    byteBuffer.put(ct)
    val number = new BigInteger(byteBuffer.array())
    number
  }

  def decrypt(ctAsNumber: BigInteger): String = {
    if (ctAsNumber.signum < 0 || ctAsNumber.bitLength < 15) throw new IllegalArgumentException("Magic of ciphertext number doesn't match")
    import java.nio.ByteBuffer
    val fixed = ByteBuffer.allocate((ctAsNumber.bitLength   java.lang.Byte.SIZE - 1) / java.lang.Byte.SIZE)
    fixed.put(ctAsNumber.toByteArray)
    fixed.flip()
    val ct = new Array[Byte](fixed.remaining())
    fixed.get(ct)
    new String(ct)
  }
}
  

Когда я тестирую функции, вывод дополняется «ММ» перед строкой

 object MainClass {

      def main(args: Array[String]): Unit ={
        val encrypt = Util2.encrypt("Hi")
        println("The encrypted string is :: " encrypt)

        val decrypt = Util2.decrypt(encrypt)
        println("The decrypted string is :: " decrypt)

      }

    }
  

вывод

 The encrypted string is :: 1296910441
The decrypted string is :: MMHi
  

Комментарии:

1. Предполагается, что ваш encrypt метод возвращает целое число, в то время как ваш decrypt метод ожидает строку, представляющую зашифрованные данные, после того, как вы зашифруете некоторые данные, вы получите обратно массив байтов, который вы можете кодировать на основе 64, для его расшифровки вы декодируете на основе 64 и пытаетесь расшифровать байты.

2. спасибо, я это исправил. Я скопировал код, пытаясь получить целое число.

3. Ваш код по-прежнему пытается преобразовать зашифрованную строку в целое число, пожалуйста, сообщите, какие результаты вы получаете после ее изменения.

4. Для создания ключа из пароля следует использовать KDF на основе пароля, такой как PBKDF2. Ключи являются двоичными, поэтому ваши keyToSpec могут быть небезопасными. Режим ECB, конечно, также опасен и может еще больше расширить зашифрованный текст из-за заполнения, совместимого с PKCS # 5/7. Не стройте свой код, используйте типы так, как они предназначены.

Ответ №1:

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

И, поскольку это язык, работающий на JVM, мы также будем использовать big endian.

Отрицательных значений и нулевых байтов можно легко избежать, добавив значения бит / байт в левую часть. Мы могли бы, например, использовать хорошо известные значения, которые также позволяют вам иметь некоторую минимальную защиту от искажения номера зашифрованного текста. Тогда вы получите, в Java:

 private static BigInteger ciphertextToNumber(byte[] ct) {
    ByteBuffer fixed = ByteBuffer.allocate(2   ct.length);
    fixed.put((byte) 0x4D).put((byte) 0x42);
    fixed.put(ct);
    BigInteger number = new BigInteger(fixed.array());
    return number;
}

private static byte[] numberToCiphertext(BigInteger ctAsNumber) {
    // if the number is negative then the buffer will be too small
    if (ctAsNumber.signum() < 0 || ctAsNumber.bitLength() < 15) {
        throw new IllegalArgumentException("Magic of ciphertext number doesn't match");
    }
    ByteBuffer fixed = ByteBuffer.allocate((ctAsNumber.bitLength()   Byte.SIZE - 1) / Byte.SIZE);
    fixed.put(ctAsNumber.toByteArray());
    fixed.flip();
    if (fixed.get() != (byte) 0x4D || fixed.get() != (byte) 0x42) {
        throw new IllegalArgumentException("Magic of ciphertext number doesn't match");
    }
    byte[] ct = new byte[fixed.remaining()];
    fixed.get(ct);
    return ct;
}
  

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

Я оставлю это на ваше усмотрение для преобразования в Scala.


Другая идея состоит в том, чтобы использовать I2OSP или OS2IP, как определено в RFC RSA, но обратите внимание, что заполнение RSA уже выполняет тот же тип заполнения слева, чтобы убедиться, что преобразование массива байтов в целое число обрабатывается корректно. Кроме того, зашифрованный текст RSA всегда имеет тот же размер, что и модуль, в то время как шифрование AES может возвращать разные размеры.

Комментарии:

1. Я попробовал ваш код для преобразования зашифрованного текста в число и числа в зашифрованный текст. Но я получаю исключение BufferUnderflowException. Не могли бы вы мне помочь.

2. Конечно, но не без точного кода и трассировки стека. Может быть, вы могли бы (временно) добавить его в свой вопрос? Я был в своем каяке, хороший повод ненадолго отключиться 🙂

3. В конце я добавил свой код и трассировку стека к вопросу

4. oos.writeObject(fixed.remaining()) remaining возвращает целое число. Последующая обработка строк совершенно не нужна и неверна. Почему бы не попробовать создать точные методы в Scala и сначала протестировать их отдельно? Вам не нужны потоки и тому подобное. Хех, теперь у вас есть MM как магия вместо MB — это была просто шутка, вы можете использовать один байт (в диапазоне 01 до 7F ) : P

5. @wandermonk; Вы вставили 0x4D (дважды), но забыли убрать их после flip() .