Java tutorial
/*! ****************************************************************************** * * 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"; } }