Monday, May 14, 2018

MySQL AES Encryption in Ruby

I was asked to find a way to encrypt strings in Ruby that would be compatible with MySQL's AES_ENCRYPT and AES_DECRYPT functions. I found several solutions online, but none of them worked the way I expected. After cobbling together several examples, I finally came up with a solution that proved to be compatible with the MySQL version. This was written for use with Ruby 2.3.5.
require 'openssl'
# This module provides AES encryption compatible with MySQL AES_ENCRYPT
# and AES_DECRYPT functions.
module MySQLEncryption
# Takes an unencrypted text string, encrypts it with the password,
# and encodes the results to a hexadecimal string
def self.mysql_encrypt(unencrypted_text, password)
encrypt(unencrypted_text, mysql_key(password))
end
# Takes an encrypted, hex-encoded text string
# and decrypts it with the password
def self.mysql_decrypt(encrypted_hex, password)
decrypt(encrypted_hex, mysql_key(password))
end
# Encode a binary value to hexadecimal
def self.bin_to_hex(binary)
binary.unpack('H*').first
end
# Decode a hex value into a binary
def self.hex_to_bin(hex)
hex.scan(/../).map {|digit| digit.hex}.pack('c*')
end
protected
def self.aes(method, mysql_key, text)
aes = OpenSSL::Cipher::Cipher.new('aes-128-ecb')
aes.send(method)
aes.key = mysql_key
aes.update(text) << aes.final
end
def self.encrypt(unencrypted_text, mysql_key)
bin_to_hex(aes(:encrypt, mysql_key, unencrypted_text))
end
def self.decrypt(encrypted_hex, mysql_key)
aes(:decrypt, mysql_key, hex_to_bin(encrypted_hex))
end
def self.mysql_key(key)
final_key = "\0" * 16
key.length.times do |i|
final_key[i % 16] = (final_key[i % 16].ord ^ key[i].ord).chr
end
final_key
end
end
This module can be used as follows:
require 'my_sql_encryption'
text = 'Sensitive data'
encrypted = MySQLEncryption.mysql_encrypt(text, @password)
decrypted = MySQLEncryption.mysql_decrypt(encrypted, @password)
log.info("Encrypted: #{encrypted}")
log.info("Decrypted: #{decrypted}")
expect(decrypted).to eq(text)

No comments: