/*
 ************************************************************************
 *
 * Copyright (c) 2003-2004, C&C Research Laboratories, NEC Europe Ltd.
 *
 * Copyright in this software belongs to C&C Research Laboratories,
 * Rathausallee 10, 53757 Sankt Augustin, Germany.
 *
 * This software may not be used, sold, licensed, transferred, copied
 * or reproduced in whole or in part in any manner or form or in or
 * on any media by any person other than in accordance with the terms
 * of the Licence Agreement supplied with the software, or otherwise
 * without the prior written consent of the copyright owners.
 *
 * This software is distributed WITHOUT ANY WARRANTY, without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE, except where stated in the Licence Agreement supplied with
 * the software.
 *
 *  Created By :           G.A. Kohring
 *  Created for Project :  GEMSS (IST-2001-37153)
 *
 ************************************************************************
 *
 */

package de.nece.ccrle.security;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.SecureRandom;
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;

/**
 * An class for managing an SSL client. The <code>UserStoreSSLClient</code>
 * will base trust decisions upon a user supplied truststore.
 *
 * @author Greg Kohring
 */
public class UserStoreSSLClient extends SSLClient{

    private String keyStoreFilename;
    private String keyStoreType;
    private String keyStorePassword;
    private SSLContext sslContext = null;

    /**
     * Constructs a new instance.
     * 
     * @param keyStoreFilename a <code>String</code> containing the name of
     *      the keystore. 
     * @param keyStoreType a <code>String</code> containing the type of
     *      keystore. 
     * @param keyStorePassword a <code>String</code> containing the password
     *      for the keystore.  (Can be <code>null</code> if the integrity
     *      of the keystore does not need to be checked.)
     */
    public UserStoreSSLClient( String keyStoreFilename, String keyStoreType,
                                String keyStorePassword ) {
        if ( keyStoreFilename == null ){
            throw new IllegalArgumentException(" keyStoreFilename == null!");
        }
        this.keyStoreFilename = keyStoreFilename;

        if ( keyStoreType == null ) {
            keyStoreType = "jks";
        } else {
            this.keyStoreType = keyStoreType;
        }

        this.keyStorePassword = keyStorePassword;
    }

    /**
     * Initializes the key store.
     * 
     * @return the initialized <code>KeyStore</code>.
     * @throws IOException if an I/O error occors while accessing the
     *      keystore. 
     * @throws GeneralSecurityException if the supplied password is
     *      not <code>null</code> and is incorrect.
     */
    protected KeyStore initKeyStore() throws IOException, 
                    GeneralSecurityException {

        KeyStore ks = KeyStore.getInstance( keyStoreType );

        BufferedInputStream bis = new BufferedInputStream(
                                new FileInputStream( keyStoreFilename ) );

        if ( keyStorePassword != null ){
            ks.load( bis, keyStorePassword.toCharArray() );
        } else {
            ks.load( bis, null );
        }

        bis.close();

        return ks;
    }

    /**
     * Retrieves the key managers.
     * 
     * @return an array of <code>KeyManagers</code> for the keystore.
     * @throws GeneralSecurityException if the supplied password is
     *      and is incorrect.
     */
    protected KeyManager[] getKeyManagers( KeyStore ks ) 
                throws GeneralSecurityException {

        String defaultAlg = KeyManagerFactory.getDefaultAlgorithm();
        KeyManagerFactory kmFact = KeyManagerFactory.getInstance( defaultAlg );

        kmFact.init( ks, keyStorePassword.toCharArray() );

        KeyManager[] kms = kmFact.getKeyManagers();
        return kms;
    }

    /**
     * Retrieves the trust managers.
     * 
     * @return an array of <code>TrustManagers</code> for the keystore.
     * @throws GeneralSecurityException if the supplied password is
     *      not <code>null</code> and is incorrect.
     */
    protected TrustManager[] getTrustManagers( KeyStore ks )
                throws GeneralSecurityException {

        String defaultAlg = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmFact = 
                                TrustManagerFactory.getInstance( defaultAlg );

        tmFact.init( ks );

        TrustManager[] tms = tmFact.getTrustManagers();
        return tms;
    }

    /**
     * Retrieves the default <code>SSLSocketFactory</code>.
     * 
     * @return an <code>SSLSocketFactory</code> object.
     * @throws IOException if an I/O error occurs while opening the socket.
     * @throws GeneralSecurityException if an error occurs whild handling the
     *      trust material.
     */
    public SSLSocketFactory getSSLSocketFactory() 
                throws IOException, GeneralSecurityException {

        if ( sslContext == null ){
            sslContext = SSLContext.getInstance( "ssl" );

            KeyStore keyStore = initKeyStore();

            KeyManager keyManagers[] = getKeyManagers( keyStore );

            TrustManager trustManagers[] = getTrustManagers( keyStore );

            sslContext.init( keyManagers, trustManagers, new SecureRandom() );
        }

        return sslContext.getSocketFactory();
    }

}
