WaarpPassword.java

/*
 * This file is part of Waarp Project (named also Waarp or GG).
 *
 *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
 *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
 * individual contributors.
 *
 *  All Waarp Project is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * Waarp 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along with
 * Waarp . If not, see <http://www.gnu.org/licenses/>.
 */
package org.waarp.uip;

import org.waarp.common.crypto.Blowfish;
import org.waarp.common.crypto.Des;
import org.waarp.common.crypto.KeyObject;
import org.waarp.common.exception.CryptoException;
import org.waarp.common.file.FileUtils;
import org.waarp.common.logging.SysErrLogger;
import org.waarp.common.utility.SystemPropertyUtil;
import org.waarp.common.utility.WaarpStringUtils;
import org.waarp.common.utility.WaarpSystemUtil;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * Console Command Line Main class to provide Password Management for GoldenGate
 * Products.
 */
public class WaarpPassword {
  static boolean desModel = true;
  static boolean clearPasswordView;
  static final String HELPOPTIONS = "Options available\r\n" +
                                    "* -ki file to specify the Key File by default\r\n" +
                                    "* -ko file to specify a new Key File to build and save\r\n\r\n" +
                                    "* -des to specify DES format (default)\r\n" +
                                    "* -blf to specify BlowFish format\r\n\r\n" +
                                    "* -pi file to specify a GGP File by default(password)\r\n" +
                                    "* -pwd to specify a clear ggp password as entry\r\n" +
                                    "* -cpwd to specify a crypted ggp password as entry\r\n" +
                                    "* -po file to specify a GGP File as output for the password\r\n" +
                                    "* -clear to specify uncrypted password shown as clear text";
  static final String GGPEXTENSION = "ggp";
  static String ki;
  static String ko;
  static String pi;
  static String po;
  static String pwd;
  static String cpwd;

  private File keyFile;
  private File passwordFile;
  private String clearPassword;
  private String cryptedPassword;

  private final KeyObject currentKey;

  /**
   * @param args
   *
   * @throws Exception
   */
  public static void main(final String[] args) throws Exception {
    if (!loadOptions(args)) {
      // Bad options
      WaarpSystemUtil.systemExit(2);
      return;
    }
    final WaarpPassword waarpPassword = new WaarpPassword();
    if (po == null && pi == null) {
      // stop
      SysErrLogger.FAKE_LOGGER.sysout("Key written");
      WaarpSystemUtil.systemExit(0);
      return;
    }
    if (waarpPassword.clearPassword == null ||
        waarpPassword.clearPassword.length() == 0) {
      SysErrLogger.FAKE_LOGGER.sysout("Password to crypt:");
      final String newp = waarpPassword.readString();
      if (newp == null || newp.length() == 0) {
        SysErrLogger.FAKE_LOGGER.syserr("No password as input");
        WaarpSystemUtil.systemExit(4);
        return;
      }
      waarpPassword.setClearPassword(newp);
      if (po != null) {
        waarpPassword.setPasswordFile(new File(po));
        waarpPassword.savePasswordFile();
      }
      if (clearPasswordView) {
        SysErrLogger.FAKE_LOGGER.sysout(
            "ClearPwd: " + waarpPassword.getClearPassword());
        SysErrLogger.FAKE_LOGGER.sysout(
            "CryptedPwd: " + waarpPassword.getCryptedPassword());
      }
    }
  }

