Простое шифрование в Ruby без внешних камней - PullRequest
20 голосов
/ 09 ноября 2010

Мне нужно простое шифрование для некоторых текстовых строк. Я хочу создать купонные коды и сделать их классными, чтобы впоследствии созданный код выглядел совсем иначе. (И, кроме того, что он выглядит круто, угадать код нелегко.) Но я хочу иметь возможность снова расшифровать их. Таким образом, алгоритм должен быть обратимым.

Я уже попробовал кое-что с движущимися битами, так что они уже выглядят случайными. Но два последующих кода (только один бит по-разному), конечно, выглядят очень похоже.

Есть предложения? Я хотел бы сделать это без использования внешних драгоценных камней.

Philip

Ответы [ 9 ]

42 голосов
/ 09 ноября 2010

Вы можете использовать OpenSSL :: Cypher

# for more info, see http://ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/Cipher.html

require 'openssl'
require 'digest/sha1'

# create the cipher for encrypting
cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
cipher.encrypt

# you will need to store these for later, in order to decrypt your data
key = Digest::SHA1.hexdigest("yourpass")
iv = cipher.random_iv

# load them into the cipher
cipher.key = key
cipher.iv = iv

# encrypt the message
encrypted = cipher.update('This is a secure message, meet at the clock-tower at dawn.')
encrypted << cipher.final
puts "encrypted: #{encrypted}\n"

# now we create a sipher for decrypting
cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
cipher.decrypt
cipher.key = key
cipher.iv = iv

# and decrypt it
decrypted = cipher.update(encrypted)
decrypted << cipher.final
puts "decrypted: #{decrypted}\n"

Но промежуточная форма плохо подходит для печати


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

ПОЖАЛУЙСТА, ПОНИМАЙТЕ, ЧТО ЭТО НЕ НАДЕЖНО

Вы можете легко грубым нажатием клавиши, но, похоже, она соответствует вашим требованиям.

