/*
 * Copyright 1999-2010 University of Chicago
 *
 * 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.globus.tools;

import org.globus.gsi.util.CertificateLoadUtil;

import java.io.File;
import java.io.IOException;
import java.io.FileOutputStream;
import java.security.PrivateKey;
import java.security.KeyStore;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;

import org.globus.gsi.OpenSSLKey;
import org.globus.gsi.bc.BouncyCastleOpenSSLKey;
import org.globus.common.Version;
import org.globus.common.CoGProperties;
import org.globus.util.Util;

public class KeyStoreConvert {

    public static final String DEFAULT_ALIAS         = "globus";
    public static final String DEFAULT_PASSWORD      = "globus";
    public static final String DEFAULT_KEYSTORE_FILE = "globus.jks";

    private static final String message =
	"\n" +
	"Syntax: java KeyStoreConvert [options]\n" +
	"        java KeyStoreConvert -help\n\n" +
	"\tConverts Globus credentials (user key and certificate) into \n" +
	"\tJava keystore format (JKS format supported by Sun).\n\n" +
	"\tOptions\n" +
	"\t-help | -usage\n" +
	"\t\tDisplays usage.\n" +
	"\t-version\n" +
	"\t\tDisplays version.\n" +
	"\t-debug\n" +
	"\t\tEnables extra debug output.\n" +
	"\t-cert     <certfile>\n" +
	"\t\tNon-standard location of user certificate.\n" +
	"\t-key      <keyfile>\n" +
	"\t\tNon-standard location of user key.\n" +
	"\t-alias    <alias>\n" +
	"\t\tKeystore alias entry. Defaults to '" +DEFAULT_ALIAS + "'\n" +
	"\t-password <password>\n" +
	"\t\tKeystore password. Defaults to '" +DEFAULT_PASSWORD + "'\n" +
	"\t-out      <keystorefile>\n" +
	"\t\tLocation of the Java keystore file. Defaults to\n" +
	"\t\t'" + DEFAULT_KEYSTORE_FILE + "'\n\n";

    public static void main(String args[]) {

	CoGProperties props = CoGProperties.getDefault();

	boolean error       = false;
	boolean debug       = false;
	String alias        = DEFAULT_ALIAS;
	String password     = DEFAULT_PASSWORD;
	String keyFile      = props.getUserKeyFile();
	String certFile     = props.getUserCertFile();
	String keyStoreFile = DEFAULT_KEYSTORE_FILE;

	for (int i = 0; i < args.length; i++) {
	    if (args[i].equalsIgnoreCase("-debug")) {
		debug = true;
	    } else if (args[i].equalsIgnoreCase("-out")) {
		keyStoreFile = args[++i];
	    } else if (args[i].equalsIgnoreCase("-key")) {
		keyFile = args[++i];
	    } else if (args[i].equalsIgnoreCase("-cert")) {
		certFile = args[++i];
	    } else if (args[i].equalsIgnoreCase("-alias")) {
		alias = args[++i];
	    } else if (args[i].equalsIgnoreCase("-pwd") ||
		       args[i].equalsIgnoreCase("-password")) {
		password = args[++i];
	    } else if (args[i].equalsIgnoreCase("-version")) {
		System.err.println(Version.getVersion());
		System.exit(1);
	    } else if (args[i].equalsIgnoreCase("-help") ||
		       args[i].equalsIgnoreCase("-usage")) {
		System.err.println(message);
		System.exit(1);
	    } else {
		System.err.println("Error: Argument not recognized: " + args[i]);
		error = true;
	    }
	}

	if (error) return;

	if (keyStoreFile == null) {
	    System.err.println("Error: Java key store output file is not specified.");
	    return;
	}

	if (debug) {
	    System.out.println("### Current settings ###");
	    System.out.println("       Certificate file : " + certFile);
	    System.out.println("        SSLeay key file : " + keyFile);
	    System.out.println("     Java keystore file : " + keyStoreFile);
	    System.out.println("        Key entry Alias : " + alias);
	    System.out.println(" Java keystore password : " + password);
	}

	File f = new File(keyStoreFile);
	if (f.exists()) {
	    System.err.println("Error: Output file (" + keyStoreFile + ") already exists.");
	    return;
	}

	int rs = createKeyStore(certFile,
				keyFile,
				alias,
				password,
				keyStoreFile,
				debug);

	// Workaround to fix JNI bug (noticeable on some RedHat 6.1 and 7.1 systems)
	// for a description of the bug see http://java.sun.com/j2se/1.3/relnotes.html
	// and there grep for "ERROR REPORT"
	// gavin McCance <mccance@a5.ph.gla.ac.uk>

	System.exit(rs);
    }

    private static int createKeyStore(String certFile,
				      String keyFile,
				      String alias,
				      String password,
				      String keyStoreFile,
				      boolean debug) {

	X509Certificate [] certs = new X509Certificate[1];
	PrivateKey key = null;

	try {
	    certs[0] = CertificateLoadUtil.loadCertificate(certFile);
	} catch(Exception e) {
	    System.err.println("Failed to load certificate: " + e.getMessage());
	    return -1;
	}

	try {
	    OpenSSLKey sslkey = new BouncyCastleOpenSSLKey(keyFile);

	    if (sslkey.isEncrypted()) {
		String pwd = Util.getPrivateInput("Enter pass phrase: ");

		if (pwd == null) {
		    // user canceled
		    return -2;
		}

		sslkey.decrypt(pwd);
	    }

	    key = sslkey.getPrivateKey();

	} catch(IOException e) {
	    System.err.println("Failed to load key: " + e.getMessage());
	    return -1;
	} catch(GeneralSecurityException e) {
	    System.err.println("Error: Wrong pass phrase");
	    if (debug) {
		e.printStackTrace();
	    }
	    return -1;
	}

	System.out.println("Creating Java keystore...");

	FileOutputStream out = null;

	try {
	    KeyStore ks = KeyStore.getInstance("JKS", "SUN");
	    ks.load(null, null);
	    // this takes a while for some reason
	    ks.setKeyEntry(alias, key, password.toCharArray(), certs);
	    out = new FileOutputStream(keyStoreFile);
	    ks.store(out, password.toCharArray());
	} catch(Exception e) {
	    System.err.println("Failed to create Java key store: " + e.getMessage());
	    return -1;
	} finally {
	    if (out != null) {
		try { out.close(); } catch(IOException ee) {}
	    }
	}

	System.out.println("Java keystore file (" + keyStoreFile +
			   ") successfully created.");

	return 0;
    }

}
