Loading…

Kryptel/Java

File Encryption and Compression

File Encryption and Compression Example

File Encryption example shows how easy can you create a file encryption program – a rather simplified one but quite practical. We can make that example even more practical by adding a compression step.

As in the original example, the function encrypts or decrypts the specified file with the supplied password. If the file name has the .enc extension, then the file gets decrypted, and the result is stored to a file with the same name with removed .enc extension. Otherwise the file gets encrypted and stored to a file with added .enc extension.

import static com.kryptel.Guids.CID_CIPHER_AES;
import static com.kryptel.Guids.CID_COMPRESSOR_ZIP;
import static com.kryptel.Guids.CID_HASH_SHA256;
import static com.kryptel.Guids.IID_ICipher;
import static com.kryptel.Guids.IID_ICipherParams;
import static com.kryptel.Guids.IID_ICompressor;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

import com.kryptel.ApiHelpers;
import com.kryptel.IDataSink;
import com.kryptel.IKryptelComponent;
import com.kryptel.KeyRecord;
import com.kryptel.Loader;
import com.kryptel.cipher.ICipher;
import com.kryptel.cipher.ICipherParams;
import com.kryptel.compressor.ICompressor;

Encrypted data can't be compressed, so on the encryption phase we need first compress the data, and then encrypt them. It is done by chaining the components – we send the data to the compressor, the compressor's datasink sends the compressor's output to the cipher, and as the last step the cipher's datasink writes the data to the disk. So the datasink classes for encryption will look as this:

class CompressSink implements IDataSink {

  private ICipher cipher;

  public void Init(Object arg) throws Exception {
    cipher = (ICipher)arg;
  }

  public void PutData(byte[] buf, int start, int size) throws Exception {
    cipher.Encrypt(buf, start, size);
  }

  public void Done() throws Exception {
    cipher.Done();
  }
}


class EncryptSink implements IDataSink {

  private FileOutputStream stream;

  public void Init(Object arg) throws Exception {
    stream = (FileOutputStream)arg;
  }

  public void PutData(byte[] buf, int start, int size) throws Exception {
    stream.write(buf, start, size);
  }

  public void Done() throws Exception {
    stream.close();
  }
}

Decryption datasinks are very similar, but their order is opposite – the input data are sent to the cipher, its datasink calls the compressor, and the compressor's datasink writes the decrypted and decompressed data to the disk:

class DecryptSink implements IDataSink {

  private ICompressor compressor;

  public void Init(Object arg) throws Exception {
    compressor = (ICompressor)arg;
  }

  public void PutData(byte[] buf, int start, int size) throws Exception {
    compressor.Decompress(buf, start, size);
  }

  public void Done() throws Exception {
    compressor.Done();
  }
}


class DecompressSink implements IDataSink {

  private FileOutputStream stream;

  public void Init(Object arg) throws Exception {
    stream = (FileOutputStream)arg;
  }

  public void PutData(byte[] buf, int start, int size) throws Exception {
    stream.write(buf, start, size);
  }

  public void Done() throws Exception {
    stream.close();
  }
}

The main encryption function FileEncryption is a bit more complicated than the original example, but still remains very simple:

public static void FileEncryption(String fileName, String password) throws Exception {
  IKryptelComponent compCipher = Loader.CreateComponent(CID_CIPHER_AES);
  ICipherParams cipherParams = (ICipherParams)compCipher.GetInterface(IID_ICipherParams);
  ICipher cipher = (ICipher)compCipher.GetInterface(IID_ICipher);

  // Convert the password and set encryption key
  KeyRecord kr = ApiHelpers.PasswordToKeyRecord(password, CID_HASH_SHA256);
  cipherParams.SetKey(kr.keyData, 0, kr.keyData.length);
 
  IKryptelComponent compCompressor =
               Loader.CreateComponent(CID_COMPRESSOR_ZIP);
  ICompressor compressor = (ICompressor)compCompressor.GetInterface(IID_ICompressor);

  boolean bEncrypt = !fileName.endsWith(".enc");
  String outputFileName = bEncrypt
                          ? fileName + ".enc"
                          : fileName.substring(0, fileName.length() - 4);

  try (FileInputStream fin = new FileInputStream(fileName);
      FileOutputStream fout = new FileOutputStream(outputFileName)) {

    File f = new File(fileName);
    assert (f.isFile());
    long fsize = f.length();

    byte[] ioBuffer = new byte [64 * 1024];
    int len;

    if (bEncrypt) {  // Encrypt file
      cipher.Init(new EncryptSink(), fout);
      compressor.Init(new CompressSink(), cipher);

      while (fsize > 0) {
        len = (int)Math.min(fsize, ioBuffer.length);
        fin.read(ioBuffer, 0, len);
        compressor.Compress(ioBuffer, 0, len);
        fsize -= len;
      }
      
      compressor.Done();
    }
    
    else {  // Decrypt file
      compressor.Init(new DecompressSink(), fout);
      cipher.Init(new DecryptSink(), compressor);

      while (fsize > 0) {
        len = (int)Math.min(fsize, ioBuffer.length);
        fin.read(ioBuffer, 0, len);
        cipher.Decrypt(ioBuffer, 0, len);
        fsize -= len;
      }
    
      cipher.Done();
    }
  }
}

Interfaces used: ICipher, ICipherParams, and ICompressor.

More about components: Components, Loader class, IKryptelComponent interface.

See Also: IDataSink interface, ApiHelpers class, KeyRecord structure.