Simple Encryption in Ruby without external gems

前端 未结 9 926
庸人自扰
庸人自扰 2020-12-08 07:43

I need a simple encryption for some text strings. I want to create coupon codes and make them look cool so subsequently created code should look very different. (And besides

相关标签:
9条回答
  • 2020-12-08 08:03

    Optional method for encryption and decryption

    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)
    
    0 讨论(0)
  • 2020-12-08 08:10

    You could use 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"
    

    But the intermediate form doesn't lend itself well to printing


    Given your thought that it would be nice if the intermediate form was the same length, you might just use a simple map of one char to another.

    PLEASE UNDERSTAND THAT THIS IS NOT SECURE

    You can easily brute force the key, but it seems to be congruent with your requirements.

    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"
    
    0 讨论(0)
  • 2020-12-08 08:17

    If you don't need real encryption, you can use a simple cipher. (This can be used when you don't need security, or to encrypt short random/one-off strings.)

    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
    
    0 讨论(0)
  • 2020-12-08 08:17

    The solution is kind of from scratch but based on this: https://math.stackexchange.com/questions/9508/looking-for-a-bijective-discrete-function-that-behaves-as-chaotically-as-possib

    The simplest way presented is using a * x + b (mod 2^n)

    Obviously this is no real encryption and really only useful if you want to create sequential coupon codes without using much code.

    So to implement this, you first need to pick a, b and n. (a must be odd) For example a=17, b=37 and n=27. Also we need to find "a^(-1)" on "mod 2^n". It's possible to do this on https://www.wolframalpha.com using the ExtendedGcd function:

    So the inverse of a is therefore 15790321. Putting all this together:

    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
    

    And now you can do:

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

    Obviously we want the coupon codes to look cool. So 2 extra things are needed: start the sequence at 4000 or so, so the codes are longer. Also convert them into something alpha-numeric, that's also an easy one with Ruby:

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

    One nice additional property is that consecutive numbers are different enough that guessing is practically impossible. Of course we assume that the users are not crypto analysts and if someone indeed guesses a valid number, it's not the end of the world: :-)

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

    Let's try to naively "hack" it by counting from 1gie to 1gif:

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

    That's completely out of range, there are just 2000 or so coupons anyways - not a million. :-) Also if I remember correctly one can experiment a bit with the parameters, so subsequent numbers look more chaotic.

    (Pick a larger n for longer codes and vice-versa. Base 36 means 6 bits are needed for each character ("Math.log(36, 2)"). So n=27 allows for up to 5 characters.)

    0 讨论(0)
  • 2020-12-08 08:18

    Do you really want to trust the user to give you back the right value? If you trust what the client gives you back and the user figures out your encryption scheme you'll be using data they provide. That sounds like a very bad idea.

    It's not clear to me why you don't want to give them a key into a database that maps a random numbers, perhaps with some error correction properties, to the coupon discounts. That way you have control of the final result. They provide you a key, you look up the associated coupon and apply the coupon. In this way you're only using your own data and if you want to remove a coupon it's all on the server side.

    If you keep all the key-codes you can also check that new codes are different from previously released ones.

    0 讨论(0)
  • 2020-12-08 08:19

    For basic encoding/decode purpose I guess ruby's inbuilt Base64 library can be handy:

    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" 
    

    It also has the urlsafe version methods in case the encoded strings are to be used in urls.

    Reference: http://ruby-doc.org/stdlib-2.3.0/libdoc/base64/rdoc/Base64.html

    0 讨论(0)
提交回复
热议问题