1

I'm trying to build a tool to decrypt content in bash encrypted in a scala application:

But first, I've to succeed encoding the same message in both languages and make them equal:

Given the passphrase "0123456789abcdef"
(hex: "30313233343536373839616263646566" and byte[]:[48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102])

scala> import javax.crypto.Cipher
scala> import javax.crypto.spec.SecretKeySpec
scala> val cipher = Cipher.getInstance("Blowfish")
scala> cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec("0123456789abcdef".getBytes("utf-8"), "Blowfish"))
scala> javax.xml.bind.DatatypeConverter.printBase64Binary(cipher.doFinal("message".getBytes("utf-8")))
res7: String = K679Jz06jmc=

But I can't reproduce the same with openssl in bash.

$ echo "message" | openssl enc -a -e -blowfish -nosalt -nopad -k "0123456789abcdef"
LJ3iFJ2/mYk=
$ echo "message" | openssl enc -a -e -blowfish -nosalt -nopad -k "30313233343536373839616263646566"
JkkJgYv3fQg=

Any hint? Thanks!

inieto
  • 680
  • 12
  • 17
  • 2
    A pass phrase or password is not a key. You should use a PBKDF (password based key derivation function) to create a key out of a password. Otherwise, if you must, specify the binary key in hexadecimals or indeed base 64. Note that Java 8 JRE has a `Base64` class in `java.util`. – Maarten Bodewes May 20 '16 at 23:30
  • 1
    General advice: **Always use a fully qualified Cipher string.** `Cipher.getInstance("Blowfish");` may result in different ciphers depending on the default security provider. It most likely results in `"Blowfish/ECB/PKCS5Padding"`, but it doesn't have to be. If it changes, you'll lose compatibility between different JVMs. – Artjom B. May 21 '16 at 08:38
  • 1
    **Never use [ECB mode](http://crypto.stackexchange.com/q/14487/13022)**. It's deterministic and therefore not semantically secure. You should at the very least use a randomized mode like [CBC](http://crypto.stackexchange.com/q/22260/13022) or [CTR](http://crypto.stackexchange.com/a/2378/13022). It is better to authenticate your ciphertexts so that attacks like a [padding oracle attack](http://crypto.stackexchange.com/q/18185/13022) are not possible. This can be done with authenticated modes like GCM or EAX, or with an [encrypt-then-MAC](http://crypto.stackexchange.com/q/202/13022) scheme. – Artjom B. May 21 '16 at 08:38

2 Answers2

3

First of all, if you want to have the same ciphertexts you have to ensure that Scala and OpenSSL encryption use the same deterministic encryption algortithms with the same input parameters.

Since OpenSSL uses CBC as the default mode, we do the same in Scala.

import javax.crypto.Cipher
import javax.crypto.spec._
import javax.xml.bind.DatatypeConverter

val cipher = Cipher.getInstance("Blowfish/CBC/NoPadding")

val key = new SecretKeySpec(DatatypeConverter.parseHexBinary("0123456789ABCDEF0123456789ABCDEF"), "Blowfish")

val specIv = new IvParameterSpec(DatatypeConverter.parseHexBinary("0000000000000000"))

cipher.init(Cipher.ENCRYPT_MODE, key, specIv)

val enc = cipher.doFinal("messages".getBytes("UTF-8"))

println(DatatypeConverter.printBase64Binary(enc))

Note that without padding the input length must be a multiple of the Blowfish block length of 64 bits.

With OpenSSL you have to explicitely specify the same key and initialization vector (IV)

printf %s "messages" | openssl enc -e -blowfish -nopad -K "0123456789ABCDEF0123456789ABCDEF" -iv "0000000000000000" -base64

With the above example you will get JSj0k4FwtG8= as ciphertext in both cases.

aventurin
  • 2,056
  • 4
  • 26
  • 30
2

It would be more accurate to tag OpenSSL, which runs the same from any shell or no shell at all.

OpenSSL enc by default does password-based encryption, with a key (plus IV) derivation similar to (but not exactly) PBKDF1. To encrypt with a key, not password, you need to use UPPERCASE -K with hex, and you need to specify at least part of -iv; on the other hand -nosalt is useless.

Also the echo command adds a newline to the data it echoes, and encryption of 'm e s s a g e NL' is entirely different from encryption of 'm e s s a g e'. Some OSes and shells, depending on whether the echo you are using is builtin or external, have ways to omit the newline, but they aren't all the same. OTOH all POSIX systems support printf, so:

$ printf %s message | openssl enc -blowfish -K 30313233343536373839616263646566 -iv 00 -a
K679Jz06jmc=

EDIT: Artjom is correct; Java/Scala (probably) defaults to ECB, which is a Bad Idea; you should specify /CBC or /CTR and padding. Unlike aventurin I see no reason you should force yourself into the straightjacket of /NoPadding; Java /PKCS5Padding is compatible with OpenSSL's default. For CBC or CTR you need to explicitly set the IV (with the third argument to Cipher.init) to get a deterministic result -- and you must either set the IV, or retrieve the default random one, to produce a decryptable result, which people usually want. OpenSSL enc -blowfish defaults to CBC, but it's clearer to explicitly state -bf-cbc or -bf-ctr. Or -bf-ecb if you really want ECB, which as above is Unrecommended.

dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70
  • I do not see a reason for NoPadding either, except that the OP apparently wanted to use it. – aventurin May 21 '16 at 23:10
  • For longer messages, the bash version doesn't work: `...printBase64Binary(cipher.doFinal("messagemessage".getBytes("utf-8")))` --> "sEKY5ZaIyqnY97ExD1KNeA==" vs `printf %s "messagemessage" | openssl enc...` --> "sEKY5ZaIyqk85nzvvMulxg==". Also, binaries are different "b04298e59688caa93ce67cefbccba5c6" vs "B04298E59688CAA9D8F7B1310F528D78" – inieto May 26 '16 at 16:26
  • @inieto as Artjom explained and I already edited, Java/Scala defaults to ECB mode, which is often easily broken (google: Adobe password breach). To match this beyond one block in `openssl enc` you need `-bf-ecb`, which is Blowfish in ECB mode. – dave_thompson_085 May 26 '16 at 23:14