-1

I would like to encrypt and decrypt files. While decrypting I wish to skip first few bytes of the file and decrypt only the rest.

Here contains three files

  1. input_file.txt - input text file for encryption
  2. output_file.txt - encrypted input_file.txt file
  3. decrypted_file.txt - decrypted output_file.txt file

Sample code:

import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class FileReadWrite {

    public static byte[] getAESKey() {
        SecureRandom secureRandom = new SecureRandom();
        byte[] bytes = new byte[32];
        secureRandom.nextBytes(bytes);
        return bytes;
    }
/**
 * Method used to generate a random new iv
 * 
 * @return Randomly generated iv
 */
public static byte[] getAESIV() {
    SecureRandom secureRandom = new SecureRandom();
    byte[] bytes = new byte[16];
    secureRandom.nextBytes(bytes);
    return bytes;
}
public static void main(String[] args) throws Exception {
    // TODO Auto-generated method stub
    FileInputStream fin = new FileInputStream("/Users/emp/Research/Streams/input_file.txt");
    FileOutputStream fout = new FileOutputStream("/Users/emp/Research/Streams/output_file.txt");

    SecretKeySpec keySpec = null;
    IvParameterSpec ivSpec = null;
    Cipher ecipher = null;
    Cipher dcipher = null;
    byte[] keyBytes = getAESKey();
    byte[] ivBytes = getAESIV();
    // Creating keyspec and ivspec for generating cipher
    keySpec = new SecretKeySpec(keyBytes,"AES");
    ivSpec = new IvParameterSpec(ivBytes);
    try {
        ecipher = Cipher.getInstance("AES/CTR/NoPadding");
        ecipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
    } catch (Exception e) {
        System.out.println("Thus the exception occured during cipher generation is ::: "+e);
    }

    CipherOutputStream cout = new CipherOutputStream(fout, ecipher);
    try {
        int count = 0;
        int BUFFER_SIZE = 1024;
        byte[] bytearray = new byte[BUFFER_SIZE];
        while((count = fin.read(bytearray, 0, BUFFER_SIZE)) != -1) {
            //fout.write(bytearray, 0, count);
            cout.write(bytearray, 0, count);
        }
    } catch(Exception ex) {
        System.out.println("Thus the exception occured is ::: "+ex);
    } finally {
        fin.close();
        fout.close();
        cout.close();
    }

    fin = new FileInputStream("/Users/emp/Research/Streams/output_file.txt");
    fout = new FileOutputStream("/Users/emp/Research/Streams/decrypted_file.txt");

    try {
        dcipher = Cipher.getInstance("AES/CTR/NoPadding");
        dcipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
    } catch (Exception e) {
        System.out.println("Thus the exception occured during cipher generation is ::: "+e);
    }
    //fin.skip(1024);
    CipherInputStream cin = new CipherInputStream(fin, dcipher);
    try {
        int count = 0;
        int BUFFER_SIZE = 1024;
        byte[] bytearray = new byte[BUFFER_SIZE];

        **//cin.read(bytearray, 0, 30);**

        while((count = cin.read(bytearray, 0, BUFFER_SIZE)) != -1) {
            //fout.write(bytearray, 0, count);
            fout.write(bytearray, 0, count);
        }
    } catch(Exception ex) {
        System.out.println("Thus the exception occured is ::: "+ex);
    } finally {
        fin.close();
        cin.close();
        fout.close();
    }

    System.out.println("File read write completed successfully !!! ");
}
}

Tried:

  1. Cipher input stream skip - cin.skip(no_of_bytes) - this doesn't work - this decrypts the entire file

  2. File input stream skip

     fin.skip(no_of_bytes); CipherInputStream cin = new
     CipherInputStream(fin, cipher);
    

    This does not decrypt the file. The output file looks like encrypted.

  3. Dummy read - Reading and ignoring from cipher input stream - this works

    //cin.read(bytearray, 0, 30);
    

Please clarify me the following:

  1. What skip and seek input stream does internally?
  2. Why my cipher input stream seek doesn't work?
  3. Why cipher input stream requires all the bytes to be read from the stream (I assume so) for decrypting? How does it work?
techraf
  • 64,883
  • 27
  • 193
  • 198
Tom Taylor
  • 3,344
  • 2
  • 38
  • 63
  • 1
    Yes, if you skip a large number of bytes in an encrypted file everything has to be decrypted by the CipherInputStream even if the CTR mode would allow skipping (limitation of the implementation). See also: http://stackoverflow.com/questions/23743842/random-access-inputstream-using-aes-ctr-mode-in-android – Robert Oct 11 '16 at 08:29
  • So is `dummy read` the only solution? – Tom Taylor Oct 11 '16 at 08:36
  • Have you read the answer I linked? It shows how to "skip" in CTR mode. It requires to decrypt dummy data but it doe snot require to read the encrypted file. – Robert Oct 11 '16 at 08:50
  • Yes Robert just now read the full article.. Was It's really very useful.. – Tom Taylor Oct 11 '16 at 09:18

1 Answers1

1

With reference to the article,

Random access InputStream using AES CTR mode in android

After updating the iv with offset value the decryption works fine.

Here is the updated code

