Encrypt.java

package org.flasby.settings;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Iterator;
import java.util.Map.Entry;
import org.flasby.crypto.CryptoUtils;
import org.flasby.crypto.CryptoUtils.CipherPair;

/**
 * encrypts all key value pairs when adding them as a setting. decrypting will generate a new Cipher
 * instance for decryption as every encrypted value will likely have a different IV.
 */
public class Encrypt implements SimpleSettings {

  private static final byte[] DEFAULT_SALT = {
    (byte) 0xA9,
    (byte) 0x9B,
    (byte) 0xC8,
    (byte) 0x32,
    (byte) 0x56,
    (byte) 0x35,
    (byte) 0xE3,
    (byte) 0x03
  };
  private static final byte[] DEFAULT_IV = {
    (byte) 0xA9,
    (byte) 0x9B,
    (byte) 0xC8,
    (byte) 0x32,
    (byte) 0x56,
    (byte) 0x35,
    (byte) 0xE3,
    (byte) 0x03,
    (byte) 0xA9,
    (byte) 0x9B,
    (byte) 0xC8,
    (byte) 0x32,
    (byte) 0x56,
    (byte) 0x35,
    (byte) 0xE3,
    (byte) 0x03,
  };

  private CipherPair cipherPair;

  private final SimpleSettings simpleSettings;

  /**
   * create a wrapped SimpleSettings which is internally encrypted.
   *
   * @param settings to be encrypted.
   */
  public Encrypt(SimpleSettings settings, char[] passPhrase) throws Exception {
    this(settings, passPhrase, DEFAULT_SALT, CryptoUtils.DEFAULT_KEY_ALGORITHM);
  }

  @SuppressFBWarnings(
      value = "EI_EXPOSE_REP2",
      justification = "Exposed simpleSettings, not a bug, works as designed")
  public Encrypt(
      final SimpleSettings settings,
      final char[] passPhrase,
      final byte[] salt,
      final String keyAlgorithm)
      throws Exception {
    simpleSettings = settings;

    // Using the DEFAULT_SALT as it's the correct length - might be a massive error
    cipherPair = CryptoUtils.generateCipherPair(passPhrase, DEFAULT_IV);
  }

  public String encrypt(String toEncrypt) {
    byte[] bytes;
    try {
      bytes = toEncrypt.getBytes(CryptoUtils.ENCODING_CHARSET);
      return CryptoUtils.toBase64(encrypt(bytes));
    } catch (Exception e) {
      throw new RuntimeException("Couldn't encrypt because " + e, e);
    }
  }

  /**
   * encrypt the plain byte array and prepend the IV value.
   *
   * @param toEncrypt
   * @return a pair contaiing the IV and the encrypted array of bytes
   * @throws Exception
   */
  public byte[] encrypt(byte[] toEncrypt) throws Exception {
    return cipherPair.enCipher.doFinal(toEncrypt);
  }

  public String decrypt(String toDecrypt) {
    try {
      byte[] encrypted = CryptoUtils.fromBase64(toDecrypt);
      return new String(decrypt(encrypted), CryptoUtils.ENCODING_CHARSET);
    } catch (Exception e) {
      throw new RuntimeException("Couldn't decrypt because " + e, e);
    }
  }

  public byte[] decrypt(byte[] encrypt) throws Exception {
    return cipherPair.deCipher.doFinal(encrypt);
  }

  public static void main(String[] args) throws Exception {

    String message = "MESSAGEMESSAGEMESSAGEMESSAGEMESSAGEMESSAGE MESSAGE";
    String password = "PASSWORD";

    Encrypt encrypter = new Encrypt(null, password.toCharArray());
    String encrypted = encrypter.encrypt(message);
    String decrypted = encrypter.decrypt(encrypted);

    System.out.println(
        "Encrypt(\"" + message + "\", \"" + password + "\") = \"" + encrypted + "\"");
    System.out.println(
        "Decrypt(\"" + encrypted + "\", \"" + password + "\") = \"" + decrypted + "\"");
  }

  // void setPassword( char[] password);

  @Override
  public Iterator<Entry<String, String>> iterator() {
    return simpleSettings.iterator();
  }

  @Override
  public String get(String name) {
    String v = getRaw(name);
    if (v == null) {
      return v;
    }
    return decrypt(v);
  }

  @Override
  public void add(String name, String value) {
    addRaw(name, encrypt(value));
  }

  @Override
  public void remove(String name) {
    simpleSettings.remove(name);
  }

  @Override
  public boolean isEmpty() {
    return simpleSettings.isEmpty();
  }

  protected String getRaw(String name) {
    return simpleSettings.get(name);
  }

  protected void addRaw(String name, String value) {
    simpleSettings.add(name, value);
  }

  @Override
  public void doIfExists(String name, ApplyWithExistingValue ifExists) {
    simpleSettings.doIfExists(name, ifExists);
  }

  @Override
  public void doIfExists(
      String key, ApplyWithExistingValue ifExists, ApplyWithMissingKey otherwise) {
    if (get(key) != null) {
      ifExists.apply(key, get(key));
    } else {
      otherwise.apply(key);
    }
  }
}