001    /*
002     * Copyright 2006 Stephen McConnell.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package net.dpml.http;
017    
018    import java.io.InputStream;
019    import java.net.URI;
020    import java.net.URL;
021    import java.security.SecureRandom;
022    import java.security.KeyStore;
023    import java.security.NoSuchAlgorithmException;
024    import java.security.NoSuchProviderException;
025    
026    import javax.net.ssl.KeyManager;
027    import javax.net.ssl.KeyManagerFactory;
028    import javax.net.ssl.TrustManager;
029    import javax.net.ssl.TrustManagerFactory;
030    import javax.net.ssl.SSLContext;
031    import javax.net.ssl.SSLServerSocketFactory;
032    
033    import net.dpml.transit.Artifact;
034    
035    import org.mortbay.resource.Resource;
036    import org.mortbay.jetty.security.Password;
037    
038    /**
039     * SSL socket connector.
040     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
041     * @version 0.0.4
042     */
043    public class SslSocketConnector extends org.mortbay.jetty.security.SslSocketConnector
044    {
045        private static final int HEADER_BUFFER_SIZE = 4*1024;
046        private static final int REQUEST_BUFFER_SIZE = 8*1024;
047        private static final int RESPONSE_BUFFER_SIZE = 32*1024;
048        private static final int MAXIMUM_IDLE_TIME = 30000;
049        private static final int ACCEPT_QUEUE_SIZE = 0;
050        private static final int ACCEPTORS = 1;
051        private static final int SO_LINGER_TIME = 1000;
052        private static final int CONFIDENTIAL_PORT = 0;
053        private static final int INTEGRAL_PORT = 0;
054        private static final boolean ASSUME_SHORT_DISPATCH = false;
055        private static final String KEYSTORE_TYPE = "JKS";
056        private static final String PROTOCOL = "TLS";
057        private static final String ALGORITHM = "SunX509";
058        
059        private transient Context m_context;
060        private transient Password m_certificatePassword;
061        private transient Password m_keystorePassword;
062        private transient Password m_trustPassword;
063        
064       /**
065        * SSL connector context definition.
066        */
067        public interface Context extends ConnectorContext
068        {
069           /**
070            * Set the cipher suites.
071            * @param suites the default suites argument
072            * @return the cipher suites
073            */
074            //String[] getCipherSuites( String[] suites );
075    
076           /**
077            * Return the keystore password.
078            * @param password implementation defined default value
079            * @return the supplied value unless overriden in the deployment configuration
080            */
081            String getKeyStorePassword( String password );
082            
083           /**
084            * Return the certificate password.
085            * @param password implementation defined default value
086            * @return the supplied value unless overriden in the deployment configuration
087            */
088            String getCertificatePassword( String password );
089            
090           /**
091            * Return the keystore algorithm.
092            * @param algorithm implementation defined default value
093            * @return the supplied value unless overriden in the deployment configuration
094            */
095            String getSecureRandomAlgorithm( String algorithm );
096            
097           /**
098            * Return the keystore type.
099            * @param type implementation defined default value
100            * @return the supplied value unless overriden in the deployment configuration
101            */
102            String getKeyStoreType( String type );
103            
104           /**
105            * Return the SSL protocol.
106            * @param protocol implementation defined default value
107            * @return the supplied value unless overriden in the deployment configuration
108            */
109            String getProtocol( String protocol );
110            
111           /**
112            * Return the keystore location uri.
113            * @param keystore implementation defined default value
114            * @return the supplied value unless overriden in the deployment configuration
115            */
116            URI getKeyStore( URI keystore );
117            
118           /**
119            * Return the 'want-client-authentication' policy.
120            * @param flag implementation defined default value
121            * @return the supplied value unless overriden in the deployment configuration
122            */
123            boolean getWantClientAuth( boolean flag );
124            
125           /**
126            * Return the 'need-client-authentication' policy.
127            * @param flag implementation defined default value
128            * @return the supplied value unless overriden in the deployment configuration
129            */
130            boolean getNeedClientAuth( boolean flag );
131            
132           /**
133            * Return the SSL context provider.
134            * @param provider implementation defined default value
135            * @return the supplied value unless overriden in the deployment configuration
136            */
137            String getProvider( String provider );
138            
139            // extras
140            
141           /**
142            * Return the keystore algorithm.
143            * @param algorithm implementation defined default value
144            * @return the supplied value unless overriden in the deployment configuration
145            */
146            String getTrustAlgorithm( String algorithm );
147            
148           /**
149            * Return the keystore location uri.
150            * @param uri implementation defined default value
151            * @return the supplied value unless overriden in the deployment configuration
152            */
153            URI getTrustStore( URI uri );
154            
155           /**
156            * Return the keystore type.
157            * @param type implementation defined default value
158            * @return the supplied value unless overriden in the deployment configuration
159            */
160            String getTrustStoreType( String type );
161            
162           /**
163            * Return the trust store password.
164            * @param password implementation defined default value
165            * @return the supplied value unless overriden in the deployment configuration
166            */
167            String getTrustStorePassword( String password );
168        }
169        
170       /**
171        * Creation of a new ssl connector.
172        * @param context the deployment context
173        * @exception Exception if an instantiation error occurs
174        */
175        public SslSocketConnector( Context context ) throws Exception
176        {
177            super();
178            
179            m_context = context;
180            
181            String host = context.getHost( null );
182            if( null != host )
183            {
184                setHost( host );
185            }
186        
187            int port = context.getPort();
188            setPort( port );
189            
190            int headerBufferSize = context.getHeaderBufferSize( HEADER_BUFFER_SIZE );
191            setHeaderBufferSize( headerBufferSize );
192            
193            int requestBufferSize = context.getRequestBufferSize( REQUEST_BUFFER_SIZE );
194            setRequestBufferSize( requestBufferSize );
195            
196            int responseBufferSize = context.getResponseBufferSize( RESPONSE_BUFFER_SIZE );
197            setResponseBufferSize( responseBufferSize );
198            
199            int maxIdle = context.getMaxIdleTime( MAXIMUM_IDLE_TIME );
200            setMaxIdleTime( maxIdle );
201            
202            int queueSize = context.getAcceptQueueSize( ACCEPT_QUEUE_SIZE );
203            setAcceptQueueSize( queueSize );
204            
205            int acceptCount = context.getAcceptors( ACCEPTORS );
206            setAcceptors( acceptCount );
207            
208            int linger = context.getSoLingerTime( SO_LINGER_TIME );
209            setSoLingerTime( linger );
210            
211            int confidentialPort = context.getConfidentialPort( CONFIDENTIAL_PORT );
212            setConfidentialPort( confidentialPort );
213            
214            Scheme confidentialScheme = Scheme.parse( context.getConfidentialScheme( "https" ) );
215            setConfidentialScheme( confidentialScheme.getName() );
216            
217            int integralPort = context.getIntegralPort( INTEGRAL_PORT );
218            setIntegralPort( integralPort );
219            
220            Scheme integralScheme = Scheme.parse( context.getIntegralScheme( "https" ) );
221            setIntegralScheme( integralScheme.getName() );
222            
223            // SslSocketConnector$Context
224            
225            String certificatePassword = context.getCertificatePassword( null );
226            if( null != certificatePassword )
227            {
228                m_certificatePassword = 
229                  Password.getPassword( KEYPASSWORD_PROPERTY, certificatePassword, null );
230                setKeyPassword( certificatePassword );
231            }
232            
233            String keystorePassword = context.getKeyStorePassword( null );
234            if( null != keystorePassword )
235            {
236                m_keystorePassword = Password.getPassword( PASSWORD_PROPERTY, keystorePassword, null );
237                setPassword( keystorePassword );
238            }
239            
240            String algorithm = context.getSecureRandomAlgorithm( ALGORITHM );
241            setSecureRandomAlgorithm( algorithm );
242            
243            String protocol = context.getProtocol( PROTOCOL );
244            setProtocol( protocol );
245            
246            URI keystore = context.getKeyStore( null );
247            if( null != keystore )
248            {
249                String keystorePath = keystore.toASCIIString();
250                setKeystore( keystorePath );
251            }
252            
253            String provider = context.getProvider( null );
254            if( null != provider )
255            {
256                setProvider( provider );
257            }
258            
259            String keystoreType = context.getKeyStoreType( KEYSTORE_TYPE );
260            setKeystoreType( keystoreType );
261            
262            boolean wantClientAuth = context.getWantClientAuth( false );
263            setWantClientAuth( wantClientAuth );
264            
265            boolean needClientAuth = context.getNeedClientAuth( false );
266            setNeedClientAuth( needClientAuth );
267            
268            //String[] suites = context.getCipherSuites( (String[]) null );
269            //if( null != suites )
270            //{
271            //    setCipherSuites( suites );
272            //}
273        }
274    
275       /**
276        * Create a new SSLServerSocketFactory.
277        * @return the factory
278        * @exception Exception if an error occurs during factory creation
279        */
280        protected SSLServerSocketFactory createFactory() 
281            throws Exception
282        {
283            final SSLContext context = getSSLContext();
284            KeyManager[] keyManagers = getKeyManagers();
285            TrustManager[] trustManagers = getTrustManagers();
286            SecureRandom random = new SecureRandom();
287            context.init( keyManagers, trustManagers, random );
288            return context.getServerSocketFactory();
289        }
290        
291        private KeyManager[] getKeyManagers() throws Exception
292        {
293            final String algorithm = getSecureRandomAlgorithm();
294            final KeyManagerFactory factory = KeyManagerFactory.getInstance( algorithm );
295            final KeyStore store = loadKeyStore();
296            final char[] password = toCharArray( m_certificatePassword );
297            factory.init( store, password );
298            return factory.getKeyManagers();
299        }
300        
301        private KeyStore loadKeyStore() throws Exception
302        {
303            final String type = getKeystoreType();
304            final KeyStore keyStore = KeyStore.getInstance( type );
305            final char[] password = toCharArray( m_keystorePassword );
306            final String keyStorePath = getKeystore();
307            final InputStream input = Resource.newResource( keyStorePath ).getInputStream();
308            keyStore.load( input, password );
309            return keyStore;
310        }
311        
312        private KeyStore loadTrustStore() throws Exception
313        {
314            final String type = m_context.getTrustStoreType( KEYSTORE_TYPE );
315            final KeyStore store = KeyStore.getInstance( type );
316            final char[] password = toCharArray( m_trustPassword );
317            URI uri = m_context.getTrustStore( null );
318            if( null != uri )
319            {
320                URL url = Artifact.toURL( uri );
321                final InputStream input = Resource.newResource( url ).getInputStream();
322                store.load( input, password );
323                return store;
324            }
325            else
326            {
327                return null;
328            }
329        }
330        
331        private TrustManager[] getTrustManagers() throws Exception
332        {
333            final String algorithm = m_context.getTrustAlgorithm( ALGORITHM );
334            final TrustManagerFactory factory = TrustManagerFactory.getInstance( algorithm );
335            final KeyStore store = loadTrustStore();
336            if( store != null )
337            {
338                factory.init( store );
339                return factory.getTrustManagers();
340            }
341            else
342            {
343                return new TrustManager[0];
344            }
345        }
346    
347        private char[] toCharArray( Password value )
348        {
349            if( null == value )
350            {
351                return null;
352            }
353            else
354            {
355                return value.toString().toCharArray();
356            }
357        }
358        
359        private SSLContext getSSLContext() throws NoSuchAlgorithmException, NoSuchProviderException
360        {
361            final String protocol = getProtocol();
362            final String provider = getProvider();
363            if( null == provider )
364            {
365                return SSLContext.getInstance( protocol );
366            }
367            else
368            {
369                return SSLContext.getInstance( protocol, provider );
370            }
371        }
372    }
373