Search code examples
javapython-3.xpycryptoblowfish

java and Python implementation of Blowfish produce different results


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?


Solution

  • 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)