I am using Ruby's Open SSL bindings to do AES-256 encryption. I can encrypt a non-empty string. However, when attempting to encrypt an empty string, Ruby raises an exception complaining that the data must not be empty. How can I encrypt an empty string using Ruby's OpenSSL bindings?
require "openssl"
KEY = OpenSSL::Cipher::Cipher.new("aes-256-cbc").random_key
def encrypt(plaintext)
cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
cipher.encrypt
iv = cipher.random_iv
cipher.iv = iv
cipher.key = KEY
ciphertext = cipher.update(plaintext) # <- ArgumentError here
ciphertext << cipher.final
[iv, ciphertext]
end
def decrypt(iv, ciphertext)
cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
cipher.decrypt
cipher.iv = iv
cipher.key = KEY
plaintext = cipher.update(ciphertext)
plaintext << cipher.final
plaintext
end
p decrypt(*encrypt("foo")) # "foo"
p decrypt(*encrypt(""))
# /tmp/foo.rb:11:in `update': data must not be empty (ArgumentError)
# from /tmp/foo.rb:11:in `encrypt'
# from /tmp/foo.rb:27:in `<main>'
I am writing an ETL program to migrate data from one database to a SqlServer database. Certain columns from the source database must be encrypted before writing them to the destination database. The source columns may contain any data, including empty strings. The destination columns are usually non-nullable. The destination columns will be decrypted by .net code.
Goal #1: No information about the encrypted field, including whether or not it even exists, should be recoverable without properly decrypting it. An encrypted empty string should be indistinguishable from any other encrypted data.
Goal #2: The .net code that will decrypt these values should not need to handle empty strings specially.
If I can get openssl to encrypt empty strings, I will achieve both of these goals.
I could just not encrypt empty strings, passing them through.
def encrypt(plaintext)
return plaintext if plaintext.empty?
...
end
def decrypt(iv, ciphertext)
return ciphertext if ciphertext.empty?
...
end
This has the disadvantages of exposing information, and also of requiring cooperating code to be written on the .net side.
I could add some constant string to the plaintext before encryption, and remove it after decryption:
PLAINTEXT_SUFFIX = " "
def encrypt(plaintext)
plaintext += PLAINTEXT_SUFFIX
...
end
def decrypt(iv, ciphertext)
...
plaintext.chomp(PLAINTEXT_SUFFIX)
end
This hides whether the data exists or not, but still requires cooperating .net code.
As suggested by @ArtjomB, it's as simple as not calling Cipher#update
with the empty string. The value returned by Cipher#final
then properly encrypts an empty string.
require "openssl"
KEY = OpenSSL::Cipher::Cipher.new("aes-256-cbc").random_key
def encrypt(plaintext)
cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
cipher.encrypt
iv = cipher.random_iv
cipher.iv = iv
cipher.key = KEY
ciphertext = ""
ciphertext << cipher.update(plaintext) unless plaintext.empty?
ciphertext << cipher.final
[iv, ciphertext]
end
def decrypt(iv, ciphertext)
cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
cipher.decrypt
cipher.iv = iv
cipher.key = KEY
plaintext = cipher.update(ciphertext)
plaintext << cipher.final
end
p decrypt(*encrypt("foo")) # "foo"
p decrypt(*encrypt("")) # ""
If you can use DBMS provided encryption functions, then, MySQL AES_ENCRYPT
, seems to be able to encrypt blank string.
For example:
UPDATE some_table
SET some_column = AES_ENCRYPT('',UNHEX('F3229A0B371ED2D9441B830D21A390C3'));
It's AES-128 by default, I am guessing that will be a problem as you need AES-256. Also, not sure which DBMS you are using and whether that DBMS has encryption functions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With