com.stg.crypto.IOObfuscator.java Source code

Java tutorial

Introduction

Here is the source code for com.stg.crypto.IOObfuscator.java

Source

/*
 * Copyright (c) 2014 Mastek Ltd. All rights reserved.
 * 
 * This file is part of JBEAM. JBEAM is free software: you can
 * redistribute it and/or modify it under the terms of the GNU Lesser
 * General Public License as published by the Free Software Foundation.
 *
 * JBEAM is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
 * Public License for the specific language governing permissions and 
 * limitations.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with JBEAM. If not, see <http://www.gnu.org/licenses/>.
 */
package com.stg.crypto;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;

/**
 * A simple utility class that obfuscates the given IO stream.
 * Use static methods for hard-coded encryption otherwise, use non-static methods
 * to make use of Randomized encryption.
 * 
 * @author Kedar Raybagkar
 * @since V1.0R27
 */
public class IOObfuscator {

    static {
        BouncyCastleProvider bcProvider = new BouncyCastleProvider();
        String name = bcProvider.getName();
        Provider installedProvider = Security.getProvider(name);
        if (installedProvider == null) {
            Security.addProvider(bcProvider);
        }
    }

    /**
      * 
      */
    private static final byte[] keyBytes = new byte[] { (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03,
            (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0a, (byte) 0x0b,
            (byte) 0x0c, (byte) 0x0d, (byte) 0x0e, (byte) 0x0f };
    // (byte)0x10, (byte)0x11, (byte)0x12, (byte)0x13, (byte)0x14, (byte)0x15,
    // (byte)0x16, (byte)0x17 };

    /**
      * 
      */
    private static final byte[] ivBytes = new byte[] { (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03,
            (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01 };

    private int strength;

    /**
     * Indicates that the keys should be generated randomly using with the given
     * strength.
     * 
     * @param strength of the key.
     */
    public IOObfuscator(int strength) {
        this.strength = strength;
    }

    /**
     * Returns an wrapped OutputStream. The obfuscation either encryption or
     * decryption is determined by the mode. Remember to call close().
     * 
     * @param out
     *            OutputStream that needs to be encrypted.
     * @param mode
     *            {@link Cipher#ENCRYPT_MODE} or {@link Cipher#DECRYPT_MODE}
     * @return Encrypted OutputStream
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws InvalidAlgorithmParameterException
     * @throws FileNotFoundException
     */
    public static OutputStream wrappedOuputStream(OutputStream out, int mode)
            throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException,
            InvalidAlgorithmParameterException, FileNotFoundException {
        // return out;
        SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
        IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
        Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC");
        cipher.init(mode, key, ivSpec);
        return (new CipherOutputStream(out, cipher));
    }

    /**
     * Returns a wrapped InputStream. The obfuscation either encryption or
     * decryption is determined by the mode. Remember to call close().
     * 
     * @param in
     *            Encrypted Source
     * @param mode
     *            {@link Cipher#ENCRYPT_MODE} or {@link Cipher#DECRYPT_MODE}
     * @return InputStream
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws InvalidAlgorithmParameterException
     * @throws FileNotFoundException
     */
    public static InputStream wrappedInputStream(InputStream in, int mode)
            throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException,
            InvalidAlgorithmParameterException, FileNotFoundException {
        SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
        IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
        Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC");
        cipher.init(mode, key, ivSpec);
        return (new CipherInputStream(in, cipher));
    }

    public OutputStream getEncryptedOutputStream(OutputStream os)
            throws NoSuchAlgorithmException, NoSuchProviderException, IOException, NoSuchPaddingException,
            InvalidKeyException, InvalidAlgorithmParameterException {
        byte[] ivBytes = SecureRandom.getSeed(16);
        KeyGenerator generator = KeyGenerator.getInstance("AES", "BC");
        generator.init(strength);
        Key key = generator.generateKey();
        Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC");
        cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(ivBytes));
        return new MyOS(os, cipher, key, ivBytes);
    }

    /**
     * The passed {@link InputStream} is wrapped around with an decrypt'ed input stream.
     * 
     * @param is InputStream that needs to be wrapped.
     * @return InputStream
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws IOException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws InvalidKeySpecException
     * @throws InvalidAlgorithmParameterException
     */
    public InputStream getDecrytedInputStream(InputStream is)
            throws NoSuchAlgorithmException, NoSuchProviderException, IOException, NoSuchPaddingException,
            InvalidKeyException, InvalidKeySpecException, InvalidAlgorithmParameterException {
        int len = is.read();
        if (len < 0) {
            return is;
        }
        byte[] bytes = new byte[len];
        is.read(bytes);
        len = is.read();
        if (len < 0) {
            return is;
        }
        try {
            byte[] ivBytes = new byte[len];
            is.read(ivBytes);
            SecretKeySpec keySpec;
            try {
                keySpec = new SecretKeySpec(Base64.decode(bytes), "AES");
            } catch (ArrayIndexOutOfBoundsException e) {
                return is;
            }
            Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding", "BC");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(ivBytes));
            return new CipherInputStream(is, cipher);
        } catch (ArrayIndexOutOfBoundsException e) {
            throw new ObfuscationException("The file is not obfuscated this class.");
        }
    }
}

final class MyOS extends CipherOutputStream {

    private boolean isWritten;

    private Key key;

    private byte[] ivBytes;

    /**
     * @param os
     * @param c
     * @param bytes 
     */
    public MyOS(OutputStream os, Cipher c, Key key, byte[] bytes) {
        super(os, c);
        isWritten = false;
        this.key = key;
        this.ivBytes = bytes;
    }

    /*
     * (non-Javadoc)
     * 
     * @see javax.crypto.CipherOutputStream#write(int)
     */
    @Override
    public void write(int b) throws IOException {
        if (!isWritten) {
            persist();
        }
        super.write(b);
    }

    /*
     * (non-Javadoc)
     * 
     * @see javax.crypto.CipherOutputStream#write(byte[])
     */
    @Override
    public void write(byte[] b) throws IOException {
        if (!isWritten) {
            persist();
        }
        super.write(b);
    }

    /*
     * (non-Javadoc)
     * 
     * @see javax.crypto.CipherOutputStream#write(byte[], int, int)
     */
    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        if (!isWritten) {
            persist();
        }
        super.write(b, off, len);
    }

    private void persist() throws IOException {
        synchronized (this) {
            if (!isWritten) {
                byte[] keyBytes = Base64.encode(key.getEncoded());
                super.out.write(keyBytes.length);
                super.out.write(keyBytes);
                super.out.write(ivBytes.length);
                super.out.write(ivBytes);
                isWritten = true;
            }
        }
    }
}