  public static boolean loadOptions(final String[] args) {
    desModel = true;
    clearPasswordView = false;
    ki = null;
    ko = null;
    pi = null;
    po = null;
    pwd = null;
    cpwd = null;

    int i;
    if (args.length == 0) {
      SysErrLogger.FAKE_LOGGER.syserr(HELPOPTIONS);
      return false;
    }
    if (!SystemPropertyUtil.isFileEncodingCorrect()) {
      SysErrLogger.FAKE_LOGGER.syserr(
          "Issue while trying to set UTF-8 as default file encoding: use -Dfile.encoding=UTF-8 as java command argument\n" +
          "Currently file.encoding is: " +
          SystemPropertyUtil.get(SystemPropertyUtil.FILE_ENCODING));
      return false;
    }
    for (i = 0; i < args.length; i++) {
      if ("-ki".equalsIgnoreCase(args[i])) {
        i++;
        if (i < args.length) {
          ki = args[i];
        } else {
          SysErrLogger.FAKE_LOGGER.syserr("-ki needs a file as argument");
          return false;
        }
      } else if ("-ko".equalsIgnoreCase(args[i])) {
        i++;
        if (i < args.length) {
          ko = args[i];
        } else {
          SysErrLogger.FAKE_LOGGER.syserr("-ko needs a file as argument");
          return false;
        }
      } else if ("-pi".equalsIgnoreCase(args[i])) {
        i++;
        if (i < args.length) {
          pi = args[i];
        } else {
          SysErrLogger.FAKE_LOGGER.syserr("-pi needs a file as argument");
          return false;
        }
      } else if ("-po".equalsIgnoreCase(args[i])) {
        i++;
        if (i < args.length) {
          po = args[i];
        } else {
          SysErrLogger.FAKE_LOGGER.syserr("-po needs a file as argument");
          return false;
        }
      } else if ("-des".equalsIgnoreCase(args[i])) {
        desModel = true;
      } else if ("-blf".equalsIgnoreCase(args[i])) {
        desModel = false;
      } else if ("-pwd".equalsIgnoreCase(args[i])) {
        i++;
        if (i < args.length) {
          pwd = args[i];
        } else {
          SysErrLogger.FAKE_LOGGER.syserr("-pwd needs a password as argument");
          return false;
        }
      } else if ("-cpwd".equalsIgnoreCase(args[i])) {
        i++;
        if (i < args.length) {
          cpwd = args[i];
        } else {
          SysErrLogger.FAKE_LOGGER.syserr(
              "-cpwd needs a crypted password as argument");
          return false;
        }
      } else if ("-clear".equalsIgnoreCase(args[i])) {
        clearPasswordView = true;
      } else {
        SysErrLogger.FAKE_LOGGER.syserr("Unknown option: " + args[i]);
        return false;
      }
    }
    if (ki == null && ko == null) {
      SysErrLogger.FAKE_LOGGER.syserr(
          "You must specify one of ki or ko options");
      return false;
    }
    if (ki == null) {
      ki = ko;
    }
    if (ki == null && (po != null || pi != null)) {
      SysErrLogger.FAKE_LOGGER.syserr(
          "If pi or po options are set, ki or ko options must be set also!\n");
      return false;
    }
    if (pi == null && po == null && (pwd != null || cpwd != null)) {
      SysErrLogger.FAKE_LOGGER.syserr(
          "Cannot create a password if no password GGP file is specified with pi or po options");
      return false;
    }
    return true;
  }

  public WaarpPassword() throws Exception {
    if (desModel) {
      currentKey = new Des();
    } else {
      currentKey = new Blowfish();
    }
    if (ko != null) {
      createNewKey();
      saveKey(new File(ko));
    }
    if (ki != null) {
      loadKey(new File(ki));
    }
    if (pi != null) {
      setPasswordFile(new File(pi));
      loadPasswordFile();
    }
    if (pwd != null) {
      setClearPassword(pwd);
    }
    if (cpwd != null) {
      setCryptedPassword(cpwd);
    }
    if (po != null) {
      setPasswordFile(new File(po));
      savePasswordFile();
    }
    if (clearPassword != null) {
      if (clearPasswordView) {
        SysErrLogger.FAKE_LOGGER.sysout("ClearPwd: " + getClearPassword());
      }
      SysErrLogger.FAKE_LOGGER.sysout("CryptedPwd: " + getCryptedPassword());
    }
  }

