Skip to content

Instantly share code, notes, and snippets.

@wteuber
Last active May 10, 2024 12:04
Show Gist options
  • Star 65 You must be signed in to star a gist
  • Fork 41 You must be signed in to fork a gist
  • Save wteuber/5318013 to your computer and use it in GitHub Desktop.
Save wteuber/5318013 to your computer and use it in GitHub Desktop.
Simply encrypt and decrypt Strings in Ruby.
require 'openssl'
class String
def encrypt(key)
cipher = OpenSSL::Cipher.new('DES-EDE3-CBC').encrypt
cipher.key = Digest::SHA1.hexdigest key
s = cipher.update(self) + cipher.final
s.unpack('H*')[0].upcase
end
def decrypt(key)
cipher = OpenSSL::Cipher.new('DES-EDE3-CBC').decrypt
cipher.key = Digest::SHA1.hexdigest key
s = [self].pack("H*").unpack("C*").pack("c*")
cipher.update(s) + cipher.final
end
end
puts plain = 'confidential' # confidential
puts key = 'secret' # secret
puts cipher = plain.encrypt(key) # 5C6D4C5FAFFCF09F271E01C5A132BE89
puts cipher.decrypt('guess') # raises OpenSSL::Cipher::CipherError
puts cipher.decrypt(key) # confidential
@leduytung
Copy link

Nice :) , 2017 and still works

@xiaohui-zhangxh
Copy link

Very helpful, thanks

@bater
Copy link

bater commented Sep 1, 2017

I got key must be 24 bytes message at this line:

cipher.key = Digest::SHA1.hexdigest key

so I change to like this

key = cipher.random_key
cipher.key = key

It will always fit.

Reference http://ruby-doc.org/stdlib-2.0.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html

@grammakov
Copy link

To avoid deprecation messages change

cipher = OpenSSL::Cipher::Cipher.new('DES-EDE3-CBC').decrypt

to

cipher = OpenSSL::Cipher.new('DES-EDE3-CBC').decrypt

@TiredOfProgramming
Copy link

Doesn't work for me. Getting no implicit conversion of Object into String (TypeError) on line s = [self].pack("H*").unpack("C*").pack("c*")

@wteuber
Copy link
Author

wteuber commented Aug 23, 2018

@TiredOfProgramming
The provided code monkey-patches the class String and works only there. If you want to use it another class (e.g. Object), you need to define the string to encrypt or decrypt separately, e.g.

require 'openssl'

def encrypt(plain_text, key)
  cipher = OpenSSL::Cipher.new('DES-EDE3-CBC').encrypt
  cipher.key = Digest::SHA1.hexdigest key
  s = cipher.update(plain_text) + cipher.final

  s.unpack('H*')[0].upcase
end

def decrypt(cipher_text, key)
  cipher = OpenSSL::Cipher.new('DES-EDE3-CBC').decrypt
  cipher.key = Digest::SHA1.hexdigest key
  s = [cipher_text].pack("H*").unpack("C*").pack("c*")

  cipher.update(s) + cipher.final
end

puts plain = 'confidential'            # confidential
puts key = 'secret'                    # secret
puts cipher = encrypt(plain, key)      # 5C6D4C5FAFFCF09F271E01C5A132BE89

puts decrypt(cipher, 'guess')          # raises OpenSSL::Cipher::CipherError
puts decrypt(cipher, key)              # confidential

I hope you find that helpful.

@mikosullivan
Copy link

mikosullivan commented Nov 11, 2019

When I try to set encrypter.key I always get this error message: key must be 24 bytes. Here's my complete script:
#!/usr/bin/ruby -w
require 'openssl'
key = 'my-secret-key'
cipher = OpenSSL::Cipher.new('DES-EDE3-CBC')
encrypter = cipher.encrypt
encrypter.key = Digest::SHA1.hexdigest(key)

I'm running ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu]. The OS is Ubuntu 18.04.3 LTS.

I should also mention that this script works fine on an Ubuntu 17 machine with Ruby version ruby 2.3.3p222 (2016-11-21) [x86_64-linux-gnu]. The OS is Ubuntu 17.10.

Any help is appreciated.

@wteuber
Copy link
Author

wteuber commented Nov 13, 2019

@mikosullivan key is used in different context. In this gist, key is a cipher "password". You are trying to set the "encryption key". Consider using your key as cipher password and generate a secure random key. Check out Ruby's documentation for encrypt and key and the section Choosing a key.

If you absolutely need to use passwords as encryption keys, you should use Password-Based Key Derivation Function 2 (PBKDF2) by generating the key with the help of the functionality provided by OpenSSL::PKCS5.pbkdf2_hmac_sha1 or OpenSSL::PKCS5.pbkdf2_hmac.

Although there is #pkcs5_keyivgen, its use is deprecated and it should only be used in legacy applications because it does not use the newer PKCS#5 v2 algorithms.

I suggest, you don't use a password, but generate a secure (random) key. Store it somewhere safe, e.g. base64-encoded.

require 'openssl'
key = 'my-secret-key'
cipher = OpenSSL::Cipher.new('DES-EDE3-CBC')
encrypter = cipher.encrypt
encrypter.key_len
# => 24
encrypter.key = encrypter.random_key

@ogaida
Copy link

ogaida commented Aug 5, 2020

my complete solution is:

require 'openssl'

class String
  def encrypt(key)
    cipher = OpenSSL::Cipher::AES.new(128, :CBC).encrypt
    cipher.key = key
    cipher.update(self) + cipher.final
  end

  def decrypt(key)
    cipher = OpenSSL::Cipher::AES.new(128, :CBC).decrypt
    cipher.key = key
    cipher.update(self) + cipher.final
  end
end

keygen = OpenSSL::Cipher::AES.new(128, :CBC).encrypt
key = keygen.random_key

encrypted = "geheim".encrypt(key)
encrypted.decrypt(key)            # => geheim

@shqear93
Copy link

shqear93 commented Nov 4, 2022

avoiding key must be 24 bytes

def encrypt(key)
        cipher = OpenSSL::Cipher.new("aes-256-cbc").encrypt
        cipher.key = Digest::MD5.hexdigest key
        s = cipher.update(self) + cipher.final

        s.unpack('H*')[0].upcase
      end

      def decrypt(key)
        cipher = OpenSSL::Cipher.new('aes-256-cbc').decrypt
        cipher.key = Digest::MD5.hexdigest key
        s = [self].pack("H*").unpack("C*").pack("c*")

        cipher.update(s) + cipher.final
      end

@gustavo-iha
Copy link

avoiding key must be 24 bytes

def encrypt(key)
        cipher = OpenSSL::Cipher.new("aes-256-cbc").encrypt
        cipher.key = Digest::MD5.hexdigest key
        s = cipher.update(self) + cipher.final

        s.unpack('H*')[0].upcase
      end

      def decrypt(key)
        cipher = OpenSSL::Cipher.new('aes-256-cbc').decrypt
        cipher.key = Digest::MD5.hexdigest key
        s = [self].pack("H*").unpack("C*").pack("c*")

        cipher.update(s) + cipher.final
      end

thanks, works perfectly!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment