Florin Asăvoaie
Florin Asăvoaie

Reputation: 918

Blowfish encryption: Java vs. Ruby

I am trying to generate some encrypted JBoss data source passwords from Puppet (Ruby). JBoss uses this library (see line 149) to encrypt / decrypt the passwords (using Blowfish with a predefined key compiled in the Java code).

Now, researching a bit I found that, as default, javax.crypto uses for blowfish the following parameters: Blowfish/ECB/PKCS5Padding.

By default, Ruby's OpenSSL library does not make any padding at all (simply refuses the key if it is not a multiple of 8). I tried to do the padding manually and here is my complete code:

#!/usr/bin/ruby

require 'openssl'

key = 'jaas is the way'

ciph = OpenSSL::Cipher.new('bf-ecb').encrypt
ciph.key = key + (8 - key.length % 8).chr * (8 - key.length % 8)

b = ciph.update(ARGV[0]) + ciph.final()
puts b.unpack('H*')

I tried to implement the PKCS5 padding the same way as I saw it implemented in Python in a similar (Python vs. Java Blowfish) question here on SO by adding chr(number of bytes until the length is multiple of 8) until the length of the string is multiple of 8. For this specific key, it is supposed to add exactly one chr(1) and when trying to debug, it seems to do it just fine.

The thing is that my Ruby code is giving different results to the ones obtained by the code in the Picketbox library in Java.

EDIT: Here are some conclusions I got after running more tests:

Upvotes: 0

Views: 1345

Answers (2)

Florin Asăvoaie
Florin Asăvoaie

Reputation: 918

Worakarn Isaratham's answer is 100% correct, however, I'd like to provide an implementation for this problem using the standard OpenSSL library:

#!/usr/bin/ruby

require 'openssl'

def JBossPasswordEncrypt(s)
    ciph = OpenSSL::Cipher.new('bf-ecb').encrypt

    # https://source.jboss.org/browse/PicketBox/trunk/security-jboss-sx/jbosssx/src/main/java/org/picketbox/datasource/security/SecureIdentityLoginModule.java?r=332 Line 153
    ciph.key_len = 15
    ciph.key = 'jaas is the way'

    # PKCS5 Padding
    s = s + (8 - s.length % 8).chr * (8 - s.length % 8)

    # Split the secret in chunks of 8 and encrypt it
    e = s.scan(/.{8}/).map { |block| ciph.update(block).unpack('H*') }

    # Convert the bytes array into a signed number
    en = e.join.to_i(16)
    en = en - (2 ** (en.size * 8)) if en >= (2 ** (en.size * 8 - 1))

    # Return the result in hex (as string)
    return en.to_s(16)
end

puts JBossPasswordEncrypt(ARGV[0])

Thank you very much for the help to everyone!

EDIT: I added conversion to a signed number, just like Java does. Not so nice but it works.

EDIT: I wrote it as a method with easier to understand syntax and comments.

Upvotes: 1

Worakarn Isaratham
Worakarn Isaratham

Reputation: 1034

Look like the Java Blowfish does no padding to the key at all; it just starts over from the first byte once it reaches the end of the key. (Try using keys "abc, then "abcabc" and see what happens.)

I suggest looking into this Crypt19 gem - their Blowfish implementation accepts key that is not a multiple of 8. But they only offer CBC mode, so you need to implement ECB yourself. Should be pretty easy - just:

  • pad the message (like how you pad the key)
  • split the message into 8-byte blocks, and
  • call encrypt_block for each block

EDIT: okay, here's the code. This produces identical output to Java version, at least for this "Hello, world!" input.

require 'crypt/blowfish' 

secret = "Hello, world!"
blowfish = Crypt::Blowfish.new("jaas is the way")
padded_secret = secret + (8 - secret.length % 8).chr * (8 - secret.length % 8)
encrypted = padded_secret.scan(/.{8}/).map{|block| blowfish.encrypt_block(block).unpack('H*')}.join
puts encrypted #print "17a0aef80205dd34d08dbf27a0101c9c"

Upvotes: 1

Related Questions