  private String readString() {
    String read = "";
    final InputStreamReader input =
        new InputStreamReader(System.in, WaarpStringUtils.UTF8);
    final BufferedReader reader = new BufferedReader(input);
    try {
      read = reader.readLine();
    } catch (final IOException e) {
      SysErrLogger.FAKE_LOGGER.syserr(e);
    }
    return read;
  }

  /**
   * Create a new Key but do not save it on file
   *
   * @throws Exception
   */
  public final void createNewKey() throws Exception {
    try {
      currentKey.generateKey();
    } catch (final Exception e) {
      throw new CryptoException("Create New Key in error", e);
    }
    if (clearPassword != null) {
      setClearPassword(clearPassword);
    }
  }

  /**
   * @param file source file
   *
   * @throws CryptoException
   */
  public final void loadKey(final File file) throws CryptoException {
    keyFile = file;
    try {
      currentKey.setSecretKey(file);
    } catch (final IOException e) {
      throw new CryptoException("Load Key in error", e);
    }
  }

  /**
   * @param file destination file, if null previously set file is used
   *
   * @throws CryptoException
   */
  public final void saveKey(final File file) throws CryptoException {
    if (file != null) {
      keyFile = file;
    }
    try {
      currentKey.saveSecretKey(keyFile);
    } catch (final IOException e) {
      throw new CryptoException("Save Key in error", e);
    }
  }

  /**
   * @return True if the associated key is ready
   */
  public final boolean keyReady() {
    return currentKey.keyReady();
  }

  /**
   * @return The File associated with the current Key
   */
  public final File getKeyFile() {
    return keyFile;
  }

  /**
   * Set the new password and its crypted value
   *
   * @param passwd
   *
   * @throws Exception
   */
  public final void setClearPassword(final String passwd) throws Exception {
    clearPassword = passwd;
    cryptedPassword = currentKey.cryptToHex(clearPassword);
  }

  /**
   * @return the passwordFile
   */
  public final File getPasswordFile() {
    return passwordFile;
  }

  /**
   * @param passwordFile the passwordFile to set
   *
   * @throws IOException
   */
  public final void setPasswordFile(final File passwordFile) {
    this.passwordFile = passwordFile;
  }

  /**
   * Save the Crypted Paswword to the File
   *
   * @throws IOException
   */
  public final void savePasswordFile() throws IOException {
    final FileOutputStream outputStream = new FileOutputStream(passwordFile);
    try {
      outputStream.write(cryptedPassword.getBytes(WaarpStringUtils.UTF8));
    } finally {
      FileUtils.close(outputStream);
    }
  }

  /**
   * Load the crypted password from the file
   *
   * @throws Exception
   */
  public final void loadPasswordFile() throws Exception {
    if (passwordFile.canRead()) {
      final int len = (int) passwordFile.length();
      final byte[] key = new byte[len];
      final FileInputStream inputStream;
      inputStream = new FileInputStream(passwordFile);
      DataInputStream dis = null;
      try {
        dis = new DataInputStream(inputStream);
        dis.readFully(key);
      } finally {
        if (dis != null) {
          FileUtils.close(dis);
        } else {
          FileUtils.close(inputStream);
        }
      }
      setCryptedPassword(new String(key, WaarpStringUtils.UTF8));
    } else {
      throw new CryptoException("Cannot read crypto file");
    }
  }

  /**
   * @return the cryptedPassword
   */
  public final String getCryptedPassword() {
    return cryptedPassword;
  }

  /**
   * @param cryptedPassword the cryptedPassword to set
   *
   * @throws Exception
   */
  public final void setCryptedPassword(final String cryptedPassword)
      throws Exception {
    this.cryptedPassword = cryptedPassword;
    clearPassword = currentKey.decryptHexInString(cryptedPassword);
  }

  /**
   * @return the clearPassword
   */
  public final String getClearPassword() {
    return clearPassword;
  }

}