I'm using Ruby to try and encrypt a string which will be stored in a database and read/decrypted by a Flash/Actionscript app.
The app uses this blowfish implementation.
I have tried both the openssl and crypt/blowfish methods of creating a compatible string. Neither match each other and neither matches what the Flash app expects.
Where do I start in terms of getting this to work?
irb(main):001:0> require 'openssl'
=> true
irb(main):002:0> require 'crypt/blowfish'
=> true
irb(main):007:0> require 'base64'
=> true
irb(main):003:0> key = "foo"
=> "foo"
irb(main):004:0> plain = "some string"
=> "some string"
irb(main):005:0> blowfish = Crypt::Blowfish.new(key)
=>
irb(main):006:0> enc = blowfish.encrypt_block(plain)
=> "\xF5\xAFB\x12=\xB9\xDB\f"
irb(main):008:0> Base64.encode64(enc)
=> "9a9CEj252ww=\n"
# Now, openssl version
irb(main):009:0> cipher = OpenSSL::Cipher::Cipher.new('bf-cbc').send(:encrypt)
=> #<OpenSSL::Cipher::Cipher:0x00000000f26430>
irb(main):010:0> cipher.key = Digest::SHA256.digest(key)
=> ",&\xB4kh\xFF\xC6\x8F\xF9\x9BE<\x1D0A4\x13B-pd\x83\xBF\xA0\xF9\x8A^\x88bf\xE7\xAE"
irb(main):011:0> enc = cipher.update(plain) << cipher.final
=> "m<\xDB\xC1B\x02p\xB0\xD6\xD0\xA4\xE8XyY\x99"
irb(main):012:0> Base64.encode64(enc)
=> "bTzbwUICcLDW0KToWHlZmQ==\n"
EDIT
Here's what we're doing in AS3 (with the blowfish code mentioned above):
import com.lassieadventurestudio.Blowfish;
import fl.controls.Button;
import fl.controls.TextInput;
import flash.events.MouseEvent;
var $key:String = "foo";
BTN_Submit.label = "Encrypt";
BTN_Submit.addEventListener(MouseEvent.CLICK, onSubmit);
function onSubmit(event:MouseEvent):void
{
trace("INP_Pass.text, ", INP_Pass.text);
var $encryption:String = Blowfish.encrypt(INP_Pass.text, $key);
TXT_Output.text = $encryption;
}
BTN_Decrypt.label = "Decrypt";
BTN_Decrypt.addEventListener(MouseEvent.CLICK, decrypt);
function decrypt(event:MouseEvent):void
{
TXT_Output2.text = Blowfish.decrypt(TXT_Output.text, $key);
}
The problem is that both ways you tried do use CBC mode, but the AS3 example you linked to is using ECB mode.
You can achieve the same using this:
cipher = OpenSSL::Cipher.new('bf-ecb')
cipher.encrypt
cipher.key = key
enc = cipher.update(plain) << cipher.final
Be warned though, this is not a good encryption scheme at all for several reasons (ECB, Blowfish key length, password used directly as key...), as was written in the article, it will only hold off prying eyes, but this will not pose an obstacle to someone who is dedicated about it.
Edit:
I looked at the AS3 implementation, but unfortunately I wasn't able to reproduce the example results given there. But I am confident that the Blowfish implementation from the blog is bad, you shouldn't use it. There they take plain passwords for keys, and to make things worse, they Base64-decode them first to get raw bytes, thereby effectively decreasing the already bad entropy again by a factor of 3/4.
Ruby OpenSSL's Blowfish implementation takes 16 byte keys, so you won't be lucky in reproducing the results with shorter keys. I would recommend to you to do the following:
Use AES instead of Blowfish. While Blowfish has never been "broken", its effective security of 56 bits can be brute-forced.
Don't use passwords as keys. Use cryptographically secure random bytes. An easy way to do this in Ruby for example is:
key = cipher.random_key
You may want to find something similar in AS3.
Don't use ECB mode. Use CBC mode or, if available with your OpenSSL version (requires >=1.0.0), even better, use CTR mode.
You might want to look at the Cipher docs for more advice on achieving something that may be considered secure.
Related
I'm trying to create an MD5 checksum in ruby with salt, but I cannot find any way to do this using the standard digest/md5 package.
I know I can do this:
require 'digest/md5'
checksum = '$1$' + (Digest::MD5.new << plaintext).to_s
However, there doesn't appear to be any way to specify salt for this MD5 checksum generation using digest, and I haven't found any other package I could use for this in ruby.
Is this even possible in ruby?
Creating/Validating *nix Style MD5 Entries with Salt
If you're trying to manage *nix system passwords, you're better off just shelling out to system utilities rather than building your own. However, if you want to generate or validate salted passwords with only Ruby core or standard library capabilities, you certainly can.
A computed MD5 password with salt is generally stored in a flat-file database (e.g. /etc/shadow) like this, where $ is the field separator:
$1$salt$hashed_pw
Note that the first two fields are stored in cleartext, because they're needed to rebuild and hash the correct string when presented with only the password to validate. As a result, you need to treat the salt as a separate variable from the plaintext password, although the salt is included with the password when hashed.
If you don't have a character limitation imposed by your utilites, one way to generate a strong salt is to use SecureRandom#uuid to generate a UUIDv4 value. For example:
require 'securerandom'
salt = SecureRandom.uuid
#=> "c05280ef-151c-4ebc-83c6-f5f0906f89c2"
You can then invoke your MD5 hash on salt + pw or pw + salt depending on your application's password implementation. For example:
require 'digest/md5'
MD5_STR_FMT = '$1$%s$%s'.freeze
salt = 'c05280ef-151c-4ebc-83c6-f5f0906f89c2'
pw = 'plaintext password gathered securely'
pw_digest = Digest::MD5.new << salt + pw
pw_entry = MD5_STR_FMT % [salt, pw_digest]
#=> "$1$c05280ef-151c-4ebc-83c6-f5f0906f89c2$87dcc23c0008e45526e474d0364e4aa5"
You then store pw_entry in your password database file, and then parse out the salt to prepend to the offered password when you recompute the hash during authentication. For example:
require 'digest/md5'
# this is how we'll validate a password from user
# input against an entry from the password database
def valid_pw? pw, salt, hashed_pw
pw_digest = Digest::MD5.new << salt + pw
pw_digest.to_s.eql? hashed_pw.to_s
end
# extract salt and password from a database entry
def parse_pw_entry str
str.split(?$).slice -2, 2
end
# get this from your password database in whatever
# way you like
pw_entry = '$1$c05280ef-151c-4ebc-83c6-f5f0906f89c2$87dcc23c0008e45526e474d0364e4aa5'
# for demonstration purposes only; gather password
# securely from user, then perform your validation
['foo', 'plaintext password gathered securely'].map do |pw|
valid_pw? pw, *parse_pw_entry(pw_entry)
end
#=> [false, true]
You can add compute the digest of multiple chunks like this:
require 'digest/md5'
md5 = Digest::MD5.new
md5 << '$1$'
md5 << plaintext
checksum = md5.to_s
Or by string concatenation of the salt and the text in one method call:
salt = '$1$'
checksum = Digest::MD5.hexdigest("#{salt}#{plaintext}")
I found the following, and it does what I want ...
https://github.com/mogest/unix-crypt
It works like this:
require 'unix_crypt'
checksum = UnixCrypt::MD5.build(plaintext, salt)
This generates the same checksum as is used in /etc/shadow, which is what I want to use it for,
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?
Code to reproduce the problem
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>'
Versions
ruby-2.2.2p95
OpenSSL::VERSION is "1.1.0"
Microsoft SQL Server 2014 (12.0.2000.8)
Why do I want to encrypt empty strings?
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.
Workaround - Don't encrypt empty strings
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.
Workaround - Add some constant to the plaintext
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.
I cannot get Perl and Ruby to agree on CBC AES:
Perl
use Crypt::CBC;
use MIME::Base64::Perl;
my $cipher = Crypt::CBC->new(
-key => 'd2cb415e067c7b13',
-iv => 'e36dc751d0433f05', #random 16chars!!!!!! shold NOT repeat between requests
-cipher => 'OpenSSL::AES', #this is same as Rijndael
-literal_key => 1,
-header => "none",
-keysize => 16
);
$encypted = $cipher->encrypt("a really really long long text has differrent results???");
$base64 = encode_base64($encypted);
print("Ciphertext(b64): $base64");
$de_base64 = decode_base64($base64);
$decrypted = $cipher->decrypt($de_base64);
$c = $cipher->finish;
Ciphertext(b64): qz4eSQaFkQUkDOyJSbZf5W03HoldwtgvTLq0yJFRViKJnytf3PVSCGW2CYDjO+tRqV20oxeB2VPa
7NqN1TDSNQ==
there's a newline after the 2VPa section and another at the end
Ruby
require 'openssl'
require 'digest/sha2'
require 'base64'
message = "a really really long long text has differrent results???"
cipher = OpenSSL::Cipher.new('aes-128-cbc')
# digest the key, iv and hmac_key so we have 16-byte length
# also, it looks more of a funky password
# prepare cipher
cipher.encrypt
cipher.key = aes_key = "d2cb415e067c7b13"
cipher.iv = aes_iv = "e36dc751d0433f05"
encrypted = cipher.update(message)
encrypted << cipher.final()
b64_encoded = Base64.encode64(encrypted).encode('utf-8') #strict_encode64 guarantees no newlines, encode64 is default
puts "AES Key : '#{aes_key}'"
puts "AES IV : '#{aes_iv}'"
puts "Ciphertext(b64): '#{b64_encoded}'"
Ciphertext(b64): 'qz4eSQaFkQUkDOyJSbZf5W03HoldwtgvTLq0yJFRViKJnytf3PVSCGW2CYDj
O+tRqV20oxeB2VPa7NqN1TDSNQ==
'
Note the newlines chars after CYDj and after ==
I've seen the following: Perl & Ruby exchange AES encrypted information, but I'm not using padding=0
Newlines are not significant in Base64. You got exactly the same result from both languages.
While there should be absolutely no reason to do so, you could make the Perl version return the same string as the Ruby version as follows:
$base64 = encode_base64($encypted, '');
$base64 =~ s/\G.{60}\K/\n/sg;
The encode_base64 function takes a second parameter, called "eol" (end of line) which, by default, is '\n'.
The returned encoded string is broken into lines of no more than 76
characters each and it will end with $eol unless it is empty
Try:
$base64 = encode_base64($encypted, '');
instead.
How can I encrypt a string with MD5 in Rails 3.0 ?
pass = MD5.hexdigest(pass) in a model yields uninitialized constant MyModel::MD5
You can use Digest::MD5 from the Ruby standard library for this.
irb(main):001:0> require 'digest/md5'
=> true
irb(main):002:0> Digest::MD5.hexdigest('foobar')
=> "3858f62230ac3c915f300c664312c63f"
And one more thing: MD5 is a hash algorithm. You don't "encrypt" anything with a hash algorithm.
I want to encrypt a file that a ruby program will be loading data from.
In addition, I need the program to prompt for a password on startup that will be used to decrypt the file.
In other words, the file needs to reside encrypted on the machine and only users with passwords will be able to run the app.
I have started to look at openpgp but as far as I understand, this does still not solve the password problem.
There's two easy ways to go about doing this. One is to shell out to openssl to do your encryption / decryption there. The arguably better way would be to use the Ruby Crypto gem.
Program to encrypt:
require 'rubygems'
require 'crypt/blowfish';
puts "Password? "
pw = gets
puts "Secret data? "
data = gets
blowfish = Crypt::Blowfish.new(pw)
r = StringIO.new(data);
File.open('data', 'w') do |f|
while l = r.read(8) do
while l.size < 8 do l += "\0" end
f.print blowfish.encrypt_block(l)
end
end
Program to decrypt:
require 'rubygems'
require 'crypt/blowfish';
puts "Password? "
pw = gets
blowfish = Crypt::Blowfish.new(pw)
r = StringIO.new();
File.open('data', 'r') do |f|
while l = f.read(8) do
r << blowfish.decrypt_block(l)
end
end
puts "Secret data:"
puts r.string
This example uses the Blowfish symmetric block cypher. Other cyphers could be used. Also, you would probably want to concatenate a fixed string to the password, to make the key longer and to help tie the encryption/decryption to your application.
Try the encrypted strings gem. Works like a charm.