Question

I am creating a program that lets users upload and download encrypted files and decrypt them if they have the correct permissions to do so. Encrypting and uploading is fine, and so is downloading, but when I try to decrypt I get the "pad block corrupted" error.

What I am trying to do is take the encrypted file and then make a copy that is unencrypted

I shortened what I could so please don't comment that it looks incomplete.

KeyGen:

public static SecretKey genGroupSecretKey() {
    try {
        KeyGenerator keyGen = KeyGenerator.getInstance("AES", "BC");
        keyGen.init(128);
        return keyGen.generateKey();
    } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
        System.err.println("Error: " + e.getMessage());
        e.printStackTrace(System.err);
    }
    return null;
}

Encrypt:

try (FileInputStream fis = new FileInputStream(sourceFile)) {
    response = Utils.decryptEnv((byte[]) tempResponse.getObjContents().get(0), fsSecretKey, ivSpec);

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
    cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);

    do {
        byte[] buf = new byte[4096];

        int n = fis.read(buf); // can throw an IOException
        else if (n < 0) {
            System.out.println("Read error");
            fis.close();
            return false;
        }

        byte[] cipherBuf = cipher.doFinal(buf);

        // send through socket blah blah blah

    } while (fis.available() > 0);

Decrypt:

   ...     

   Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
   cipher.init(Cipher.DECRYPT_MODE, secKey, ivSpec);

    File file = new File("D_" + filename); // D_ for decrypted
    FileOutputStream fos = null;
    if (!file.exists()) {
        file.createNewFile();
        fos = new FileOutputStream(file);
    }

    try (FileInputStream fis = new FileInputStream(filename)) {
        do {
            byte[] buf = new byte[4096];
            int n = fis.read(buf);
            if (n < 0) {
                System.out.println("Read error");
                fis.close();
                return false;
            }

            byte[] cipherBuf = cipher.doFinal(buf);  // ERROR HERE
            System.out.println("IS THIS WORKING WTF: " + new String(cipherBuf));
            fos.write(cipherBuf, 0, n);

        } while (fis.available() > 0);
        fis.close();
        fos.close();
        return true;
Was it helpful?

Solution

You should you Cipher.doFinal() only for the very last block of data. You should modify both your encryption and decryption code to use Cipher.update() for all intermediate data and use a single call to Cipher.doFinal() at the end. It is permitted to use Cipher.update() for all intermediate data block and just call the Cipher.doFinal() with the empty array at the end.

Also, you are ignoring the return value of fis.read(buf) when you pass the data read from the stream to the cipher.

Also, you are using InputStream.available() as a condition to end the cycle. Even if the code inside the cycle would be correct, you would be getting unpredictable results due to spurious triggering of cycle condition.

Use the following pattern to work with the ciphers:

Cipher cipher = ...
InputStream in = ...
OutputStream out = ...

byte[] inputBuffer = new byte[ BUFFER_SIZE ];
int r = in.read(inputBuffer);
while ( r >= 0 ) {
    byte[] outputUpdate = cipher.update( inputBuffer, 0, r );
    out.write( outputUpdate );
    r = in.read(inputBuffer);
}
byte[] outputFinalUpdate = cipher.doFinal();
out.write( outputFinalUpdate );

Check other variants of Cipher.update() - it is possible to use two pre-allocated buffers and minimize additional allocations.

Also check if you can reuse javax.crypto.CipherInputStream and javax.crypto.CipherOutputStream classes so that you do not have to work with the cipher directly.

OTHER TIPS

If the objective is just to encrypt and decrypt a file, the following program would help.

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.io.output.ByteArrayOutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.fiberlink.security.crypto.CryptoUtils;

public class DecryptDocsController {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }
    public static final String PKCS7_PADDING = "AES/CBC/PKCS7Padding";
    public static final int CHUNK_SIZE = 16;
    public static final int STARTING_LOCATION = 0;
    public static final int STREAM_FINISH_LOCATION = -1;
    public void encryptFile() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IOException, IllegalBlockSizeException, BadPaddingException 
        {
        FileInputStream fis = new FileInputStream(DECRYPTED_FILE_LOCATION_1);
        FileOutputStream fos = new FileOutputStream(ENC_FILE_LOCATION_1);
        Cipher cipher = Cipher.getInstance(PKCS7_PADDING);
        SecretKeySpec skeySpec = new SecretKeySpec(getEcryptionByte(FILE_ENCRYPION_KEY), "AES");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
        CipherInputStream inputStream = new CipherInputStream(fis, cipher);
        int count =0;
        byte[] data = new byte[CHUNK_SIZE];
        while((count=(inputStream.read(data, STARTING_LOCATION, CHUNK_SIZE))) != STREAM_FINISH_LOCATION) {
            fos.write(data, STARTING_LOCATION, count);
        }
        fis.close();
        fos.close();
        inputStream.close();
       }

    public void decryptFile() throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IOException, IllegalBlockSizeException, BadPaddingException 
        {
        FileInputStream fis = new FileInputStream(ENC_FILE_LOCATION_2);
        FileOutputStream fos = new FileOutputStream(DECRYPTED_FILE_LOCATION_2);
        Cipher cipher = Cipher.getInstance(PKCS7_PADDING);
        SecretKeySpec skeySpec = new SecretKeySpec(getEcryptionByte(FILE_ENCRYPION_KEY), "AES");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
        CipherInputStream inputStream = new CipherInputStream(fis, cipher);
        int count =0;
        byte[] data = new byte[CHUNK_SIZE];
        while((count=(inputStream.read(data, STARTING_LOCATION, CHUNK_SIZE))) != STREAM_FINISH_LOCATION) {
            fos.write(data, STARTING_LOCATION, count);`enter code here`
        }
        fis.close();
        fos.close();
        inputStream.close();
        }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top