package at.gv.egovernment.moa.util;

import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

/**
 * Utility for connecting to server applications via SSL.
 * 
 * @author Paul Ivancsics
 * @version $Id$
 */
public class SSLUtils {
	
	/**
	 * Creates an <code>SSLSocketFactory</code> which utilizes the given trust store.
	 * 
   * @param trustStoreType key store type of trust store
   * @param trustStoreInputStream input stream for reading JKS trust store containing
   * 				 trusted server certificates; if <code>null</code>, the default
   * 				 trust store will be utilized
   * @param trustStorePassword if provided, it will be used to check 
   * 				 the integrity of the trust store; if omitted, it will not be checked
   * @return <code>SSLSocketFactory</code> to be used by an <code>HttpsURLConnection</code>
   * @throws IOException thrown while reading from the input stream
   * @throws GeneralSecurityException thrown while creating the socket factory
	 */
  public static SSLSocketFactory getSSLSocketFactory(
  	String trustStoreType,
  	InputStream trustStoreInputStream,
  	String trustStorePassword)
 	  throws IOException, GeneralSecurityException {
  		
	  TrustManager[] tms = getTrustManagers(trustStoreType, trustStoreInputStream, trustStorePassword);
		SSLContext ctx = SSLContext.getInstance("TLS");
		ctx.init(null, tms, null);

    SSLSocketFactory sf = ctx.getSocketFactory();
  	return sf;
  }
	/**
	 * Creates an <code>SSLSocketFactory</code> which utilizes the
	 * given trust store and keystore.
	 * 
   * @param trustStore trust store containing trusted server certificates; 
   * 				 if <code>null</code>, the default trust store will be utilized
   * @param clientKeyStoreType key store type of <code>clientKeyStore</code>
   * @param clientKeyStoreURL URL of key store containing keys to be used for
   * 				 client authentication; if <code>null</code>, the default key store will be utilized
   * @param clientKeyStorePassword if provided, it will be used to check 
   * 				 the integrity of the client key store; if omitted, it will not be checked
   * @return <code>SSLSocketFactory</code> to be used by an <code>HttpsURLConnection</code>
   * @throws IOException thrown while reading key store file
   * @throws GeneralSecurityException thrown while creating the socket factory
	 */
  public static SSLSocketFactory getSSLSocketFactory(
  	KeyStore trustStore,
  	String clientKeyStoreType,
  	String clientKeyStoreURL,
  	String clientKeyStorePassword)
 	  throws IOException, GeneralSecurityException {
  		
		SSLContext ctx = getSSLContext(
			trustStore, clientKeyStoreType, clientKeyStoreURL, clientKeyStorePassword);
    SSLSocketFactory sf = ctx.getSocketFactory();
  	return sf;
  }
	/**
	 * Creates an <code>SSLContext</code> initialized for the
	 * given trust store and keystore.
	 * 
   * @param trustStore trust store containing trusted server certificates; 
   * 				 if <code>null</code>, the default trust store will be utilized
   * @param clientKeyStoreType key store type of <code>clientKeyStore</code>
   * @param clientKeyStoreURL URL of key store containing keys to be used for
   * 				 client authentication; if <code>null</code>, the default key store will be utilized
   * @param clientKeyStorePassword if provided, it will be used to check 
   * 				 the integrity of the client key store; if omitted, it will not be checked
   * @return <code>SSLContext</code> to be used for creating an <code>SSLSocketFactory</code>
   * @throws IOException thrown while reading key store file
   * @throws GeneralSecurityException thrown while creating the SSL context
	 */
  public static SSLContext getSSLContext(
  	KeyStore trustStore,
  	String clientKeyStoreType,
  	String clientKeyStoreURL,
  	String clientKeyStorePassword)
 	  throws IOException, GeneralSecurityException {
  		
    //System.setProperty("javax.net.debug", "all");
	  TrustManager[] tms = getTrustManagers(trustStore);
		KeyManager[] kms = getKeyManagers(clientKeyStoreType, clientKeyStoreURL, clientKeyStorePassword);
		SSLContext ctx = SSLContext.getInstance("TLS");
		ctx.init(kms, tms, null);
		return ctx;
  }
  /**
   * Loads the trust store from an input stream and gets the 
   * <code>TrustManager</code>s from a default <code>TrustManagerFactory</code>,
	 * initialized from the given trust store.
   * @param trustStoreType key store type of trust store
   * @param trustStoreInputStream input stream for reading JKS trust store containing
   * 				 trusted server certificates; if <code>null</code>, the default
   * 				 trust store will be utilized
   * @param trustStorePassword if provided, it will be used to check 
   * 				 the integrity of the trust store; if omitted, it will not be checked
	 * @return <code>TrustManager</code>s to be used for creating an 
	 * 				  <code>SSLSocketFactory</code> utilizing the given trust store
   * @throws IOException thrown while reading from the input stream
   * @throws GeneralSecurityException thrown while initializing the 
   * 					default <code>TrustManagerFactory</code>
   */
	protected static TrustManager[] getTrustManagers(
		String trustStoreType,
		InputStream trustStoreInputStream,
  	String trustStorePassword)
	  throws IOException, GeneralSecurityException {
	  	
	  if (trustStoreInputStream == null)
	  	return null;

	  // Set up the TrustStore to use. We need to load the file into
	  // a KeyStore instance.
		KeyStore trustStore = KeyStoreUtils.loadKeyStore(trustStoreType, trustStoreInputStream, trustStorePassword);
		return getTrustManagers(trustStore);
	}
	/**
	 * Gets the <code>TrustManager</code>s from a default <code>TrustManagerFactory</code>,
	 * initialized from the given trust store.
	 * 
	 * @param trustStore the trust store to use
	 * @return <code>TrustManager</code>s to be used for creating an 
	 * 				  <code>SSLSocketFactory</code> utilizing the given trust store
   * @throws GeneralSecurityException thrown while initializing the 
   * 					default <code>TrustManagerFactory</code>
	 */
	protected static TrustManager[] getTrustManagers(KeyStore trustStore)
	  throws GeneralSecurityException {
	  	
	  if (trustStore == null)
	  	return null;

	  // Initialize the default TrustManagerFactory with this KeyStore
	  String alg=TrustManagerFactory.getDefaultAlgorithm();
	  TrustManagerFactory tmFact=TrustManagerFactory.getInstance(alg);
	  tmFact.init(trustStore);
	
	  // And now get the TrustManagers
	  TrustManager[] tms=tmFact.getTrustManagers();
	  return tms;
	}
  /**
   * Loads the client key store from file and gets the 
   * <code>KeyManager</code>s from a default <code>KeyManagerFactory</code>,
	 * initialized from the given client key store.
   * @param clientKeyStoreType key store type of <code>clientKeyStore</code>
   * @param clientKeyStoreURL URL of key store containing keys to be used for
   * 				 client authentication; if <code>null</code>, the default key store will be utilized
   * @param clientKeyStorePassword password used to check the integrity of the client key store; 
   * 				 if <code>null</code>, it will not be checked
	 * @return <code>KeyManager</code>s to be used for creating an 
	 * 				  <code>SSLSocketFactory</code> utilizing the given client key store
   * @throws IOException thrown while reading from the key store file
   * @throws GeneralSecurityException thrown while initializing the 
   * 					default <code>KeyManagerFactory</code>
   */
  public static KeyManager[] getKeyManagers (
		String clientKeyStoreType,
		String clientKeyStoreURL,
  	String clientKeyStorePassword)
	  throws IOException, GeneralSecurityException {
  
  	if (clientKeyStoreURL == null)
  		return null;
  		
	  // Set up the KeyStore to use. We need to load the file into
	  // a KeyStore instance.
	  KeyStore clientKeyStore = KeyStoreUtils.loadKeyStore(
	  	clientKeyStoreType, clientKeyStoreURL, clientKeyStorePassword);
		return getKeyManagers(clientKeyStore, clientKeyStorePassword);
	}  
  /**
   * Gets the <code>KeyManager</code>s from a default <code>KeyManagerFactory</code>,
	 * initialized from the given client key store.
   * @param clientKeyStore client key store
   * @param clientKeyStorePassword if provided, it will be used to check 
   * 				 the integrity of the client key store; if omitted, it will not be checked
	 * @return <code>KeyManager</code>s to be used for creating an 
	 * 				  <code>SSLSocketFactory</code> utilizing the given client key store
   * @throws GeneralSecurityException thrown while initializing the 
   * 					default <code>KeyManagerFactory</code>
   */
	public static KeyManager[] getKeyManagers (
		KeyStore clientKeyStore,
  	String clientKeyStorePassword)
	  throws GeneralSecurityException {
  
  	if (clientKeyStore == null)
  		return null;
  		
	  // Now we initialize the default KeyManagerFactory with this KeyStore
	  String alg=KeyManagerFactory.getDefaultAlgorithm();
	  KeyManagerFactory kmFact=KeyManagerFactory.getInstance(alg);
  	char[] password = null;
  	if (clientKeyStorePassword != null)
  		password = clientKeyStorePassword.toCharArray();
	  kmFact.init(clientKeyStore, password);
	
	  // And now get the KeyManagers
	  KeyManager[] kms=kmFact.getKeyManagers();
	  return kms;
	}  
}