/*------------------------------------------------------------------------- * * Copyright (c) 2011, PostgreSQL Global Development Group * * *------------------------------------------------------------------------- */ package org.postgresql.ssl; import java.io.FileInputStream; 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.TrustManager; import javax.net.ssl.TrustManagerFactory; /** * Provide an implementation of SSLSocketFactory that allows client authentication. * * This code creates and wraps a SSLSocketFactory using the paths and passwords * obtained from calls to abstract methods that must be defined by a subclass. * You cannot use this class as-is. See SysPropCertAuthFactory for one you can * use unmodified. * * Subclasses must call buildSSLSocketFactory() at the end of their * constructors, and must call the superclass constructors at the beginning * of their constructors. Subclasses must also implement the abstract methods * so that appropriate configuration details are returned. * * @author Marc-André Laverdière (marc-andre@atc.tcs.com / marcandre.laverdiere@tcs.com) */ //// //// TODO: Delegate trust checks to system trustmanager if //// no match in our own trustmanager? //// //// TODO: way to override keymanager/trustmanager supply without //// haivng to override those abstracts? public abstract class AbstractCertAuthFactory extends WrappedFactory { protected final static String DEFAULT_SSL_PROTOCOL_NAME = "SSL"; protected AbstractCertAuthFactory() { } protected AbstractCertAuthFactory(String ignored) { } /** * Builds an SSLContext with a trust store and key store constructed * using the parameters provided by the subclass's implementation of * AbstractCertAuthFactory's abstract methods. * * If a null value is passed for any argument, the system default will * be used. For example, a null keyManagers array will cause * the SSLSocketFactory to use an SSLContext with a system-default KeyStore. * On the Sun JRE, this is created using the JSSE javax.net.ssl system * properties. Similar rules apply for the TrustStore and SecureRandom instance. * * See http://download.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html . * * @param keyManagers KeyManager(s) to obtain key material from, or null for JSSE default keystore * @param trustManagers TrustManager(s) to obtain trust material from, or null for JSSE default truststore * @param secureRandom Secure random generator. Unless you know you need to do otherwise, leave this null to use the default. * */ protected void buildSSLSocketFactory(KeyManager[] keyManagers, TrustManager[] trustManagers, SecureRandom secureRandom) throws IOException, GeneralSecurityException{ if (_factory != null) { throw new IllegalStateException("buildSSLSocketFactory() already called!"); } //Create + Initialize TLS context SSLContext context = SSLContext.getInstance(getSSLProtocolName()); context.init(keyManagers, trustManagers, secureRandom); _factory = context.getSocketFactory(); } /** * Create and return KeyManager(s) using the passed information. * * If keyStoreType is null, the default keystore type for the system default * JSSE provider will be used. If the keyPass is null, the keyStorePass will * also be used to decode the key. No other arguments may be null. * * @param keyStorePath Location of keystore on file system. Non-null. * @param keyStorePass Password/passphrase to decrypt keystore with. Non-null. * @param keyPass Password/passphrase to decrypt key(s) of interest within KeyStore with. If null, keyStorePass used. * @param keyStoreType JSSE keystore type. If null, JSSE provider default is used. * @return KeyStore(s) created * @throws IOException if the KeyStore could not be read * @throws GeneralSecurityException (subtypes of) for most crypto errors, passphrase errors, etc. */ protected KeyManager[] createKeyManagers(String keyStorePath, char[] keyStorePass, char[] keyPass, String keyStoreType) throws IOException, GeneralSecurityException { //Load the Key Managers if (keyStoreType == null) { keyStoreType = KeyStore.getDefaultType(); } if (keyPass == null) { keyPass = keyStorePass; } KeyStore ks = KeyStore.getInstance(keyStoreType); FileInputStream fInKeyStore = new FileInputStream(keyStorePath); try { ks.load(fInKeyStore, keyStorePass); } finally { try { fInKeyStore.close(); } catch (IOException ex) { // Ignored } } KeyManagerFactory managerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); managerFactory.init(ks, keyPass); return managerFactory.getKeyManagers(); } /** * Create and return TrustManager(s) using the passed information. * * If trustStoreType is null, the JSSE default keystore type is used. * No other arguments may be null. * * If you want to use the Java default truststore, do not call this method * at all. Just pass a null TrustManager[] as the trustManagers argument to * buildSSLSocketFactory(...). * * @param trustStorePath Location of trust store on file system. Non-null. * @param trustStorePass Password to decrypt truststore. * @param trustStoreType JSSE TrustStore type. If null, default JSSE keystore type is used. * @return TrustStore(s) created * @throws IOException If the TrustStore cannot be read * @throws GeneralSecurityException (subtypes of) for most crypto errors, password errors, etc. */ protected TrustManager[] createTrustManagers(String trustStorePath, char[] trustStorePass, String trustStoreType) throws IOException, GeneralSecurityException { // Load the trust store if (trustStoreType == null) { trustStoreType = KeyStore.getDefaultType(); } KeyStore trustKs = KeyStore.getInstance(trustStoreType); FileInputStream fInTrustStore = new FileInputStream(trustStorePath); try { trustKs.load(fInTrustStore, trustStorePass); } finally { try { fInTrustStore.close(); } catch (IOException ex) { // Ignored } } TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustFactory.init(trustKs); return trustFactory.getTrustManagers(); } /** * If you wish to override SSL protocol selection, you may do so by * overriding this method. The defualt simply returns * AbstractCertAuthFactory.DEFAULT_SSL_PROTOCOL_NAME . * . * @return JSSE ssl/tls protocol name AbstractCertAuthFactory.DEFAULT_SSL_PROTOCOL_NAME */ protected String getSSLProtocolName() { return DEFAULT_SSL_PROTOCOL_NAME; } }