Простое шифрование в Ruby без внешних камней
Мне нужно простое шифрование для некоторых текстовых строк. Я хочу создать коды купонов и заставить их выглядеть круто, поэтому впоследствии созданный код должен выглядеть совсем по-другому. (И, кроме того, выглядя круто, это не должно быть легко угадать код.) Но я хочу иметь возможность расшифровать их снова. Таким образом, алгоритм должен быть обратимым.
Я пробовал некоторые вещи с движущимися битами, чтобы они выглядели как бы случайные. Но два последующих кода (только один бит разный), конечно, очень похожи.
Любые предложения? Я хотел бы сделать это без использования внешних драгоценных камней.
Филипп
Ответы
Ответ 1
Решение отчасти с нуля, но основано на этом: 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
Вы можете использовать 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"
Но промежуточная форма не поддается печати
Учитывая, что было бы хорошо, если бы промежуточная форма была одинаковой длины, вы могли бы просто использовать простую карту одного char для другого.
ПОЖАЛУЙСТА, ПОНИМАЙТЕ, ЧТО ЭТО НЕ БЕЗОПАСНО
Вы можете легко перетащить ключ, но он, похоже, соответствует вашим требованиям.
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"
Ответ 3
Если вам не нужно настоящее шифрование, вы можете использовать простой шифр. (Это можно использовать, когда вам не нужна безопасность, или для шифрования коротких случайных/одноразовых строк.)
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
Ответ 4
Для базовой цели кодирования/декодирования я предполагаю, что встроенная библиотека Base64 Ruby может быть удобной:
2.2.1 :001 > require 'base64'
=> true
2.2.1 :002 > str = "[email protected]"
=> "[email protected]"
2.2.1 :003 > Base64.encode64(str)
=> "YWJjQGV4YW1wbGUuY29t\n"
Он также имеет методы версии urlsafe, если кодированные строки должны использоваться в URL-адресах.
Ссылка: http://ruby-doc.org/stdlib-2.3.0/libdoc/base64/rdoc/Base64.html
Ответ 5
Я могу порекомендовать вам uuencode и uudecode utils
вы можете использовать их в стандартном рубиновом пакете функций:
str = "\007\007\002\abcde"
new_string = [str].pack("u")
original = new_string.unpack("u")
(образец из Рубинового пути Хала Фултона)
Ответ 6
Дополнительный метод для шифрования и дешифрования
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)
Ответ 7
Вы действительно хотите доверять пользователю, чтобы вернуть правильное значение? Если вы доверяете тому, что клиент возвращает вам, а пользователь определяет схему шифрования, вы будете использовать данные, которые они предоставляют. Это звучит как очень плохая идея.
Мне непонятно, почему вы не хотите давать им ключ в базу данных, которая отображает случайные числа, возможно, с некоторыми свойствами исправления ошибок, на купонные скидки. Таким образом, вы контролируете конечный результат. Они предоставляют вам ключ, вы просматриваете соответствующий купон и применяете купон. Таким образом, вы используете только свои собственные данные, и если вы хотите удалить купон, все это на стороне сервера.
Если вы сохраните все коды клавиш, вы также можете проверить, что новые коды отличаются от ранее выпущенных.
Ответ 8
Вы можете проверить все различные способы шифрования/дешифрования с помощью ruby в этом контексте: https://gist.github.com/iufuenza/183a45c601a5c157a5372c5f1cfb9e3e
Если вы не хотите использовать драгоценный камень, я бы полностью рекомендовал Openssl как наиболее безопасный, который также очень легко реализовать, так как он имеет очень хорошую поддержку Ruby.
Ответ 9
Я знаю, что вы ищете шифрование без драгоценных камней, но все же хотите предложить его тем, кто здесь и не беспокоится об использовании внешних драгоценных камней. Попробуйте glogin
(я автор):
require 'glogin/codec'
codec = GLogin:Codec.new('the secret')
encrypted = codec.encrypt('Hello, world!')
decrypted = codec.decrypt(encrypted)
Он основан на OpenSSL и Base58.