package org.postgresql.ssl; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.security.KeyStore; import java.util.Arrays; import java.util.Properties; import java.security.Security; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import org.postgresql.ssl.WrappedFactory; /** Provides an implementation of socket factory that supports client authentication (sending client certificate to the server) for the PGSQL JDBC driver. Most of this seems to happen automagicaly when a key manager is supplied to the SSLContext init method We try to use the JSSE system properties for configuration. Properties used: An attempt it made to make everything happen with as little configuration as possible. If a trust store is not specified the key store is used instead. If the key store is not specified an attempt is made to use the one created by default by keytool - {user.home}/.keystore. If the key store password is not specified 'changeit' is used. Basically, if a key store is maintained using the default setting for keytool everything should work out of the box. References: http://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html http://archives.postgresql.org/pgsql-jdbc/2006-02/msg00166.php @author Vic Simkus (vic.simkus@simkus.com) */ public class ValidatingFactory extends WrappedFactory { private String _key_store_password = null; private String _key_store_provider = null; private String _key_store_type = null; private String _key_store_location = null; private String _trust_store_location = null; private String _trust_store_password = null; private String _trust_store_provider = null; private String _trust_store_type = null; private String _key_manager_factory_algo = null; private String _trust_manager_factory_algo = null; /** XXX Does this need to be customizable? Defaults to SSLv3 */ private String _ssl_protocol = null; /** XXX Does this need to be customizable? Defaults to SunJSSE */ private String _ssl_ctx_provider = null; private KeyStore _key_store_instance = null; private KeyStore _trust_store_instance = null; private KeyManager [] _key_managers = null; private TrustManager [] _trust_managers = null; private static final String _SYS_PROP_PREFIX = "javax.net.ssl."; private static final String _KEYSTORE_FILE_NAME=".keystore"; public ValidatingFactory() throws Exception { _key_store_password = System.getProperty(_SYS_PROP_PREFIX + "keyStorePassword"); _key_store_provider = System.getProperty(_SYS_PROP_PREFIX + "keyStoreProvider"); _key_store_type = System.getProperty(_SYS_PROP_PREFIX + "keyStoreType"); _key_store_location = System.getProperty(_SYS_PROP_PREFIX + "keyStore"); _trust_store_password = System.getProperty(_SYS_PROP_PREFIX + "trustStorePassword"); _trust_store_provider = System.getProperty(_SYS_PROP_PREFIX + "trustStoreProvider"); _trust_store_type = System.getProperty(_SYS_PROP_PREFIX + "trustStoreType"); _trust_store_location = System.getProperty(_SYS_PROP_PREFIX + "trustStore"); _key_manager_factory_algo = System.getProperty("ssl.KeyManagerFactory.algorithm"); _trust_manager_factory_algo = System.getProperty("ssl.TrustManagerFactory.algorithm"); if(_key_store_password == null) { _key_store_password = "changeit"; } if(_trust_store_password == null) { _trust_store_password = "changeit"; } if(_ssl_protocol == null) //this will always be null initial since we're not trying to configure it via the sytem properties { _ssl_protocol = "SSLv3"; } if(_ssl_ctx_provider == null) //this will always be null initialy. See above. { _ssl_ctx_provider = "SunJSSE"; } if(_key_manager_factory_algo == null) { _key_manager_factory_algo = "SunX509"; } if(_trust_manager_factory_algo == null) { _trust_manager_factory_algo = "PKIX"; } if(_key_store_type == null) { _key_store_type = KeyStore.getDefaultType(); } if(_trust_store_type == null) { _trust_store_type = _key_store_type; } if(_key_store_provider == null) { _key_store_provider = KeyStore.getInstance(_key_store_type).getProvider().getName(); } if(_trust_store_provider == null) { _trust_store_provider = _key_store_provider; } /* Go on a hunt for the key store. */ if(_key_store_location == null) { File keystore_file = new File(System.getProperty("user.home"),_KEYSTORE_FILE_NAME); if(keystore_file.isFile()) { _key_store_location = keystore_file.getAbsolutePath(); } else { throw new Error("Failed to find default key store (.keystore in user's home directory)"); } } if(_trust_store_location == null) { _trust_store_password = _key_store_password; _trust_store_provider = _key_store_provider; _trust_store_type = _key_store_type; _trust_store_location = _key_store_location; } _key_store_instance = getKeyStore(_key_store_type,_key_store_provider,new File(_key_store_location),_key_store_password); _trust_store_instance = getKeyStore(_trust_store_type,_trust_store_provider,new File(_trust_store_location),_trust_store_password); /* Get a factory of a factory of a factory... */ KeyManagerFactory key_manager_factory = KeyManagerFactory.getInstance(_key_manager_factory_algo); key_manager_factory.init(_key_store_instance,_key_store_password.toCharArray()); _key_managers = key_manager_factory.getKeyManagers(); TrustManagerFactory trust_manager_factory = TrustManagerFactory.getInstance(_trust_manager_factory_algo); trust_manager_factory.init(_trust_store_instance); _trust_managers = trust_manager_factory.getTrustManagers(); SSLContext sslContext = SSLContext.getInstance(_ssl_protocol,_ssl_ctx_provider); sslContext.init(_key_managers,_trust_managers, null); _factory = sslContext.getSocketFactory(); } private static KeyStore getKeyStore(String type,String provider,File file,String password) throws Exception { KeyStore key_store = KeyStore.getInstance(type,provider); FileInputStream key_store_input = new FileInputStream(file); key_store.load(key_store_input,password.toCharArray()); key_store_input.close(); return key_store; } }