package Streams;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.SecureRandom;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class FileReadWrite {
    private static final int AES_BLOCK_SIZE = 16;

    public static final void jumpToOffset(final Cipher c, final SecretKeySpec aesKey, final IvParameterSpec iv, final long offset) {
        if (!c.getAlgorithm().toUpperCase().startsWith("AES/CTR")) {
            throw new IllegalArgumentException("Invalid algorithm, only AES/CTR mode supported");
        }
        if (offset < 0) {
            throw new IllegalArgumentException("Invalid offset");
        }
        final int skip = (int) (offset % AES_BLOCK_SIZE);
        final IvParameterSpec calculatedIVForOffset = calculateIVForOffset(iv, offset - skip);
        try {
            c.init(Cipher.ENCRYPT_MODE, aesKey, calculatedIVForOffset);
            final byte[] skipBuffer = new byte[skip];
            c.update(skipBuffer, 0, skip, skipBuffer);
            Arrays.fill(skipBuffer, (byte) 0);
        } catch (ShortBufferException | InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalStateException(e);
        }
    }

    private static IvParameterSpec calculateIVForOffset(final IvParameterSpec iv, final long blockOffset) {

        final BigInteger ivBI = new BigInteger(1, iv.getIV());
        final BigInteger ivForOffsetBI = ivBI.add(BigInteger.valueOf(blockOffset / AES_BLOCK_SIZE));
        final byte[] ivForOffsetBA = ivForOffsetBI.toByteArray();
        final IvParameterSpec ivForOffset;
        if (ivForOffsetBA.length >= AES_BLOCK_SIZE) {
            ivForOffset = new IvParameterSpec(ivForOffsetBA, ivForOffsetBA.length - AES_BLOCK_SIZE, AES_BLOCK_SIZE);
        } else {
            final byte[] ivForOffsetBASized = new byte[AES_BLOCK_SIZE];
            System.arraycopy(ivForOffsetBA, 0, ivForOffsetBASized, AES_BLOCK_SIZE - ivForOffsetBA.length, ivForOffsetBA.length);
            ivForOffset = new IvParameterSpec(ivForOffsetBASized);
        }
        return ivForOffset;
    }

    public static byte[] getAESKey() {
        SecureRandom secureRandom = new SecureRandom();
        byte[] bytes = new byte[32];
        secureRandom.nextBytes(bytes);
        return bytes;
    }
    /**
     * Method used to generate a random new iv
     * 
     * @return Randomly generated iv
     */
    public static byte[] getAESIV() {
        SecureRandom secureRandom = new SecureRandom();
        byte[] bytes = new byte[16];
        secureRandom.nextBytes(bytes);
        return bytes;
    }
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        FileInputStream fin = new FileInputStream("/Users/emp/Research/Streams/input_file.txt");
        FileOutputStream fout = new FileOutputStream("/Users/emp/Research/Streams/output_file.txt");

        SecretKeySpec keySpec = null;
        IvParameterSpec ivSpec = null;
        Cipher ecipher = null;
        Cipher dcipher = null;
        byte[] keyBytes = getAESKey();
        byte[] ivBytes = getAESIV();
        // Creating keyspec and ivspec for generating cipher
        keySpec = new SecretKeySpec(keyBytes,"AES");
        ivSpec = new IvParameterSpec(ivBytes);
        try {
            ecipher = Cipher.getInstance("AES/CTR/NoPadding");
            ecipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
        } catch (Exception e) {
            System.out.println("Thus the exception occured during cipher generation is ::: "+e);
        }

        CipherOutputStream cout = new CipherOutputStream(fout, ecipher);
        try {
            int count = 0;
            int BUFFER_SIZE = 1024;
            byte[] bytearray = new byte[BUFFER_SIZE];
            while((count = fin.read(bytearray, 0, BUFFER_SIZE)) != -1) {
                //fout.write(bytearray, 0, count);
                cout.write(bytearray, 0, count);
            }
        } catch(Exception ex) {
            System.out.println("Thus the exception occured is ::: "+ex);
        } finally {
            fin.close();
            fout.close();
            cout.close();
        }

        fin = new FileInputStream("/Users/emp/Research/Streams/output_file.txt");
        fout = new FileOutputStream("/Users/emp/Research/Streams/decrypted_file.txt");

        try {
            dcipher = Cipher.getInstance("AES/CTR/NoPadding");
            dcipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        } catch (Exception e) {
            System.out.println("Thus the exception occured during cipher generation is ::: "+e);
        }

        fin.skip(1024);
        jumpToOffset(dcipher, keySpec, ivSpec, 1024);
        CipherInputStream cin = new CipherInputStream(fin, dcipher);
        //cin.skip(1024);
        try {
            int count = 0;
            int BUFFER_SIZE = 1024;
            byte[] bytearray = new byte[BUFFER_SIZE];
            //cin.read(bytearray, 0, 30);
            while((count = cin.read(bytearray, 0, BUFFER_SIZE)) != -1) {
                //fout.write(bytearray, 0, count);
                fout.write(bytearray, 0, count);
            }
        } catch(Exception ex) {
            System.out.println("Thus the exception occured is ::: "+ex);
        } finally {
            fin.close();
            cin.close();
            fout.close();
        }

        System.out.println("File read write completed successfully !!! ");
    }
}
Community
  • 1
  • 1
Tom Taylor
  • 3,344
  • 2
  • 38
  • 63
  • Hi anyone, previously the question doesn't have much information. So, It was downvoted. I have edited my question with enough information and updated it.. But still the downvote is there and the stackoverflow account says "Sorry we no longer accept questions from this account" when I try to ask a new question - Some one please help me with this. – Tom Taylor Oct 13 '16 at 17:20