class Cipher

  def initialize(shuffled)
    normal = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a + [' ']
    @map = normal.zip(shuffled).inject(:encrypt => {} , :decrypt => {}) do |hash,(a,b)|
      hash[:encrypt][a] = b
      hash[:decrypt][b] = a
      hash
    end
  end

  def encrypt(str)
    str.split(//).map { |char| @map[:encrypt][char] }.join
  end

  def decrypt(str)
    str.split(//).map { |char| @map[:decrypt][char] }.join
  end

end

# pass the shuffled version to the cipher
cipher = Cipher.new ["K", "D", "w", "X", "H", "3", "e", "1", "S", "B", "g", "a", "y", "v", "I", "6", "u", "W", "C", "0", "9", "b", "z", "T", "A", "q", "U", "4", "O", "o", "E", "N", "r", "n", "m", "d", "k", "x", "P", "t", "R", "s", "J", "L", "f", "h", "Z", "j", "Y", "5", "7", "l", "p", "c", "2", "8", "M", "V", "G", "i", " ", "Q", "F"]

msg = "howdy pardner"

crypted = cipher.encrypt msg
crypted # => "1IzXAF6KWXvHW"

decrypted = cipher.decrypt crypted
decrypted # => "howdy pardner"
16 голосов
/ 30 июля 2015

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

ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

#generated with ALPHABET.split('').shuffle.join
ENCODING = "MOhqm0PnycUZeLdK8YvDCgNfb7FJtiHT52BrxoAkas9RWlXpEujSGI64VzQ31w"

def encode(text)
  text.tr(ALPHABET, ENCODING)
end

def decode(text)
  text.tr(ENCODING, ALPHABET)
end
7 голосов
/ 17 февраля 2016

Для базовых целей кодирования / декодирования, я думаю, встроенная библиотека Ruby Base64 может быть полезна:

2.2.1 :001 > require 'base64'
 => true 
2.2.1 :002 > str = "abc@example.com"
 => "abc@example.com" 
2.2.1 :003 > Base64.encode64(str)
 => "YWJjQGV4YW1wbGUuY29t\n" 

Она также имеет методы версии urlsafe на случай использования закодированных строкв URL.

Ссылка: http://ruby -doc.org / stdlib-2.3.0 / libdoc / base64 / rdoc / Base64.html

3 голосов
/ 09 мая 2018

Необязательный метод шифрования и дешифрования

gem 'activesupport'

require 'active_support'

key = SecureRandom.random_bytes(32)
crypt = ActiveSupport::MessageEncryptor.new(key)
encrypted_data = crypt.encrypt_and_sign("your password")
password = crypt.decrypt_and_verify(encrypted_data)
3 голосов
/ 09 ноября 2010

Я могу порекомендовать вам утилиты uuencode и uudecode Вы можете использовать их со стандартным функциональным пакетом ruby:

str = "\007\007\002\abcde"
new_string = [str].pack("u")
original = new_string.unpack("u")

(образец из рубинового пути Хэла Фултона)

2 голосов
/ 13 ноября 2010

Решение отчасти с нуля, но основано на этом: https://math.stackexchange.com/questions/9508/looking-for-a-bijective-discrete-function-that-behaves-as-chaotically-as-possib

Простейшим представленным способом является использование a * x + b (mod 2^n)

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

Итак, чтобы реализовать это, сначала нужно выбрать a, b и n. (a должно быть нечетным) Например a=17, b=37 и n=27. Также нам нужно найти "a^(-1)" в "mod 2 ^ n". Это можно сделать на https://www.wolframalpha.com с помощью функции ExtendedGcd:

enter image description here

Таким образом, обратное значение a равно 15790321. Собираем все это вместе:

A=17
B=37
A_INV=15790321

def encrypt(x)
  (A*x+B)%(2**27)
end

def decrypt(y)
  ((y-B)*A_INV)%(2**27)
end

А теперь вы можете сделать:

irb(main):038:0> encrypt(4)
=> 105
irb(main):039:0> decrypt(105)
=> 4

Очевидно, мы хотим, чтобы коды купонов выглядели круто. Таким образом, необходимы 2 дополнительные вещи: начните последовательность с 4000 или около того, чтобы коды были длиннее. Также конвертируйте их во что-то буквенно-цифровое, это также легко с Ruby:

irb(main):050:0> decrypt("1ghx".to_i(36))
=> 4000
irb(main):051:0> encrypt(4000).to_s(36)
=> "1ghx"

Еще одним приятным дополнительным свойством является то, что последовательные числа достаточно разные, поэтому угадывание практически невозможно. Конечно, мы предполагаем, что пользователи не являются криптоаналитиками, и если кто-то действительно угадает действительное число, это не конец света:: -)

irb(main):053:0> encrypt(4001).to_s(36)
=> "1gie"
irb(main):054:0> decrypt("1gie".to_i(36))
=> 4001

Давайте попробуем наивно «взломать» его, считая от 1gie до 1gif:

irb(main):059:0* decrypt("1gif".to_i(36))
=> 15794322

Это совершенно за пределами досягаемости, так или иначе, всего около 2000 купонов, а не миллион. :-) Также, если я правильно помню, можно немного поэкспериментировать с параметрами, поэтому последующие числа выглядят более хаотично.

(Выберите большее n для более длинных кодов и наоборот. База 36 означает, что 6 бит необходимо для каждого символа ("Math.log(36, 2)"). Таким образом, n=27 допускает до 5 символов .)

2 голосов
/ 09 ноября 2010

Вы действительно хотите, чтобы пользователь вернул вам правильное значение? Если вы доверяете тому, что клиент возвращает вам, и пользователь выясняет вашу схему шифрования, вы будете использовать данные, которые они предоставляют. Это звучит как очень плохая идея.

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

Если вы сохраните все коды клавиш, вы также можете проверить, отличаются ли новые коды от ранее выпущенных.

0 голосов
/ 04 июля 2019

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

require 'glogin/codec'
codec = GLogin:Codec.new('the secret')
encrypted = codec.encrypt('Hello, world!')
decrypted = codec.decrypt(encrypted)

Он основан на OpenSSL.

0 голосов
/ 31 июля 2017

Вы можете проверить все различные способы шифрования / дешифрования, используя ruby ​​в этом списке: https://gist.github.com/iufuenza/183a45c601a5c157a5372c5f1cfb9e3e

Если вы не хотите использовать гем, я бы полностью порекомендовал Openssl как наиболее безопасный, который также очень прост в реализации, поскольку имеет очень хорошую поддержку Ruby.

...