2

I have a legacy implementation of blowfish in java that I am trying to port to Python.

Java:

import blowfishj.*;
import org.apache.commons.codec.binary.Hex;

private static byte[] EncryptBlowFish(byte[] sStr, String sSecret) {        
    byte[] key = sSecret.getBytes();
    byte[] cipher = new byte[sStr.length];
    BlowfishECB blowfish = new BlowfishECB(key, 0, key.length);
    blowfish.encrypt(sStr, 0, cipher, 0, sStr.length);

    return (new String(Hex.encodeHex(cipher)));

}

Python:

from Crypto.Cipher import Blowfish
import binascii

def encrypt(encr_str, key_str):
    cipher = Blowfish.new(key_str, Blowfish.MODE_ECB)
    return binascii.hexlify(cipher.encrypt(encr_str)).decode('utf-8')

If the string to be encrypted is "12345678" and the key is "1234567890123456", the java code outputs "e00723bbb58234aa" and the python code outputs "61d2570dc6e09632".

Since the java code is legacy, I can't touch it. This indicates there's a problem in pycrypto's implementation of blowfish. However, I can confirm the accepted answer here works. Not sure why though. I tried both pycrypto as well as this blowfish module with the same result.

Any ideas how to replicate the same blowfish output in Python as the legacy java code?

Community
  • 1
  • 1
Jim Walker
  • 494
  • 4
  • 9
  • seems like your java code uses ECB mode of blowfish did you check that pyhon is also running in ECB and not in CBC or another mode? – Kai Iskratsch Jan 13 '16 at 07:59
  • I'm using `cipher = Blowfish.new(key_str, Blowfish.MODE_ECB)`. Doesn't that set python blowfish to ECB mode? – Jim Walker Jan 13 '16 at 08:01
  • yes your right, after a bit chekcing myself i think python actually produces right result for blowfish. and java seems to be blowfish-compact – Kai Iskratsch Jan 13 '16 at 08:46
  • what is blowfish-compact? Could you please elaborate a bit? Is there a way to get that on python? – Jim Walker Jan 13 '16 at 09:02
  • never heared it before but i have seen it now in a tool to select as option when testing. seems like it was created by a bug when someone did not use big endian byte order in blowfish encryption as specifieed by algorithm. there is a question here on this already: http://stackoverflow.com/questions/11422497/whats-the-difference-between-blowfish-and-blowfish-compat – Kai Iskratsch Jan 13 '16 at 09:04
  • thanks for that reference, I'll take a look, btw it's blowfish-compat (as in compatible vs compact). – Jim Walker Jan 13 '16 at 09:08
  • sorry was a typo before – Kai Iskratsch Jan 13 '16 at 09:11
  • Have you run the [available test vectors](https://www.schneier.com/code/vectors.txt) through both implementations and seen which one is broken? If you have the code for the java implementation, you could dig in and see if there are differences according to the standard. Maybe the number of rounds is different. – Artjom B. Jan 13 '16 at 10:11
  • Unfortunately, that didn't work. I tried reversing the 4 byte blocks as per blowfist-compat before and after the encryption, the results still don't match the java output. – Jim Walker Jan 13 '16 at 10:18
  • @Artjom B. That may be beyond my abilities, but I did test the java implementation with the sample string/key from the bug report in my original post, and the output matches that of the java example provided there. And so does the output of the python implementation, i.e. to say, the bug report's results are identical on my setup. – Jim Walker Jan 13 '16 at 10:22
  • @Kai Iskratsch: you were right. It had to do with blowfish-compat. It didn't work for me initially due to another issue in the code, which had to do with the way I was padding the string to be encrypted. Once I corrected that, reversing the bytes as per blowfish-compat generates the exact same output as the java impl. Your stackflow reference led [here](https://gist.github.com/adamb70/1f140573b37939e78eb5") where I found sample code for both the byte order reversal as well as padding the incoming string correctly. Could you please add your comment as an answer so I can accept it? thanks. – Jim Walker Jan 13 '16 at 11:40

1 Answers1

0

All credit to @Kai Iskratsch for pointing in the right direction.

Reference:

  1. What's the difference between Blowfish and Blowfish-compat?

  2. https://gist.github.com/adamb70/1f140573b37939e78eb5%22

Here's the code that worked for me.

Python:

from Crypto.Cipher import Blowfish
from binascii import hexlify

def encrypt(key, string):
    """
    Encrypts input string using BlowFish-Compat ECB algorithm.
    :param key: secret key
    :param string: string to encrypt
    :return: encrypted string
    """
    cipher = Blowfish.new(key, Blowfish.MODE_ECB)
    return hexlify(_reverse_bytes(cipher.encrypt(_reverse_bytes(string)))).decode('utf-8')

@staticmethod
def _reverse_bytes(data):
    """
    Takes data and reverses byte order to fit blowfish-compat format. For example, using _reverse_bytes('12345678')
    will return 43218765.
    :param data as bytes
    :return: reversed bytes
    """
    data_size = 0
    for n in data:
        data_size += 1

    reversed_bytes = bytearray()
    i = 0
    for x in range(0, data_size // 4):
        a = (data[i:i + 4])
        i += 4
        z = 0

        n0 = a[z]
        n1 = a[z + 1]
        n2 = a[z + 2]
        n3 = a[z + 3]
        reversed_bytes.append(n3)
        reversed_bytes.append(n2)
        reversed_bytes.append(n1)
        reversed_bytes.append(n0)

    return bytes(reversed_bytes)
Community
  • 1
  • 1
Jim Walker
  • 494
  • 4
  • 9