import javax.xml.bind.DatatypeConverter;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Date;
import java.text.SimpleDateFormat;

import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import java.security.Key;

public class EncryptString {
  public static void main(String[] args) {
    if(args.length == 4){
        String cyphertext=EncryptString.encryptWithPassword(args[0],args[1],args[2],args[3]);
        System.out.println("encryptWithPassword(password="+args[0]+",salt="+args[1]+",IV="+args[2]+",plaintext="+args[3]+") => "+cyphertext);
        String plaintext=EncryptString.decryptWithPassword(args[0],args[1],args[2],cyphertext);
        System.out.println("decryptWithPassword(password="+args[0]+",salt="+args[1]+",iv="+args[2]+",cyphertext="+cyphertext+") => ["+plaintext+"]");
    }
    else if(args.length == 3){
        String cyphertext=EncryptString.encryptWithKey(args[0],args[1],args[2]);
        System.out.println("encryptWithKey(key="+args[0]+",IV="+args[1]+",plaintext="+args[2]+") => "+cyphertext);
        System.out.println("cyphertext="+DatatypeConverter.printHexBinary(DatatypeConverter.parseBase64Binary(cyphertext)));
        String plaintext=EncryptString.decryptWithKey(args[0],args[1],cyphertext);
        System.out.println("decryptWithKey(key="+args[0]+",iv="+args[1]+",cyphertext="+cyphertext+") => ["+plaintext+"]");
    }
    else{
        System.err.println("usage: java EncryptString { key | password salt} IV plaintext");
    }
  }


  public static String encryptWithPassword(String password, String salt, String iv, String plaintext){
     String cipherText=null;
     byte[] buffer = null;
     try{
         secretKeySpec=null;
         PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(),DatatypeConverter.parseHexBinary(salt),1000,128);
         SecretKeyFactory secretKeyFactory=SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
         //SecretKeyFactory secretKeyFactory=SecretKeyFactory.getInstance("PBEWithMD5AndDES");
         SecretKey secretKey=secretKeyFactory.generateSecret(pbeKeySpec);
         SecretKeySpec secretKeySpec=new SecretKeySpec(secretKey.getEncoded(),"AES");
         System.out.println("secretKeySpec.getEncoded()="+DatatypeConverter.printHexBinary(secretKeySpec.getEncoded())+"  secretKeySpec.getAlgorithm()="+secretKeySpec.getAlgorithm());
         byte[] ivBytes = DatatypeConverter.parseHexBinary(iv);
         IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
         Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding");
         cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
         buffer = cipher.doFinal(plaintext.getBytes());
         System.out.println("plaintext.length()="+plaintext.length()+" buffer.length="+buffer.length);
         cipherText=DatatypeConverter.printBase64Binary(buffer);
     }
     catch (Exception e){
        System.err.println(e.getMessage());
        e.printStackTrace();
     }
     return cipherText;
  }

  public static String decryptWithPassword(String password, String salt, String iv, String cyphertext){
     String plainText = null;
     try{
         byte[] buffer = DatatypeConverter.parseBase64Binary(cyphertext);
         System.out.println("buffer.length="+buffer.length);
         PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(),DatatypeConverter.parseHexBinary(salt),1000,128);
         SecretKeyFactory secretKeyFactory=SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
         //SecretKeyFactory secretKeyFactory=SecretKeyFactory.getInstance("PBEWithMD5AndDES");
        SecretKey secretKey=secretKeyFactory.generateSecret(pbeKeySpec);
         SecretKeySpec secretKeySpec=new SecretKeySpec(secretKey.getEncoded(),"AES");
         byte[] ivBytes = DatatypeConverter.parseHexBinary(iv);
         IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
         Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding");
         cipher.init(Cipher.DECRYPT_MODE, secretKeySpec,ivSpec);
         plainText = new String(cipher.doFinal(buffer));
     }
     catch (Exception e){
        System.err.println(e.getMessage());
        e.printStackTrace();
     }
     return plainText;
  }

  public static String encryptWithKey(String key, String iv, String plaintext){
     String cipherText=null;
     byte[] buffer = null;
     try{
         SecretKeySpec aesKeySpec = new SecretKeySpec(DatatypeConverter.parseHexBinary(key),"AES");
         byte[] ivBytes = DatatypeConverter.parseHexBinary(iv);
         IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
         Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding");
         cipher.init(Cipher.ENCRYPT_MODE, aesKeySpec, ivSpec);
         buffer = cipher.doFinal(plaintext.getBytes());
         System.out.println("plaintext.length()="+plaintext.length()+" buffer.length="+buffer.length);
         cipherText=DatatypeConverter.printBase64Binary(buffer);
     }
     catch (Exception e){
        System.err.println(e.getMessage());
        e.printStackTrace();
     }
     return cipherText;
  }

  public static String decryptWithKey(String key, String iv, String cyphertext){
     String plainText = null;
     try{
         byte[] buffer = DatatypeConverter.parseBase64Binary(cyphertext);
         System.out.println("buffer.length="+buffer.length);
         SecretKeySpec aesKeySpec = new SecretKeySpec(DatatypeConverter.parseHexBinary(key),"AES");
         byte[] ivBytes = DatatypeConverter.parseHexBinary(iv);
         IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
         Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding");
         cipher.init(Cipher.DECRYPT_MODE, aesKeySpec,ivSpec);
         plainText = new String(cipher.doFinal(buffer));
     }
     catch (Exception e){
        System.err.println(e.getMessage());
        e.printStackTrace();
     }
     return plainText;
  }

}