15

I need to write a simple tool which encrypts/decrypts files.

I guess the best way is to use OpenSSL:

Generate a key:

openssl rand -base64 2048 > secret_key

Encrypt a file:

openssl aes-256-cbc -a -e -in file -out file.enc -k secret_key

Decrypt a file:

openssl aes-256-cbc -d -in file.enc -out file -k secret_key

Is there an easy way to implement this in Ruby? Is there a better way to do that? Using PGP maybe?

emboss
  • 38,880
  • 7
  • 101
  • 108
Istvan
  • 7,500
  • 9
  • 59
  • 109
  • 1
    So what exactly the question: using OpenSSL without the need to call an external program? –  Jun 15 '12 at 03:50
  • 1
    the command to decrypt should be : `openssl aes-256-cbc -d -a -in file.enc -out file -k secret_key` or else you will get a `bad magic number` – aelor Nov 21 '14 at 11:36
  • Seems like using CBC for the 'Block cipher mode' is a bad idea. Best I can tell people seem to be suggesting using GCM. I found this video very useful https://www.youtube.com/watch?v=Rk0NIQfEXBA and also this other SO post https://stackoverflow.com/questions/1220751/how-to-choose-an-aes-encryption-mode-cbc-ecb-ctr-ocb-cfb – Evolve May 11 '22 at 02:40
  • This post was also very useful and the question and answer unpacks many relevant questions on this : https://crypto.stackexchange.com/questions/17999/aes256-gcm-can-someone-explain-how-to-use-it-securely-ruby/18092#18092?newreg=1e147802e3674366adb4ecda098d3c62 – Evolve May 11 '22 at 02:52

2 Answers2

26

Ruby's OpenSSL is a thin wrapper around OpenSSL itself and provides almost all the functionality that OpenSSL itself does, so yes, there's a one-to-one mapping for all your examples:

openssl rand -base64 2048 > secret_key

That's actually exaggerated, you are using AES-256, so you only need a 256 bit key, you are not using RSA here. Ruby OpenSSL takes this decision off your shoulders, it will automatically determine the correct key size given the algorithm you want to use.

You are also making the mistake of using a deterministic IV during your encryption. Why? Because you don't specify an IV at all, OpenSSL itself will default to an IV of all zero bytes. That is not a good thing, so I'll show you the correct way to do it, for more information have a look at the Cipher documentation.

require 'openssl'

# encryption
cipher = OpenSSL::Cipher.new('aes-256-cbc')
cipher.encrypt
key = cipher.random_key
iv = cipher.random_iv

buf = ""
File.open("file.enc", "wb") do |outf|
  File.open("file", "rb") do |inf|
    while inf.read(4096, buf)
      outf << cipher.update(buf)
    end
    outf << cipher.final
  end
end

# decryption
cipher = OpenSSL::Cipher.new('aes-256-cbc')
cipher.decrypt
cipher.key = key
cipher.iv = iv # key and iv are the ones from above

buf = ""
File.open("file.dec", "wb") do |outf|
  File.open("file.enc", "rb") do |inf|
    while inf.read(4096, buf)
      outf << cipher.update(buf)
    end
    outf << cipher.final
  end
end

As you can see, encryption and decryption are fairly similar, so you can probably combine the streaming reading/writing into one shared method and just pass it a properly configured Cipher plus the corresponding file names, I just stated them explicitly for the sake of clarity.

If you'd like to Base64-encode the key (and probably the IV, too), you can use the Base64 module:

base64_key = Base64.encode64(key)
emboss
  • 38,880
  • 7
  • 101
  • 108
4

Ruby has an OpenSSL library that should take care of the heavy lifting.

sczizzo
  • 3,196
  • 20
  • 28
  • Thanks. I might need to move over to PGP because it supports PKI for files (I have smallish files) so I can easily support multiple users without having trouble storing the secret key. – Istvan Jun 15 '12 at 07:57
  • @Istvan There is PKI support in OpenSSL as well. – emboss Jun 15 '12 at 10:07