com.diona.fileReader.CipherUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.diona.fileReader.CipherUtil.java

Source

/*
 * Copyright (c) 2014 Diona Ltd.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of Diona
 * ("Confidential Information"). You shall not disclose such Confidential Information
 * and shall use it only in accordance with the terms of the license agreement you
 * entered into with Diona.
 */
package com.diona.fileReader;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

// import org.apache.commons.io.FileUtils;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Base64;
import android.util.Log;
import java.util.Date;

// import com.diona.socialworker.app.SocialWorkerSharedPreferences;

import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaWebView;
import org.json.JSONObject;
import org.json.JSONArray;
import org.json.JSONException;

import android.app.Activity;
import android.content.Intent;

/**
 * Cipher utility class.
 */
public final class CipherUtil extends CordovaPlugin {
    // private static BootstrapProperties bootstrapProperties = BootstrapProperties.getInstance();
    public static final String ACTION_ENCRYPT_FILE = "encryptFile";
    public static final String ACTION_DECRYPT_FILE = "decryptFile";
    private static final boolean ENCRYPTION_ENABLED = true;
    private static final String ENCRYPTION_ALGORITHM = "AES";
    private static final String SECRET_KEY_ALGORITHM = "PBKDF2WithHmacSHA1";
    private static final int KEY_SIZE = 256;
    private static final int KEY_ITERATIONS = 1;

    private static final String TAG = "CipherUtil";
    private static final String SECRET_KEY_PASSPHRASE = "!$%^&*()+=";
    private static CipherUtil instance;
    private static final int IV_LENGTH = 16;
    private static final int BUFFER_SIZE = 65536;
    private static final int SALT_LENGTH = 20;

    public byte[] usedIV = new byte[IV_LENGTH];
    public SecretKeySpec usedSecretKey = null;
    public IvParameterSpec ivspec = null;

    public CipherUtil() {
        // Do nothing
    }

    public void initialize(CordovaInterface cordova, CordovaWebView webView) {
        super.initialize(cordova, webView);
        Log.v(TAG, "Init File Reader plugin");
    }

    /**
     * 
     * @return an instance
     */
    public static CipherUtil getInstance() {
        if (instance == null) {
            instance = new CipherUtil();
        }
        return instance;
    }

