javacryptographybouncycastlejce

Passing from JCE to Bouncy Castle (Blowfish)


I need to convert a JCE based code to a Bouncy Castle based code. I'm completely new to Bouncy Castle and couldn't find a easy-to-understand introduction to this topic in general or my issue specifically. This is the JCE based class:

import java.security.NoSuchAlgorithmException;
import java.security.Security;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

import com.sun.crypto.provider.SunJCE;

public class JCEBlowfishEncrypterDecrypter {
    
    private static final String ALGORITHM = "Blowfish";
    public static SecretKeySpec key;

    public static String crypt(String msg, String k) throws Exception {
        SecretKeySpec key = init(k);
        return Hex.byte2hex(intCrypt(msg, key));
    }

    public static String decrypt(String msg, String k) throws Exception {
        SecretKeySpec key = init(k);
        return new String(intDecrypt(Hex.hex2byte(msg), key));
    }

    private static byte[] intCrypt(String msg, SecretKeySpec key) throws Exception {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(1, key);
        return cipher.doFinal(msg.getBytes());
    }

    private static byte[] intDecrypt(byte encrypted[], SecretKeySpec key) throws Exception {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(2, key);
        return cipher.doFinal(encrypted);
    }

    private static SecretKeySpec init(String myKey) throws NoSuchAlgorithmException {
        SunJCE sunJce = new SunJCE();
        Security.addProvider(sunJce);
        byte raw[] = myKey.getBytes();
        return new SecretKeySpec(raw, ALGORITHM);
    }

}

... And this is the Bouncy Castle based class:

import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.engines.BlowfishEngine;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;

public class BouncyCastleBlowfishEncrypterDecrypter {

    public static String crypt(String msg, String key) throws Exception {
        PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new BlowfishEngine(), new PKCS7Padding());
        cipher.init(true, new KeyParameter(key.getBytes()));
        byte[] inputAsBytes = msg.getBytes();
        byte[] encryptedAsBytes = new byte[cipher.getOutputSize(inputAsBytes.length)];
        int numberOfBytesCopiedOnEncryptedAsBytes = cipher.processBytes(inputAsBytes, 0, inputAsBytes.length,
                encryptedAsBytes, 0);
        cipher.doFinal(encryptedAsBytes, numberOfBytesCopiedOnEncryptedAsBytes);
        return Hex.byte2hex(encryptedAsBytes);
    }

    public static String decrypt(String msg, String key) throws Exception {
        PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new BlowfishEngine(), new PKCS7Padding());
        cipher.init(false, new KeyParameter(key.getBytes()));
        byte[] outputConvertedAsBytes = Hex.hex2byte(msg);
        byte[] decryptedAsBytes = new byte[cipher.getOutputSize(outputConvertedAsBytes.length)];
        int numberOfBytesCopiedOnDecryptedAsBytes = cipher.processBytes(outputConvertedAsBytes, 0,
                outputConvertedAsBytes.length, decryptedAsBytes, 0);
        cipher.doFinal(decryptedAsBytes, numberOfBytesCopiedOnDecryptedAsBytes);
        return new String(decryptedAsBytes);
    }
}

... And this is the main method:


    public static void main(String[] args) throws Exception {
        String encrypted = JCEBlowfishEncrypterDecrypter.crypt("Test", "mmTSQOFzSL9xAwXGLMEe1Q==");
        String decrypted = JCEBlowfishEncrypterDecrypter.decrypt(encrypted, "mmTSQOFzSL9xAwXGLMEe1Q==");

        System.out.println("--------------");
        System.out.println("encrypted   -> " + encrypted);
        System.out.println("decrypted -> " + decrypted);
        
        encrypted = BouncyCastleBlowfishEncrypterDecrypter.crypt("Test",
                "mmTSQOFzSL9xAwXGLMEe1Q==");
        decrypted = BouncyCastleBlowfishEncrypterDecrypter.decrypt(encrypted,
                "mmTSQOFzSL9xAwXGLMEe1Q==");

        System.out.println("--------------");
        System.out.println("encrypted   -> " + encrypted);
        System.out.println("decrypted -> " + decrypted);
    }

... But I get some strange additional characters when decrypting: What's wrong?

enter image description here


Solution

  • In the meantime, you have changed your original post, so I refer you to the original post and list all necessary changes:

    Sample implementation:

    import java.nio.charset.StandardCharsets;
    import org.bouncycastle.crypto.engines.BlowfishEngine;
    import org.bouncycastle.crypto.paddings.PKCS7Padding;
    import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
    import org.bouncycastle.crypto.params.KeyParameter;
    import org.bouncycastle.util.encoders.Hex;
    
    ...
    
    public static String crypt(String msg, String key) throws Exception {
        PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new BlowfishEngine(), new PKCS7Padding()); // Fix 1: Apply PKCS#7 padding
        cipher.init(true, new KeyParameter(key.getBytes(StandardCharsets.UTF_8)));
        byte[] plaintext = msg.getBytes(StandardCharsets.UTF_8);
        byte[] ciphertext = new byte[cipher.getOutputSize(plaintext.length)];
        int length = cipher.processBytes(plaintext, 0, plaintext.length, ciphertext, 0);
        cipher.doFinal(ciphertext, length);
        return Hex.toHexString(ciphertext);
    }
    
    public static String decrypt(String ciphertextHex, String key) throws Exception {
        PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new BlowfishEngine(), new PKCS7Padding()); // Fix 1: Apply PKCS#7 padding
        cipher.init(false, new KeyParameter(key.getBytes(StandardCharsets.UTF_8)));
        byte[] ciphertext = Hex.decode(ciphertextHex);
        byte[] decrypted = new byte[cipher.getOutputSize(ciphertext.length)];
        int length = cipher.processBytes(ciphertext, 0, ciphertext.length, decrypted, 0);
        length += cipher.doFinal(decrypted, length);                                                                // Fix 2: determine the actual size of the decrypted data
        return new String(decrypted, 0, length, StandardCharsets.UTF_8);                                            // only take the actual length into account when decoding
    } 
    

    Further issues: