org.pentaho.di.job.entries.pgpencryptfiles.GPG.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.di.job.entries.pgpencryptfiles.GPG.java

Source

/*! ******************************************************************************
 *
 * Pentaho Data Integration
 *
 * Copyright (C) 2002-2013 by Pentaho : http://www.pentaho.com
 *
 *******************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ******************************************************************************/

package org.pentaho.di.job.entries.pgpencryptfiles;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileType;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.logging.LogChannelInterface;
import org.pentaho.di.core.vfs.KettleVFS;
import org.pentaho.di.i18n.BaseMessages;

/**
 * This defines a GnuPG wrapper class.
 *
 * @author Samatar
 * @since 25-02-2011
 *
 */

public class GPG {

    private static Class<?> PKG = JobEntryPGPEncryptFiles.class; // for i18n purposes, needed by Translator2!!

    private LogChannelInterface log;

    private final String gnuPGCommand = "--batch --armor ";

    /** gpg program location **/
    private String gpgexe = "/usr/local/bin/gpg";

    /** temporary file create when running command **/
    private File tmpFile;

    /**
     * Reads an output stream from an external process. Implemented as a thread.
     */
    class ProcessStreamReader extends Thread {
        StringBuffer stream;
        InputStreamReader in;

        static final int BUFFER_SIZE = 1024;

        /**
         * Creates new ProcessStreamReader object.
         *
         * @param in
         */
        ProcessStreamReader(InputStream in) {
            super();

            this.in = new InputStreamReader(in);

            this.stream = new StringBuffer();
        }

        public void run() {
            try {
                int read;
                char[] c = new char[BUFFER_SIZE];

                while ((read = in.read(c, 0, BUFFER_SIZE - 1)) > 0) {
                    stream.append(c, 0, read);
                    if (read < BUFFER_SIZE - 1) {
                        break;
                    }
                }
            } catch (IOException io) {
                // Ignore read errors
            }
        }

        String getString() {
            return stream.toString();
        }
    }

    /**
     * Constructs a new GnuPG
     *
     * @param gpgFilename
     *          gpg program location
     * @param logInterface
     *          LogChannelInterface
     * @throws KettleException
     */
    public GPG(String gpgFilename, LogChannelInterface logInterface) throws KettleException {
        this.log = logInterface;
        this.gpgexe = gpgFilename;
        // Let's check GPG filename
        if (Const.isEmpty(getGpgExeFile())) {
            // No filename specified
            throw new KettleException(BaseMessages.getString(PKG, "GPG.GPGFilenameMissing"));
        }
        // We have a filename, we need to check
        FileObject file = null;
        try {
            file = KettleVFS.getFileObject(getGpgExeFile());

            if (!file.exists()) {
                throw new KettleException(BaseMessages.getString(PKG, "GPG.GPGFilenameNotFound"));
            }
            // The file exists
            if (!file.getType().equals(FileType.FILE)) {
                throw new KettleException(BaseMessages.getString(PKG, "GPG.GPGNotAFile", getGpgExeFile()));
            }

            // Ok we have a real file
            // Get the local filename
            this.gpgexe = KettleVFS.getFilename(file);

        } catch (Exception e) {
            throw new KettleException(BaseMessages.getString(PKG, "GPG.ErrorCheckingGPGFile", getGpgExeFile()), e);
        } finally {
            try {
                if (file != null) {
                    file.close();
                }
            } catch (Exception e) {
                // Ignore close errors
            }
        }
    }

    /**
     * Returns GPG program location
     *
     * @return GPG filename
     */
    public String getGpgExeFile() {
        return this.gpgexe;
    }

    /**
     * Runs GnuPG external program
     *
     * @param commandArgs
     *          command line arguments
     * @param inputStr
     *          key ID of the key in GnuPG's key database
     * @param fileMode
     * @return result
     * @throws KettleException
     */
    private String execGnuPG(String commandArgs, String inputStr, boolean fileMode) throws KettleException {
        Process p;
        String command = getGpgExeFile() + " " + (fileMode ? "" : gnuPGCommand + " ") + commandArgs;

        if (log.isDebug()) {
            log.logDebug(BaseMessages.getString(PKG, "GPG.RunningCommand", command));
        }
        String retval;

        try {
            if (Const.isWindows()) {
                p = Runtime.getRuntime().exec(command);
            } else {
                ProcessBuilder processBuilder = new ProcessBuilder("/bin/sh", "-c", command);
                p = processBuilder.start();
            }
        } catch (IOException io) {
            throw new KettleException(BaseMessages.getString(PKG, "GPG.IOException"), io);
        }

        ProcessStreamReader psr_stdout = new ProcessStreamReader(p.getInputStream());
        ProcessStreamReader psr_stderr = new ProcessStreamReader(p.getErrorStream());
        psr_stdout.start();
        psr_stderr.start();
        if (inputStr != null) {
            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
            try {
                out.write(inputStr);
            } catch (IOException io) {
                throw new KettleException(BaseMessages.getString(PKG, "GPG.ExceptionWrite"), io);
            } finally {
                if (out != null) {
                    try {
                        out.close();
                    } catch (Exception e) {
                        // Ignore
                    }
                }
            }
        }

        try {
            p.waitFor();

            psr_stdout.join();
            psr_stderr.join();
        } catch (InterruptedException i) {
            throw new KettleException(BaseMessages.getString(PKG, "GPG.ExceptionWait"), i);
        }

        try {
            if (p.exitValue() != 0) {
                throw new KettleException(
                        BaseMessages.getString(PKG, "GPG.Exception.ExistStatus", psr_stderr.getString()));
            }
        } catch (IllegalThreadStateException itse) {
            throw new KettleException(BaseMessages.getString(PKG, "GPG.ExceptionillegalThreadStateException"),
                    itse);
        } finally {
            p.destroy();
        }

        retval = psr_stdout.getString();

        return retval;

    }

    /**
     * Decrypt a file
     *
     * @param cryptedFilename
     *          crypted filename
     * @param passPhrase
     *          passphrase for the personal private key to sign with
     * @param decryptedFilename
     *          decrypted filename
     * @throws KettleException
     */
    public void decryptFile(FileObject cryptedFilename, String passPhrase, FileObject decryptedFilename)
            throws KettleException {

        decryptFile(KettleVFS.getFilename(cryptedFilename), passPhrase, KettleVFS.getFilename(decryptedFilename));
    }

    /**
     * Decrypt a file
     *
     * @param cryptedFilename
     *          crypted filename
     * @param passPhrase
     *          passphrase for the personal private key to sign with
     * @param decryptedFilename
     *          decrypted filename
     * @throws KettleException
     */
    public void decryptFile(String cryptedFilename, String passPhrase, String decryptedFilename)
            throws KettleException {

        try {
            execGnuPG("--batch --yes "
                    + (Const.isEmpty(passPhrase) ? "" : "--passphrase " + "\"" + passPhrase + "\" ") + "--output "
                    + "\"" + decryptedFilename + "\" " + "--decrypt " + "\"" + cryptedFilename + "\"", null, true);

        } catch (Exception e) {
            throw new KettleException(e);
        }
    }

    /**
     * Encrypt a file
     *
     * @param filename
     *          file to encrypt
     * @param userID
     *          specific user id key
     * @param cryptedFilename
     *          crypted filename
     * @param asciiMode
     *          output ASCII file
     * @throws KettleException
     */
    public void encryptFile(FileObject filename, String userID, FileObject cryptedFilename, boolean asciiMode)
            throws KettleException {
        encryptFile(KettleVFS.getFilename(filename), userID, KettleVFS.getFilename(cryptedFilename), asciiMode);
    }

    /**
     * Encrypt a file
     *
     * @param filename
     *          file to encrypt
     * @param userID
     *          specific user id key
     * @param cryptedFilename
     *          crypted filename
     * @param asciiMode
     *          output ASCII file
     * @throws KettleException
     */
    public void encryptFile(String filename, String userID, String cryptedFilename, boolean asciiMode)
            throws KettleException {
        try {
            execGnuPG(
                    "--batch --yes" + (asciiMode ? " -a" : "") + " -r " + "\"" + Const.NVL(userID, "") + "\" "
                            + "--output " + "\"" + cryptedFilename + "\" " + "--encrypt  " + "\"" + filename + "\"",
                    null, true);

        } catch (Exception e) {
            throw new KettleException(e);
        }
    }

    /**
     * Sign and encrypt a file
     *
     * @param file
     *          file to encrypt
     * @param userID
     *          specific user id key
     * @param cryptedFile
     *          crypted filename
     * @param asciiMode
     *          output ASCII file
     * @throws KettleException
     */
    public void signAndEncryptFile(FileObject file, String userID, FileObject cryptedFile, boolean asciiMode)
            throws KettleException {
        signAndEncryptFile(KettleVFS.getFilename(file), userID, KettleVFS.getFilename(cryptedFile), asciiMode);
    }

    /**
     * Sign and encrypt a file
     *
     * @param filename
     *          file to encrypt
     * @param userID
     *          specific user id key
     * @param cryptedFilename
     *          crypted filename
     * @param asciiMode
     *          output ASCII file
     * @throws KettleException
     */
    public void signAndEncryptFile(String filename, String userID, String cryptedFilename, boolean asciiMode)
            throws KettleException {

        try {

            execGnuPG(
                    "--batch --yes" + (asciiMode ? " -a" : "")
                            + (Const.isEmpty(userID) ? "" : " -r " + "\"" + userID + "\"") + " " + "--output "
                            + "\"" + cryptedFilename + "\" " + "--encrypt --sign " + "\"" + filename + "\"",
                    null, true);
        } catch (Exception e) {
            throw new KettleException(e);
        }
    }

    /**
     * Sign a file
     *
     * @param filename
     *          file to encrypt
     * @param userID
     *          specific user id key
     * @param cryptedFilename
     *          crypted filename
     * @param asciiMode
     *          output ASCII file
     * @throws KettleException
     */
    public void signFile(String filename, String userID, String signedFilename, boolean asciiMode)
            throws KettleException {
        try {
            execGnuPG("--batch --yes" + (asciiMode ? " -a" : "")
                    + (Const.isEmpty(userID) ? "" : " -r " + "\"" + userID + "\"") + " " + "--output " + "\""
                    + signedFilename + "\" " + (asciiMode ? "--clearsign " : "--sign ") + "\"" + filename + "\"",
                    null, true);

        } catch (Exception e) {
            throw new KettleException(e);
        }
    }

    /**
     * Sign a file
     *
     * @param file
     *          file to encrypt
     * @param userID
     *          specific user id key
     * @param signedFile
     *          crypted filename
     * @param asciiMode
     *          output ASCII file
     * @throws KettleException
     */
    public void signFile(FileObject file, String userID, FileObject signedFile, boolean asciiMode)
            throws KettleException {
        try {
            signFile(KettleVFS.getFilename(file), userID, KettleVFS.getFilename(signedFile), asciiMode);

        } catch (Exception e) {
            throw new KettleException(e);
        }
    }

    /**
     * Verify a signature
     *
     * @param filename
     *          filename
     * @throws KettleException
     */
    public void verifySignature(FileObject filename) throws KettleException {
        verifySignature(KettleVFS.getFilename(filename));
    }

    /**
     * Verify a signature
     *
     * @param filename
     *          filename
     * @originalFilename fill this value in case of detached signature
     * @throws KettleException
     */
    public void verifySignature(String filename) throws KettleException {

        execGnuPG("--batch --verify " + "\"" + filename + "\"", null, true);
    }

    /**
     * Verify a signature for detached file
     *
     * @param signatureFilename
     *          filename
     * @param originalFilenamefill
     *          this value in case of detached signature
     * @throws KettleException
     */
    public void verifyDetachedSignature(String signatureFilename, String originalFilename) throws KettleException {
        execGnuPG("--batch --verify " + "\"" + signatureFilename + "\" " + "\"" + originalFilename + "\"", null,
                true);
    }

    /**
     * Verify a signature for detached file
     *
     * @param signatureFile
     *          filename
     * @param originalFile
     *          fill this value in case of detached signature
     * @throws KettleException
     */
    public void verifyDetachedSignature(FileObject signatureFile, FileObject originalFile) throws KettleException {
        verifyDetachedSignature(KettleVFS.getFilename(signatureFile), KettleVFS.getFilename(originalFile));
    }

    /**
     * Encrypt a string
     *
     * @param plainText
     *          input string to encrypt
     * @param keyID
     *          key ID of the key in GnuPG's key database to encrypt with
     * @return encrypted string
     * @throws KettleException
     */
    public String encrypt(String plainText, String keyID) throws KettleException {
        return execGnuPG("-r \"" + keyID + "\" --encrypt ", plainText, false);

    }

    /**
     * Signs and encrypts a string
     *
     * @param plainText
     *          input string to encrypt
     * @param userID
     *          key ID of the key in GnuPG's key database to encrypt with
     * @param passPhrase
     *          passphrase for the personal private key to sign with
     * @return encrypted string
     * @throws KettleException
     */
    public String signAndEncrypt(String plainText, String userID, String passPhrase) throws KettleException {
        try {
            createTempFile(plainText);

            return execGnuPG("-r \"" + userID + "\" --passphrase-fd 0 -se \"" + getTempFileName() + "\"",
                    passPhrase, false);
        } finally {

            deleteTempFile();
        }

    }

    /**
     * Sign
     *
     * @param stringToSign
     *          input string to sign
     * @param passPhrase
     *          passphrase for the personal private key to sign with
     * @throws KettleException
     */
    public String sign(String stringToSign, String passPhrase) throws KettleException {
        String retval;
        try {

            createTempFile(stringToSign);

            retval = execGnuPG("--passphrase-fd 0 --sign \"" + getTempFileName() + "\"", passPhrase, false);

        } finally {
            deleteTempFile();
        }
        return retval;
    }

    /**
     * Decrypt a string
     *
     * @param cryptedText
     *          input string to decrypt
     * @param passPhrase
     *          passphrase for the personal private key to sign with
     * @return plain text
     * @throws KettleException
     */
    public String decrypt(String cryptedText, String passPhrase) throws KettleException {
        try {
            createTempFile(cryptedText);

            return execGnuPG("--passphrase-fd 0 --decrypt \"" + getTempFileName() + "\"", passPhrase, false);

        } finally {
            deleteTempFile();
        }
    }

    /**
     * Create a unique temporary file when needed by one of the main methods. The file handle is store in tmpFile object
     * var.
     *
     * @param content
     *          data to write into the file
     * @throws KettleException
     */
    private void createTempFile(String content) throws KettleException {
        this.tmpFile = null;
        FileWriter fw;

        try {
            this.tmpFile = File.createTempFile("GnuPG", null);
            if (log.isDebug()) {
                log.logDebug(BaseMessages.getString(PKG, "GPG.TempFileCreated", getTempFileName()));
            }
        } catch (Exception e) {
            throw new KettleException(BaseMessages.getString(PKG, "GPG.ErrorCreatingTempFile"), e);
        }

        try {
            fw = new FileWriter(this.tmpFile);
            fw.write(content);
            fw.flush();
            fw.close();
        } catch (Exception e) {
            // delete our file:
            deleteTempFile();

            throw new KettleException(BaseMessages.getString(PKG, "GPG.ErrorWritingTempFile"), e);
        }
    }

    /**
     * Delete temporary file.
     *
     * @throws KettleException
     */
    private void deleteTempFile() {
        if (this.tmpFile != null) {
            if (log.isDebug()) {
                log.logDebug(BaseMessages.getString(PKG, "GPG.DeletingTempFile", getTempFileName()));
            }
            this.tmpFile.delete();
        }
    }

    /**
     * Returns temporary filename.
     *
     * @return temporary filename
     */
    private String getTempFileName() {
        return this.tmpFile.getAbsolutePath();
    }

    public String toString() {
        return "GPG";
    }

}