/*
 * keys - a primitive program to export and import (DSA) private and public keys and certificates from/into keystore
 * 
 * Author: Andrey Tretyakov
 */

import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.InvalidKeyException;

import sun.security.provider.DSAPrivateKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;

import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;

public class keys {
		private File keystoreFile;
		private String storetype;
		private char[] storepass;
		private String alias;
		private char[] keypass;
		private File certfile;
		private File publicfile;
		private File privatefile;
		private Certificate certificate;
		private PublicKey publickey;
		private PrivateKey privatekey;
		private boolean certificatespecified;
		private boolean privatekeyspecified;

		public String getPassword() {
			Runtime runtime = Runtime.getRuntime();
			Process process = null;
			String pass = null;
			try {
				//echo off
				String[] cmd = {
						"/bin/sh",
						"-c", 
						"/bin/stty -echo < /dev/tty"
				};
				process = runtime.exec(cmd);
				process.waitFor();
				//read
				pass = (new BufferedReader(new InputStreamReader(System.in))).readLine();
				//echo on
				cmd[2] = "/bin/stty echo < /dev/tty";
				process = runtime.exec(cmd);
				process.waitFor();
			} catch (InterruptedException e) { System.out.println(e);
			} catch (IOException e) { System.out.println(e);
			}
			return pass;
		}

		public PublicKey getPublicKey(KeyStore keystore, String alias) {
			try {
				certificate = keystore.getCertificate(alias);
				if (certificate == null)
					return null;
				return certificate.getPublicKey();
			} catch (KeyStoreException e) {
				certificate = null;
				System.out.println(e);
			}
			return null;
		}

		public PrivateKey getPrivateKey(KeyStore keystore, String alias, char[] password) {
			try {
				Key key = keystore.getKey(alias, password);
				if (key instanceof PrivateKey) {
					return (PrivateKey)key;
				}
			} catch (UnrecoverableKeyException e) { //System.out.println(e);
			} catch (NoSuchAlgorithmException e) { //System.out.println(e);
			} catch (KeyStoreException e) { //System.out.println(e);
			}
			return null;
		}

		public void importKeys() {
			//checks and initialization
			if (certfile == null) {
				System.out.println("Certificate file name was not specified!");
				System.out.println("Use parameters: -certfile <Certificate_File>");
				return;
			}
			if (!certfile.exists()) {
				System.out.println("Certificate file " + certfile.getPath() + " doesn't exist!");
				if (!certificatespecified)
					System.out.println("Use parameters: -certfile <Certificate_File>");
				return;
			}
			if (privatefile == null) {
				System.out.println("Private key file name was not specified!");
				System.out.println("Use parameters: -privatefile <Private_Key_File>");
				return;
			}
			if (!privatefile.exists()) {
				System.out.println("Private key file " + privatefile.getPath() + " doesn't exist!");
				if (!privatekeyspecified)
					System.out.println("Use parameters: -privatefile <Private_Key_File>");
				return;
			}
			FileInputStream inKeystore = null;
			KeyStore keystore = null;
			BASE64Decoder decoder = new BASE64Decoder();
			//get keystore password
			if (storepass == null) {
				System.out.println("Enter keystore password:");
				storepass = getPassword().toCharArray();
			}
			//loading of keystore database
			try {
				keystore = KeyStore.getInstance(storetype);
			} catch (KeyStoreException e) { System.out.println(e); return;
			}
			try {
				if (keystoreFile.exists())
					inKeystore = new FileInputStream(keystoreFile);
			} catch (FileNotFoundException e) { System.out.println("Keystore file " + keystoreFile.getPath() + " not found.\n" + e); inKeystore = null;
			}
			try {
				keystore.load(inKeystore, storepass);
			} catch (NoSuchAlgorithmException e) { System.out.println(e); return;
			} catch (CertificateException e) { System.out.println(e); return;
			} catch (IOException e) { System.out.println("Wrong password for " + keystoreFile.getPath() + "!\n" + e); return;
			}
			//load private key (DSA only!)
			DSAPrivateKey DSAprKey = new DSAPrivateKey();
			try {
				DSAprKey.decode(decoder.decodeBuffer(new FileInputStream(privatefile)));
			} catch (FileNotFoundException e) { System.out.println("Private key file " + privatefile.getPath() + " not found.\n" + e); return;
			} catch (InvalidKeyException e) { System.out.println(e); return;
			} catch (IOException e) { System.out.println(e); return;
			}
			privatekey = DSAprKey;
			char[] password;
			if (keypass != null)
				password = keypass;
			else {
				System.out.println("Enter key password for <" + alias + "> (or press ENTER if same as keystore password):");
				keypass = getPassword().toCharArray();
				if (keypass.length > 0)
					password = keypass;
				else
					password = storepass;
			}
			//load certificate
			try {
				CertificateFactory cf = CertificateFactory.getInstance("X.509");
				X509Certificate X509certificate = (X509Certificate)cf.generateCertificate(new FileInputStream(certfile));
				certificate = X509certificate;
			} catch (FileNotFoundException e) { System.out.println("Certificate file " + certfile.getPath() + " not found.\n" + e); return;
			} catch (CertificateException e) { System.out.println(e); return;
			}
			Certificate[] chain = new Certificate[1];
			chain[0] = certificate;
			//save keys into keystore
			try {
				keystore.setKeyEntry(alias, privatekey, password, chain);
				keystore.store(new FileOutputStream(keystoreFile), storepass);
			} catch (FileNotFoundException e) { System.out.println("Keystore file " + keystoreFile.getPath() + " not found.\n" + e);
			} catch (KeyStoreException e) { System.out.println(e);
			} catch (NoSuchAlgorithmException e) { System.out.println(e);
			} catch (CertificateException e) { System.out.println(e);
			} catch (IOException e) { System.out.println(e);
			}
		}

		public void exportKeys() {
			//checks and initialization
			if (!keystoreFile.exists()) {
				System.out.println("Keystore file " + keystoreFile.getPath() + " doesn't exist!");
				return;
			}
			KeyStore keystore = null;
			BASE64Encoder encoder = new BASE64Encoder();
			//get keystore password
			if (storepass == null) {
				System.out.println("Enter keystore password:");
				storepass = getPassword().toCharArray();
			}
			//loading of keystore database
			try {
				keystore = KeyStore.getInstance(storetype);
			} catch (KeyStoreException e) { System.out.println(e); return;
			}
			try {
				keystore.load(new FileInputStream(keystoreFile), storepass);
			} catch (FileNotFoundException e) { System.out.println("Keystore file " + keystoreFile.getPath() + " not found.\n" + e); return;
			} catch (NoSuchAlgorithmException e) { System.out.println(e); return;
			} catch (CertificateException e) { System.out.println(e); return;
			} catch (IOException e) { System.out.println("Wrong password for " + keystoreFile.getPath() + "!\n" + e); return;
			}
			//get certificate and public key
			if (certfile != null || publicfile != null) {
				publickey = getPublicKey(keystore, alias);
				if (certificate == null)
					System.out.println("Couldn't get certificate for <" + alias + ">!");
				if (publickey == null)
					System.out.println("Couldn't get public key for <" + alias + ">!");
			}
			//save certificate and public key to files
			FileWriter fw = null;
			if (certfile != null && certificate != null)
				try {
					fw = new FileWriter(certfile);
					fw.write("-----BEGIN CERTIFICATE-----\n");
					fw.write(encoder.encode(certificate.getEncoded()));
					fw.write("\n-----END CERTIFICATE-----\n");
					fw.close();
				} catch (CertificateEncodingException e) { System.out.println(e);
				} catch (IOException e) { System.out.println(e);
				}
			if (publicfile != null && publickey != null)
				try {
					fw = new FileWriter(publicfile);
					fw.write(encoder.encode(publickey.getEncoded()));
					fw.close();
				} catch (IOException e) { System.out.println(e);
				}
			//get private key
			if (privatefile != null) {
				privatekey = getPrivateKey(keystore, alias, storepass);
				if (privatekey == null) {
					if (keypass == null) {
						System.out.println("Enter key password for <" + alias + ">:");
						keypass = getPassword().toCharArray();
					}
					privatekey = getPrivateKey(keystore, alias, keypass);
					if (privatekey == null)
						System.out.println("Wrong password for <" + alias + ">!");
				}
				}
			//save private key to file
			if (privatefile != null && privatekey != null)
				try {
					fw = new FileWriter(privatefile);
//					fw.write("—–BEGIN PRIVATE KEY—–\n");
					fw.write(encoder.encode(privatekey.getEncoded()));
//					fw.write("\n—–END PRIVATE KEY—–\n");
					fw.close();
				} catch (IOException e) { System.out.println(e);
				}
		}

