javaencryptionblowfish

Java Decryption creating additional symbols


I'm writing a encryption and decryption code as follows

import java.io.*;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.BadPaddingException;
import java.nio.file.Files;
import java.util.Scanner;

public class EncryptFile
{
    public static void main(String args[]) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
//Encrypt Mode
        FileOutputStream outputStream = new FileOutputStream(new File("D:\\encryptedNewStringFile.txt"));
        Key secretKey = new SecretKeySpec("encKey".getBytes(), "Blowfish");
        Cipher cipher = Cipher.getInstance("Blowfish");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        byte[] getFileBytes = "writing a file using encryption ".getBytes();
        byte[] outputBytes = cipher.doFinal(getFileBytes);
        outputStream.write(outputBytes);
        getFileBytes = "\n".getBytes();
        outputBytes = cipher.doFinal(getFileBytes);
        outputStream.write(outputBytes);
        getFileBytes = "This is New Line 2 \nThis is NewLine 3".getBytes();
        outputBytes = cipher.doFinal(getFileBytes);
        outputStream.write(outputBytes);
        outputStream.close();
//Decrypt Mode
        File curFile = new File("D:\\encryptedNewStringFile.txt");
        secretKey = new SecretKeySpec("encKey".getBytes(), "Blowfish");
        cipher = Cipher.getInstance("Blowfish/ECB/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        getFileBytes = Files.readAllBytes(curFile.toPath());
        outputBytes = cipher.doFinal(getFileBytes);
        InputStream bai = new ByteArrayInputStream(outputBytes);
        BufferedReader bfReader = new BufferedReader(new InputStreamReader(bai));
        Scanner scan = new Scanner(bfReader);
        while(scan.hasNextLine())
        {
            System.out.println(scan.nextLine());
        }
}
}

here i have a problem in output which is the printed output has some extra symbols (i.e question marks and box symbols)in it.
The output i received is
enter image description here

Any suggestions will be really helpful thanks in advance


Solution

  • Combining the answers from @Artjom B. and @The 5th column mouse you get a file encryption program that will encrypt a file with Blowfish in CBC mode. The encryption and decryption is done in chunks so large files (up to some GB) could get encrypted and decrypted without "out of memory errors".

    The key is generated randomly, and you should keep in mind - without knowledge of the key no decryption of the file is possible.

    output:

    file encryption with Blowfish CBC mode
    used key (Base64): jsErS04so1NCC7Jmds6Grr+0tPkNoaj0hx/izLaW5H8=
    result encryption: true
    result decryption: true
    

    Security warning: the code has no exception handling, no correct file handling (e.g. overwriting without notice) and is for educational purpose only:

    import javax.crypto.Cipher;
    import javax.crypto.CipherInputStream;
    import javax.crypto.CipherOutputStream;
    import javax.crypto.NoSuchPaddingException;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.security.InvalidAlgorithmParameterException;
    import java.security.InvalidKeyException;
    import java.security.NoSuchAlgorithmException;
    import java.security.SecureRandom;
    import java.util.Base64;
    
    public class BlowfishCbcFileEncryption {
        public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, IOException,
                InvalidKeyException, InvalidAlgorithmParameterException {
            System.out.println("file encryption with Blowfish CBC mode");
    
            String uncryptedFilename = "uncrypted.txt";
            String encryptedFilename = "encrypted.enc";
            String decryptedFilename = "decrypted.txt";
    
            // random blowfish 256 key
            byte[] key = new byte[32];
            SecureRandom secureRandom = new SecureRandom();
            secureRandom.nextBytes(key);
            System.out.println("used key (Base64): " + base64Encoding(key));
    
            // random iv
            byte[] iv = new byte[8]; // blowfish iv is 8 bytes long
            secureRandom.nextBytes(iv);
    
            boolean result;
            result = encryptCbcFileBufferedCipherOutputStream(uncryptedFilename, encryptedFilename, key, iv);
            System.out.println("result encryption: " + result);
            result = decryptCbcFileBufferedCipherInputStream(encryptedFilename, decryptedFilename, key);
            System.out.println("result decryption: " + result);
    
        }
    
        public static boolean encryptCbcFileBufferedCipherOutputStream(String inputFilename, String outputFilename, byte[] key, byte[] iv)
                throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException {
            Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
            try (FileInputStream in = new FileInputStream(inputFilename);
                 FileOutputStream out = new FileOutputStream(outputFilename);
                 CipherOutputStream encryptedOutputStream = new CipherOutputStream(out, cipher);) {
                out.write(iv);
                SecretKeySpec secretKeySpec = new SecretKeySpec(key, "Blowfish");
                IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
                cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
                byte[] buffer = new byte[8096];
                int nread;
                while ((nread = in.read(buffer)) > 0) {
                    encryptedOutputStream.write(buffer, 0, nread);
                }
                encryptedOutputStream.flush();
            }
            if (new File(outputFilename).exists()) {
                return true;
            } else {
                return false;
            }
        }
    
        public static boolean decryptCbcFileBufferedCipherInputStream(String inputFilename, String outputFilename, byte[] key) throws
                IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException {
            byte[] iv = new byte[8]; // blowfish iv is 8 bytes long
            Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
            try (FileInputStream in = new FileInputStream(inputFilename); // i don't care about the path as all is local
                 CipherInputStream cipherInputStream = new CipherInputStream(in, cipher);
                 FileOutputStream out = new FileOutputStream(outputFilename)) // i don't care about the path as all is local
            {
                byte[] buffer = new byte[8192];
                in.read(iv);
                SecretKeySpec secretKeySpec = new SecretKeySpec(key, "Blowfish");
                IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
                cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
                int nread;
                while ((nread = cipherInputStream.read(buffer)) > 0) {
                    out.write(buffer, 0, nread);
                }
                out.flush();
            }
            if (new File(outputFilename).exists()) {
                return true;
            } else {
                return false;
            }
        }
    
        private static String base64Encoding(byte[] input) {
            return Base64.getEncoder().encodeToString(input);
        }
    }