Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the Blowfish output in Java and PHP differ by only 2 chars?

I have a blowfish encryption script in PHP and JAVA vice versa that was working fine until today when I came across a problem.

The same content is encrypted differently in Java vs PHP by only 2 chars, which is really weird.

PHP

wTHzxfxLHdMm/JMFnoh0hciS/JADvFFg

Java

wTHzxfxLHdMm/JMFnoh0hciS/D8DvFFg
-------------------------^^

As you see those two positions do not match. Unfortunately the value is a real email address and I can't share it. Also I was not able to reproduce the problem with other few values I've tested. I've tried changing Base64 encode classes on Java, and that neither helped.

The source code for PHP is here, and for Java is here.

What could I do to resolve this problem?

like image 924
Pentium10 Avatar asked Jul 20 '11 09:07

Pentium10


1 Answers

Let's have a look at your Java code:

String c = new String(Test.encrypt((new String("thevalue")).getBytes(),
                                   (new String("mykey")).getBytes()));
...
System.out.println("Base64 encoded String:" +
                   new sun.misc.BASE64Encoder().encode(c.getBytes()));

What you are doing here is:

  1. Convert the plaintext string to bytes, using the system's default encoding
  2. convert the key to bytes, using the system's default encoding
  3. encrypt the bytes
  4. convert the encrypted bytes back to a string, using the system's default encoding
  5. convert the encrypted string back to bytes, using the system's default encoding
  6. encode these encrypted bytes using Base64.

The problem is in step 4. It assumes that an arbitrary byte array represents a string in your system's default encoding, and encoding this string back gives the same byte[]. This is valid for some encodings (the ISO-8859 series, for example), but not for others. In Java, when some byte (or byte sequence) is not representable in the given encoding, it will be replaced by some other character, which later for reconverting will be mapped to byte 63 (ASCII ?). Actually, the documentation even says:

The behavior of this constructor when the given bytes are not valid in the default charset is unspecified.

In your case, there is no reason to do this at all - simply use the bytes which your encrypt method outputs directly to convert them to Base64.

byte[] encrypted = Test.encrypt("thevalue".getBytes(),
                                "mykey".getBytes());
System.out.println("Base64 encoded String:"+ new sun.misc.BASE64Encoder().encode(encrypted));

(Also note that I removed the superfluous new String("...") constructor calls here, though this does not relate to your problem.)

The point to remember: Never ever convert an arbitrary byte[], which did not come from encoding a string, to a string. Output of an encryption algorithm (and most other cryptographic algorithms, except decryption) certainly belongs to the category of data which should not be converted to a string.

And never ever use the System's default encoding, if you want portable programs.

like image 177
Paŭlo Ebermann Avatar answered Nov 02 '22 04:11

Paŭlo Ebermann