		public static void printUsage() {
			System.out.println("\nUsage:\n");
			System.out.println("java keys {-import | -export} -keystore <Keystore_File> [-storepass <Keystore_File_Password>]" +
					" -alias <Alias_Name> [-keypass <Key_Pair_(Alias)_Password>]" +
					" [-storetype <Keystore_Type>] [-certfile <Certificate_File>]" +
					" [-publicfile <Public_Key_File>] [-privatefile <Private_Key_File>]");
			System.out.println("\nNote: You can import only DSA Private keys in PKCS #8 PEM format and certificates (not certificate chains!) in X.509 format.\n");
			System.out.println("\nAuthor: Andrey Tretyakov");
			System.out.println("\nThanks to:\nhttp://www.anandsekar.com/2006/01/19/exporting-the-private-key-from-a-jks-keystore/\nhttp://groups.google.com/group/comp.lang.java.programmer/msg/a132c7feda18187a\n");
		}

		public static void main(String[] args) {
			if (args.length < 1 || args[0] == null || !args[0].equals("-import") && !args[0].equals("-export")) {
				printUsage();
				return;
			}
			keys keys = new keys();
			int i = 1;
			while (i < args.length - 1 && args[i] != null && !args[i].equals("") && args[i+1] != null && !args[i+1].equals("")) {
				if (args[i].equals("-keystore"))
					keys.keystoreFile = new File(args[i+1]); else
				if (args[i].equals("-storepass"))
					keys.storepass = args[i+1].toCharArray(); else
				if (args[i].equals("-alias"))
					keys.alias = args[i+1]; else
				if (args[i].equals("-keypass"))
					keys.keypass = args[i+1].toCharArray(); else
				if (args[i].equals("-storetype"))
					keys.storetype = args[i+1]; else
				if (args[i].equals("-certfile"))
					keys.certfile = new File(args[i+1]); else
				if (args[i].equals("-publicfile"))
					keys.publicfile = new File(args[i+1]); else
				if (args[i].equals("-privatefile"))
					keys.privatefile = new File(args[i+1]); else
					i--;
				i += 2;
			}
			if (keys.keystoreFile == null || keys.alias == null) {
				printUsage();
				return;
			}
			if (keys.storetype == null)
				keys.storetype = "JKS";
			if (keys.certfile != null)
				keys.certificatespecified = true;
			else
				keys.certificatespecified = false;
			if (keys.privatefile != null)
				keys.privatekeyspecified = true;
			else
				keys.privatekeyspecified = false;
			if (keys.certfile == null && keys.publicfile == null && keys.privatefile == null) {
				String s = keys.keystoreFile.getPath();
				if (s.indexOf('/') == -1)
					s = "";
				else
					s = s.substring(0, s.lastIndexOf('/') + 1);
				s = s + keys.alias;
				keys.certfile = new File(s + ".cer");
				keys.publicfile = new File(s + ".public");
				keys.privatefile = new File(s + ".private");
			}
			if (args[0].equals("-import"))
				keys.importKeys();
			if (args[0].equals("-export"))
				keys.exportKeys();
		}
}