    /**
     * Encrypts the given string (plaintext).
     * 
     * @param bytes
     *          byte array to be encrypted.
     * @param context
     *          context to fetch preferences.
     * @return encrypted byte array.
     */
    @SuppressLint("TrulyRandom")
    public byte[] encryptBytes(final byte[] bytes, final Context context) {

        // Transaction.checkLongRunningProcessing("encryptBytes");

        if (!ENCRYPTION_ENABLED) {
            return bytes;
        }

        byte[] encryptedTextBytes = null;
        try {
            // Derive the secret key
            final SecretKeySpec secretKey = getSecretKey(context);

            // encrypt the message
            final Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM);
            final IvParameterSpec ivspec = new IvParameterSpec(getIV(context));
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
            encryptedTextBytes = cipher.doFinal(bytes);
        } catch (final Exception e) {
            Log.e(TAG, "" + e);
        }
        return encryptedTextBytes;
    }

    /**
     * Decrypts a given encrypted string.
     * 
     * @param bytes
     *          encrypted string to be decrypted.
     * @param context
     *          context to fetch preferences.
     * @return the original string.
     */
    public byte[] decryptBytes(final byte[] bytes, final Context context) {
        // Transaction.checkLongRunningProcessing("decryptBytes");

        if (!ENCRYPTION_ENABLED) {
            return bytes;
        }

        byte[] decryptedTextBytes = null;
        try {
            final IvParameterSpec ivspec = new IvParameterSpec(getIV(context));
            final Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, getSecretKey(context), ivspec);
            decryptedTextBytes = cipher.doFinal(bytes);

        } catch (final Exception e) {
            Log.e(TAG, "e" + e);
        }
        return decryptedTextBytes;
    }

    /**
     * Encrypts a file.
     * 
     * @param path
     *          path of the original file.
     * @param context
     *          context to fetch preferences.
     * @param encryptPath
     *          path of the encrypted file.
     */
    public long encryptFile(final String path, final String encryptPath, final Context context) {
        long empty = 123456789L;
        Date d1 = new Date();
        // Transaction.checkLongRunningProcessing("encryptFile");
        //System.err.println("Path = "+path);
        //System.err.println("encryptPath = "+encryptPath);
        try {
            //System.err.println("EN - 1");
            // Here you read the cleartext.
            final FileInputStream fis = new FileInputStream(path);
            // This stream write the encrypted text. This stream will be wrapped by another stream.
            final FileOutputStream fos = new FileOutputStream(encryptPath);

            final OutputStream outputStream;
            if (ENCRYPTION_ENABLED) {
                //System.err.println("EN - 2");
                final SecretKeySpec secret = usedSecretKey = getSecretKey(context);
                final byte[] ivBytes = usedIV = getIV(context);
                ivspec = new IvParameterSpec(ivBytes);
                //System.err.println("EN - 3");
                // Create cipher
                final Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM);
                cipher.init(Cipher.ENCRYPT_MODE, secret, ivspec);
                //System.err.println("EN - 4");
                // Wrap the output stream
                outputStream = new CipherOutputStream(fos, cipher);
                //System.err.println("EN - 5");
            } else {
                outputStream = fos;
            }

            // Write bytes
            int b;
            //System.err.println("EN - 6");
            final byte[] d = new byte[BUFFER_SIZE];
            while ((b = fis.read(d)) != -1) {
                outputStream.write(d, 0, b);
            }
            //System.err.println("EN - 7");
            // Flush and close streams.
            outputStream.flush();
            outputStream.close();
            fis.close();

            //System.err.println("EN - 8");
            Date d2 = new Date();
            long diff = d2.getTime() - d1.getTime();
            long diffSeconds = diff / 1000 % 60;
            System.err.println("File encrypted SUCCESSFULLY. Time = " + diffSeconds);
            return diffSeconds;
            //decryptFile(encryptPath, encryptPath+"_decrypted", null);
        } catch (final Exception e) {
            Log.e(TAG, "e" + e);
            return empty;
        }
    }

    /**
     * Decrypts a file.
     * 
     * @param path
     *          path of the encrypted file.
     * @param decryptedPath
     *          the path where file should be decrypted.
     * @param context
     *          context to fetch preferences.
     * @param decryptAsyncTask
     *          the AsyncTask that calls this method and needs to be updated. Ignored if null.
     */
    public long decryptFile(final String path, final String decryptedPath, final Context context) {
        long empty = 123456789L;
        Date d1 = new Date();
        // Transaction.checkLongRunningProcessing("decryptFile");
        //System.err.println("Path = "+path);
        //System.err.println("decryptedPath = "+decryptedPath);
        try {
            final FileInputStream fis = new FileInputStream(path);
            final FileOutputStream fos = new FileOutputStream(decryptedPath);

            final InputStream inputStream;
            if (ENCRYPTION_ENABLED) {
                //System.err.println("DE - 1");
                //        final SecretKeySpec secret = getSecretKey(context);
                final SecretKeySpec secret = usedSecretKey;
                // Decrypt the message
                final Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM);
                //System.err.println("DE - 2");
                // cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(getIV(context)));
                cipher.init(Cipher.DECRYPT_MODE, secret, ivspec);
                //System.err.println("DE - 3");
                inputStream = new CipherInputStream(fis, cipher);
                //System.err.println("DE - 4");
            } else {
                inputStream = fis;
            }

            int b;
            final byte[] d = new byte[BUFFER_SIZE];
            int totalBytesWritten = 0;
            while ((b = inputStream.read(d)) != -1) {
                fos.write(d, 0, b);
                /*if (decryptAsyncTask != null) {
                  totalBytesWritten += BUFFER_SIZE;
                  decryptAsyncTask.updateProgress(totalBytesWritten);
                }*/
            }
            fos.flush();
            fos.close();
            inputStream.close();

            Date d2 = new Date();
            long diff = d2.getTime() - d1.getTime();
            long diffSeconds = diff / 1000 % 60;
            System.err.println("File decrypted SUCCESSFULLY. Time = " + diffSeconds);
            return diffSeconds;
        } catch (final Exception e) {
            Log.e(TAG, "e" + e);
            return empty;
        }
    }

    /**
     * Decrypts a file and return the decrypted file as an array of bytes.
     * 
     * @param filePath
     *          The path to the encrypted file.
     * @param context
     *          The context for decryption.
     * @return A byte array of the decrypted file contents.
     */
    /*public byte[] decryptFile(final String filePath, final Context context) {
      try {
        // Check if the file exists
        final File encryptedFile = new File(filePath);
        if (!encryptedFile.exists()) {
    return null;
        }
        
        // Decrypt the file and return the bytes
        final byte[] encyptedBytes = FileUtils.readFileToByteArray(encryptedFile);
        return decryptBytes(encyptedBytes, context);
      } catch (final IOException e) {
        throw new RuntimeException("Exception occurred trying to decrypt file: " + filePath, e);
      }
    }*/

    /**
     * Generates the secret key to be used for encryption. The secret key is retrieved from the shared preferences if
     * previously calculated.
     * 
     * @return A new secret key if not previously calculated.
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws UnsupportedEncodingException
     */
    private SecretKeySpec getSecretKey(final Context context)
            throws NoSuchAlgorithmException, InvalidKeySpecException, UnsupportedEncodingException {
        // final SocialWorkerSharedPreferences sharedPreferences = SocialWorkerSharedPreferences.getInstance();
        // if (sharedPreferences.getSecretKey() == null) {
        final byte[] salt = generateRandomKeyBytes(SALT_LENGTH);
        final SecretKeyFactory factory = SecretKeyFactory.getInstance(SECRET_KEY_ALGORITHM);
        final PBEKeySpec spec = new PBEKeySpec(SECRET_KEY_PASSPHRASE.toCharArray(), salt, KEY_ITERATIONS, KEY_SIZE);
        final SecretKey secretKey = factory.generateSecret(spec);
        final SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), ENCRYPTION_ALGORITHM);

        // Set the value of the secret key in private shared preferences
        //sharedPreferences.setSecretKey(secretKeySpec);
        return secretKeySpec;
        /*} else {
          return sharedPreferences.getSecretKey();
        }*/
    }

    /**
     * Generates the initialization vector to be used for encryption.
     * 
     * @return the initialization vector.
     */
    private byte[] getIV(final Context context) {
        // final SocialWorkerSharedPreferences sharedPreferences = SocialWorkerSharedPreferences.getInstance();
        // if (sharedPreferences.getIV() == null) {
        try {
            final SecureRandom random = new SecureRandom();
            final byte[] iv = new byte[IV_LENGTH];
            random.nextBytes(iv);
            // sharedPreferences.setIV(iv);
            return iv;
        } catch (final Exception e) {
            Log.e(TAG, "" + e.getMessage(), e);
            return null;
        }
        // } else {
        //   return sharedPreferences.getIV();
        // }
    }

    /**
     * Generates a random Base64 encoded string value.
     * 
     * @param length
     *          The length of the key.
     * @return A random key value.
     */
    public byte[] generateRandomKeyBytes(final int length) {
        byte[] randomKey = null;

        // Use a SecureRandom generator
        try {
            final SecureRandom secureRandom = new SecureRandom();
            final KeyGenerator keyGenerator = KeyGenerator.getInstance(ENCRYPTION_ALGORITHM);
            keyGenerator.init(length, secureRandom);
            final SecretKey secretKey = keyGenerator.generateKey();
            randomKey = secretKey.getEncoded();
        } catch (final NoSuchAlgorithmException e) {
            Log.e(TAG, "Exception generating random key", e);
        }

        return randomKey;
    }

    /**
     * Generates a random Base64 encoded string value.
     * 
     * @param length
     *          The length of the key.
     * @return A random key value.
     */
    public String generateRandomKeyString(final int length) {
        return Base64.encodeToString(generateRandomKeyBytes(length), Base64.DEFAULT);
    }

    @Override
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
        try {
            JSONObject arg_object = args.getJSONObject(0);
            System.err.println("Action is : " + action);
            System.err.println("URL: " + arg_object.getString("location"));
            final String fileLoc = arg_object.getString("location");
            if (ACTION_ENCRYPT_FILE.equals(action)) {
                long time = encryptFile(fileLoc, fileLoc + "_encrypted", null);
                callbackContext.success();
            } else if (ACTION_DECRYPT_FILE.equals(action)) {
                long time = decryptFile(fileLoc + "_encrypted", fileLoc + "_decrypted", null);
                callbackContext.success();
            }
            callbackContext.error("Invalid action");
            return false;
        } catch (Exception e) {
            System.err.println("Exception: " + e.getMessage());
            callbackContext.error(e.getMessage());
            return false;
        }
    }
}