ruby-on-railsfedora-25

How do I use AES-128-GCM encryption in Rails?


I've been using ActiveSupport::MessageEncryptor for some user data encryption with its default AES-256-CBC cipher for some time. After seeing Rails Issue #28135, I wanted to convert to AES-128-GCM as Rails has done for its secrets feature. I started getting ActiveSupport::MessageEncryptor::InvalidMessage exceptions, so I created a simple test case based on the MessageEncryptor example:

["aes-256-cbc", "aes-128-gcm"].each do |cipher|
  puts "Cipher: #{cipher}"
  salt  = SecureRandom.random_bytes(64)
  key   = ActiveSupport::KeyGenerator.new('password').generate_key(salt, 32)
  crypt = ActiveSupport::MessageEncryptor.new(key, cipher: cipher)
  encrypted_data = crypt.encrypt_and_sign('my secret data')
  puts crypt.decrypt_and_verify(encrypted_data)
end

Output:

Cipher: aes-256-cbc
my secret data
Cipher: aes-128-gcm
ActiveSupport::MessageEncryptor::InvalidMessage: ActiveSupport::MessageEncryptor::InvalidMessage
        from /home/kevin/.gem/ruby/gems/activesupport-5.0.2/lib/active_support/message_encryptor.rb:103:in `rescue in _decrypt'
        from /home/kevin/.gem/ruby/gems/activesupport-5.0.2/lib/active_support/message_encryptor.rb:91:in `_decrypt'
        from /home/kevin/.gem/ruby/gems/activesupport-5.0.2/lib/active_support/message_encryptor.rb:66:in `decrypt_and_verify'
        from /work/myplaceonline/src/src/myplaceonline_rails/app/lib/myp.rb:2287:in `block in play'
        from /work/myplaceonline/src/src/myplaceonline_rails/app/lib/myp.rb:2281:in `each'
        from /work/myplaceonline/src/src/myplaceonline_rails/app/lib/myp.rb:2281:in `play'
        from (irb):3
        from /home/kevin/.gem/ruby/gems/railties-5.0.2/lib/rails/commands/console.rb:65:in `start'
        from /home/kevin/.gem/ruby/gems/railties-5.0.2/lib/rails/commands/console_helper.rb:9:in `start'
        from /home/kevin/.gem/ruby/gems/railties-5.0.2/lib/rails/commands/commands_tasks.rb:78:in `console'
        from /home/kevin/.gem/ruby/gems/railties-5.0.2/lib/rails/commands/commands_tasks.rb:49:in `run_command!'
        from /home/kevin/.gem/ruby/gems/railties-5.0.2/lib/rails/commands.rb:18:in `<top (required)>'
        from bin/rails:4:in `require'
        from bin/rails:4:in `<main>'

I'm using Rails 5.0.2 on Fedora 25 64-bit with openssl-1.0.2k-1 (the Rails pull request referenced above suggests openssl 1.0.1 should support AES-128-GCM). My openssl ciphers list appears to support GCM:

$ openssl ciphers
ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:CAMELLIA256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:CAMELLIA128-SHA:DES-CBC3-SHA:DHE-DSS-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DHE-RSA-CAMELLIA256-SHA:DHE-DSS-CAMELLIA256-SHA:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:DHE-RSA-CAMELLIA128-SHA:DHE-DSS-CAMELLIA128-SHA:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:PSK-AES256-CBC-SHA:PSK-AES128-CBC-SHA:PSK-3DES-EDE-CBC-SHA

ruby 2.3.3p222 (2016-11-21 revision 56859) [x86_64-linux]


Solution

  • The second parameter for generate_key in your example (the key_size) must be 32 Bytes for aes-256-cbc or 16 Bytes for aes-128-gcm.