com.rapidminer.cryptography.file.AbstractPBFileCryptographyOperator.java Source code

Java tutorial

Introduction

Here is the source code for com.rapidminer.cryptography.file.AbstractPBFileCryptographyOperator.java

Source

/**
 * RapidMiner Cryptography Extension
 *
 * Copyright (C) 2014-2014 by Nils Woehler
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses/.
 */
package com.rapidminer.cryptography.file;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.encoders.DecoderException;
import org.jasypt.encryption.pbe.PBEByteEncryptor;
import org.jasypt.encryption.pbe.StandardPBEByteEncryptor;

import com.rapidminer.cryptography.PBEncryptorConfigurator;
import com.rapidminer.operator.Operator;
import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.operator.ProcessSetupError.Severity;
import com.rapidminer.operator.SimpleProcessSetupError;
import com.rapidminer.operator.UserError;
import com.rapidminer.operator.nio.file.FileInputPortHandler;
import com.rapidminer.operator.nio.file.FileOutputPortHandler;
import com.rapidminer.operator.ports.InputPort;
import com.rapidminer.operator.ports.OutputPort;
import com.rapidminer.operator.ports.Port;
import com.rapidminer.operator.ports.metadata.MDTransformationRule;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeBoolean;
import com.rapidminer.parameter.PortProvider;
import com.rapidminer.parameter.UndefinedParameterError;
import com.rapidminer.tools.Tools;

/**
 * The abstract super class for all PBE file encryption/decryption operators.
 * The operator has a file input and a file output. Furthermore the user is able
 * to select the algorithm strength and password.
 * 
 * @author Nils Woehler
 * 
 */
public abstract class AbstractPBFileCryptographyOperator extends Operator {

    public static final String PARAMETER_FILE_INPUT = "file_input";
    public static final String PARAMETER_FILE_OUTPUT = "file_output";
    public static final String PARAMETER_BASE64 = "base64";
    public static final String PARAMETER_OVERRIDE = "override";

    private final InputPort fileInput = getInputPorts().createPort("file input");
    private final FileInputPortHandler filePortHandler = new FileInputPortHandler(this, fileInput,
            PARAMETER_FILE_INPUT);

    private final OutputPort fileOutput = getOutputPorts().createPort("file output");
    private final FileOutputPortHandler fileOutputHandler = new FileOutputPortHandler(this, fileOutput,
            PARAMETER_FILE_OUTPUT);

    /**
     * Used to test if encryption works with the current parameters.
     */
    private static final byte[] RANDOM_BYTES = new byte[] { 81, 79, 11, 28, 64, 42, 41 };

    private static final PBEncryptorConfigurator ALGORITHM_PROVIDER = new PBEncryptorConfigurator();

    public AbstractPBFileCryptographyOperator(OperatorDescription description) {
        super(description);

        getTransformer().addRule(new MDTransformationRule() {

            @Override
            public void transformMD() {
                try {
                    PBEByteEncryptor encryptor = configureEncryptor();
                    encryptor.decrypt(encryptor.encrypt(RANDOM_BYTES));
                } catch (Throwable t) {
                    addError(new SimpleProcessSetupError(Severity.ERROR, getPortOwner(), "file.encryption_error",
                            t.getLocalizedMessage()));
                }
            }

        });
        getTransformer().addPassThroughRule(fileInput, fileOutput);
    }

    @Override
    public void doWork() throws OperatorException {

        // first check if output file exists and if overriding is allowed
        if (isParameterSet(PARAMETER_FILE_OUTPUT) && getParameterAsFile(PARAMETER_FILE_OUTPUT).exists()
                && !getParameterAsBoolean(PARAMETER_OVERRIDE)) {
            throw new UserError(this, "file.output_file_already_exists");
        }

        // read input file
        byte[] fileContent = readInputFile();

        // in case of decryption and base64 encoding, decode first
        if (!isEncrypting() && getParameterAsBoolean(PARAMETER_BASE64)) {
            try {
                fileContent = Base64.decode(fileContent);
            } catch (DecoderException e) {
                throw new UserError(this, e, "file.base64_decoding_failed");
            }
        }

        // transform file
        fileContent = transformFile(configureEncryptor(), fileContent);

        // in case of encryption and base64 encoding, decode encrypted output
        if (isEncrypting() && getParameterAsBoolean(PARAMETER_BASE64)) {
            try {
                fileContent = Base64.encode(fileContent);
            } catch (DecoderException e) {
                throw new UserError(this, e, "file.base64_encoding_failed");
            }
        }

        // write encrypted file to output
        try (OutputStream fileOutput = fileOutputHandler.openSelectedFile()) {
            ByteArrayInputStream byteInput = new ByteArrayInputStream(fileContent);
            Tools.copyStreamSynchronously(byteInput, fileOutput, true);
        } catch (IOException e) {
            throw new UserError(this, e, 303, getParameterAsFile(PARAMETER_FILE_OUTPUT), e.getMessage());
        }
    }

    /**
     * Creates and configures a byte encryptor.
     */
    protected PBEByteEncryptor configureEncryptor() throws UndefinedParameterError {
        return ALGORITHM_PROVIDER.configureByteEncryptor(this);
    }

    /**
     * Transforms the input, e.g. encrypts or decrypts it with the provided
     * {@link StandardPBEByteEncryptor}.
     * 
     * @param encryptor
     *            the encryptor being used
     * @param fileContent
     *            the content being encrypted/decrypted
     * @return the encrypted/decrypted content
     */
    protected abstract byte[] transformFile(PBEByteEncryptor encryptor, byte[] fileContent) throws UserError;

    /**
     * Reads input from input file and returns it as byte array.
     */
    private byte[] readInputFile() throws OperatorException {

        // open input stream
        try (InputStream fs = filePortHandler.openSelectedFile()) {
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();

            int nRead;
            byte[] data = new byte[16384];

            while ((nRead = fs.read(data, 0, data.length)) != -1) {
                buffer.write(data, 0, nRead);
            }

            buffer.flush();

            return buffer.toByteArray();
        } catch (IOException e) {
            throw new UserError(this, e, 303, getParameterAsFile(PARAMETER_FILE_INPUT), e.getMessage());
        }
    }

    @Override
    public List<ParameterType> getParameterTypes() {
        List<ParameterType> parameterTypes = super.getParameterTypes();
        parameterTypes.add(FileInputPortHandler.makeFileParameterType(getParameterHandler(), PARAMETER_FILE_INPUT,
                null, new PortProvider() {

                    @Override
                    public Port getPort() {
                        return fileInput;
                    }
                }));

        parameterTypes.addAll(ALGORITHM_PROVIDER.getParameterTypes(this));

        parameterTypes.add(new ParameterTypeBoolean(PARAMETER_BASE64,
                "If checked the output will be Base64 encoded.", false, true));

        parameterTypes.add(FileOutputPortHandler.makeFileParameterType(getParameterHandler(), PARAMETER_FILE_OUTPUT,
                new PortProvider() {

                    @Override
                    public Port getPort() {
                        return fileOutput;
                    }
                }));

        parameterTypes.add(new ParameterTypeBoolean(PARAMETER_OVERRIDE,
                "If checked an already existing file will be overwritten.", false, false));

        return parameterTypes;
    }

    protected abstract boolean isEncrypting();
}