/*-------------------------------------------------------------------------
*
* Copyright (c) 2011, PostgreSQL Global Development Group
*
*
*-------------------------------------------------------------------------
*/
package org.postgresql.ssl;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;

/**
 * A CertAuthFactory that's configured using system properties, much like
 * (but independent from) the stock javax.net.ssl SSLSocketFactory implementation.
 */
public class SysPropCertAuthFactory extends AbstractCertAuthFactory {

    /**
     * File-system path to the keystore file to load, or null for system default keystore.
     * 
     * May be the set to the same path as CONFIG_TRUSTSTORE_PATH if trust and key material are
     * in the same store.
     */ 
    public final static String CONFIG_KEYSTORE_PATH = "org.postgresql.jdbc.keystore.path";
    
    /**
     *  Password to use to decode keystore. Ignored if CONFIG_KEYSTORE_PATH unset.
     */
    public final static String CONFIG_KEYSTORE_PWD = "org.postgresql.jdbc.keystore.password";
    
    /**
     * Password to use to decode keys within keystore. If unset, CONFIG_KEYSTORE_PWD used.
     * In most cases the KeyStore password is the same as the key password, so this may be
     * left unset.
     */
    public final static String CONFIG_KEYSTORE_KEYPWD = "org.postgresql.jdbc.keystore.keypassword";
    
    /**
     * KeyStore type, a JSSE KeyStore type name like JKS, JECKS, pkcs12, etc. Ignored if CONFIG_KEYSTORE_PATH unset.
     * 
     * JECKS is the usual format used to store private keys with keytool, etc.
     */ 
    public final static String CONFIG_KEYSTORE_TYPE = "org.postgresql.jdbc.keystore.type";
    
    /**
     * File system path to the trust store file to load, or null for the system default trust store.
     * 
     * May be the same path as CONFIG_KEYSTORE_PATH if trust and key material are
     * in the same store.
     * 
     * The default Sun JDK trustStore is a JKS format store with password "changeme".
     * If you wish to use the system-default list of trusted signers, rather than 
     * specifying the location, type, and password of the system default truststore
     * you should leave CONFIG_TRUSTSTORE_PATH unset so the system default truststore
     * is automatically loaded for you.
     */
    public final static String CONFIG_TRUSTSTORE_PATH = "org.postgresql.jdbc.truststore.path";
    
    /**
     * Password used to decode the truststore.  Ignored if CONFIG_TRUSTSTORE_PATH unset.
     */
    public final static String CONFIG_TRUSTSTORE_PWD = "org.postgresql.jdbc.truststore.password";
    
    /**
     * TrustStore type. Just like CONFIG_KEYSTORE_TYPE, but for the TrustStore.
     * 
     * If the TrustStore is not the same store as the KeyStore, it is common
     * to use the JKS format for it rather than the JECKS format usually used
     * for KeyStores and for combined TrustStore/KeyStores.
     */
    public final static String CONFIG_TRUSTSTORE_TYPE = "org.postgresql.jdbc.truststore.type";
    
    /**
     * The secure random algorithm to use. It is unlikely that you will need to change
     * this, but support for doing so is provided for completeness.
     */
    public final static String CONFIG_SECURERANDOM = "org.postgresql.jdbc.securerandom";

    public SysPropCertAuthFactory() throws IOException, GeneralSecurityException {
        super();
        // Load configuration. First, configure and create the key manager:
        KeyManager[] keyManagers = null;
        String keyStorePath = System.getProperty(CONFIG_KEYSTORE_PATH);
        if (keyStorePath != null && !keyStorePath.isEmpty()) {
            // If only a keystore passphrase is specified but no
            // key passphrase is given, use the keystore passphrase for both the keystore
            // and the key.
            char[] keyStorePass = null, keyPass = null;
            String ksp = System.getProperty(CONFIG_KEYSTORE_PWD, null);
            if (ksp != null) {
                keyStorePass = ksp.toCharArray();
                keyPass = keyStorePass;
            }
            String kp = System.getProperty(CONFIG_KEYSTORE_KEYPWD, null);
            if (kp != null) {
                keyPass = kp.toCharArray();
            }
            keyManagers = createKeyManagers(
                    keyStorePath, keyStorePass, keyPass,
                    System.getProperty(CONFIG_KEYSTORE_TYPE, null));
        }
        
        // then create the trust manager
        TrustManager[] trustManagers = null;
        String trustStorePath = System.getProperty(CONFIG_TRUSTSTORE_PATH);
        if (trustStorePath != null && !trustStorePath.isEmpty()) {
            char[] trustStorePass = null;
            String tsp = System.getProperty(CONFIG_TRUSTSTORE_PWD, null);
            
            if (tsp != null) {
                trustStorePass = tsp.toCharArray();
            }
            trustManagers = createTrustManagers(
                    trustStorePath, trustStorePass,
                    System.getProperty(CONFIG_TRUSTSTORE_TYPE));
        }

        // and SecureRandom instance if configured
        SecureRandom secureRandom = null;
        String secureRandomAlgo = System.getProperty(CONFIG_SECURERANDOM);
        if (secureRandomAlgo != null) {
            secureRandom = SecureRandom.getInstance(secureRandomAlgo);
        }
        
        // Finally, configure the factory.
        buildSSLSocketFactory(keyManagers, trustManagers, secureRandom);
    }

    public SysPropCertAuthFactory(String ignored) throws IOException, GeneralSecurityException {
        this();
    }	

}
