| // ======================================================================== |
| // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| |
| package org.eclipse.jetty.exssl; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.nio.channels.SelectionKey; |
| import java.nio.channels.SocketChannel; |
| import java.security.KeyStore; |
| import java.security.SecureRandom; |
| import java.security.Security; |
| import java.security.cert.CRL; |
| import java.security.cert.CertStore; |
| import java.security.cert.Certificate; |
| import java.security.cert.CertificateFactory; |
| import java.security.cert.CollectionCertStoreParameters; |
| import java.security.cert.PKIXBuilderParameters; |
| import java.security.cert.X509CertSelector; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import javax.net.ssl.CertPathTrustManagerParameters; |
| import javax.net.ssl.KeyManager; |
| import javax.net.ssl.KeyManagerFactory; |
| import javax.net.ssl.SSLContext; |
| import javax.net.ssl.SSLEngine; |
| import javax.net.ssl.SSLSession; |
| import javax.net.ssl.SSLSocket; |
| import javax.net.ssl.TrustManager; |
| import javax.net.ssl.TrustManagerFactory; |
| import javax.net.ssl.X509KeyManager; |
| |
| import org.eclipse.jetty.http.HttpParser; |
| import org.eclipse.jetty.http.HttpSchemes; |
| import org.eclipse.jetty.http.security.Password; |
| import org.eclipse.jetty.io.Buffer; |
| import org.eclipse.jetty.io.Buffers; |
| import org.eclipse.jetty.io.Connection; |
| import org.eclipse.jetty.io.EndPoint; |
| import org.eclipse.jetty.io.ThreadLocalBuffers; |
| import org.eclipse.jetty.io.bio.SocketEndPoint; |
| import org.eclipse.jetty.io.nio.DirectNIOBuffer; |
| import org.eclipse.jetty.io.nio.IndirectNIOBuffer; |
| import org.eclipse.jetty.io.nio.SelectChannelEndPoint; |
| import org.eclipse.jetty.io.nio.SelectorManager.SelectSet; |
| import org.eclipse.jetty.io.nio.SslSelectChannelEndPoint; |
| import org.eclipse.jetty.server.HttpConnection; |
| import org.eclipse.jetty.server.Request; |
| import org.eclipse.jetty.server.nio.SelectChannelConnector; |
| import org.eclipse.jetty.server.ssl.SslCertificates; |
| import org.eclipse.jetty.util.log.Log; |
| import org.eclipse.jetty.util.resource.Resource; |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * SslSelectChannelConnector. |
| * |
| * @org.apache.xbean.XBean element="sslConnector" description="Creates an NIO ssl connector" |
| * |
| * |
| * |
| */ |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| */ |
| public class EnhancedSslSelectChannelConnector extends SelectChannelConnector implements EnhancedSslConnector |
| { |
| /** SSL buffers */ |
| private Buffers _sslBuffers; |
| |
| /* ------------------------------------------------------------ */ |
| public EnhancedSslSelectChannelConnector() |
| { |
| setUseDirectBuffers(false); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return SSL buffers |
| */ |
| public Buffers getSslBuffers() |
| { |
| return _sslBuffers; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * By default, we're confidential, given we speak SSL. But, if we've been |
| * told about an confidential port, and said port is not our port, then |
| * we're not. This allows separation of listeners providing INTEGRAL versus |
| * CONFIDENTIAL constraints, such as one SSL listener configured to require |
| * client certs providing CONFIDENTIAL, whereas another SSL listener not |
| * requiring client certs providing mere INTEGRAL constraints. |
| */ |
| @Override |
| public boolean isConfidential(Request request) |
| { |
| final int confidentialPort=getConfidentialPort(); |
| return confidentialPort==0||confidentialPort==request.getServerPort(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * By default, we're integral, given we speak SSL. But, if we've been told |
| * about an integral port, and said port is not our port, then we're not. |
| * This allows separation of listeners providing INTEGRAL versus |
| * CONFIDENTIAL constraints, such as one SSL listener configured to require |
| * client certs providing CONFIDENTIAL, whereas another SSL listener not |
| * requiring client certs providing mere INTEGRAL constraints. |
| */ |
| @Override |
| public boolean isIntegral(Request request) |
| { |
| final int integralPort=getIntegralPort(); |
| return integralPort==0||integralPort==request.getServerPort(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * Allow the Listener a chance to customise the request. before the server |
| * does its stuff. <br> |
| * This allows the required attributes to be set for SSL requests. <br> |
| * The requirements of the Servlet specs are: |
| * <ul> |
| * <li> an attribute named "javax.servlet.request.ssl_session_id" of type |
| * String (since Servlet Spec 3.0).</li> |
| * <li> an attribute named "javax.servlet.request.cipher_suite" of type |
| * String.</li> |
| * <li> an attribute named "javax.servlet.request.key_size" of type Integer.</li> |
| * <li> an attribute named "javax.servlet.request.X509Certificate" of type |
| * java.security.cert.X509Certificate[]. This is an array of objects of type |
| * X509Certificate, the order of this array is defined as being in ascending |
| * order of trust. The first certificate in the chain is the one set by the |
| * client, the next is the one used to authenticate the first, and so on. |
| * </li> |
| * </ul> |
| * |
| * @param endpoint |
| * The Socket the request arrived on. This should be a |
| * {@link SocketEndPoint} wrapping a {@link SSLSocket}. |
| * @param request |
| * HttpRequest to be customised. |
| */ |
| @Override |
| public void customize(EndPoint endpoint, Request request) throws IOException |
| { |
| request.setScheme(HttpSchemes.HTTPS); |
| super.customize(endpoint,request); |
| |
| SslSelectChannelEndPoint sslHttpChannelEndpoint=(SslSelectChannelEndPoint)endpoint; |
| SSLEngine sslEngine=sslHttpChannelEndpoint.getSSLEngine(); |
| SSLSession sslSession=sslEngine.getSession(); |
| |
| SslCertificates.customize(sslSession,endpoint,request); |
| } |
| |
| /* ------------------------------------------------------------------------------- */ |
| @Override |
| protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException |
| { |
| SslSelectChannelEndPoint endp = new SslSelectChannelEndPoint(_sslBuffers,channel,selectSet,key,createSSLEngine(), EnhancedSslSelectChannelConnector.this._maxIdleTime); |
| endp.setAllowRenegotiate(_allowRenegotiate); |
| return endp; |
| } |
| |
| /* ------------------------------------------------------------------------------- */ |
| @Override |
| protected Connection newConnection(SocketChannel channel, SelectChannelEndPoint endpoint) |
| { |
| HttpConnection connection=(HttpConnection)super.newConnection(channel,endpoint); |
| ((HttpParser)connection.getParser()).setForceContentBuffer(true); |
| return connection; |
| } |
| |
| @Override |
| protected void doStart() throws Exception |
| { |
| if (_context == null) |
| _context=createSSLContext(); |
| |
| SSLEngine engine=createSSLEngine(); |
| SSLSession ssl_session=engine.getSession(); |
| |
| ThreadLocalBuffers buffers = new ThreadLocalBuffers() |
| { |
| @Override |
| protected Buffer newBuffer(int size) |
| { |
| if (getUseDirectBuffers()) |
| return new DirectNIOBuffer(size); |
| return new IndirectNIOBuffer(size); |
| } |
| @Override |
| protected Buffer newHeader(int size) |
| { |
| if (getUseDirectBuffers()) |
| return new DirectNIOBuffer(size); |
| return new IndirectNIOBuffer(size); |
| } |
| @Override |
| protected boolean isHeader(Buffer buffer) |
| { |
| return true; |
| } |
| }; |
| buffers.setBufferSize(ssl_session.getApplicationBufferSize()); |
| buffers.setHeaderSize(ssl_session.getApplicationBufferSize()); |
| _sslBuffers=buffers; |
| |
| if (getRequestHeaderSize()<ssl_session.getApplicationBufferSize()) |
| setRequestHeaderSize(ssl_session.getApplicationBufferSize()); |
| if (getRequestBufferSize()<ssl_session.getApplicationBufferSize()) |
| setRequestBufferSize(ssl_session.getApplicationBufferSize()); |
| |
| super.doStart(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| protected SSLEngine createSSLEngine() throws IOException |
| { |
| SSLEngine engine = null; |
| try |
| { |
| engine = _context.createSSLEngine(); |
| engine.setUseClientMode(false); |
| |
| if (_wantClientAuth) |
| engine.setWantClientAuth(_wantClientAuth); |
| if (_needClientAuth) |
| engine.setNeedClientAuth(_needClientAuth); |
| |
| if ((_excludeCipherSuites != null && _excludeCipherSuites.length > 0) || (_includeCipherSuites != null && _includeCipherSuites.length > 0)) |
| { |
| List<String> includedCSList; |
| if (_includeCipherSuites != null) |
| { |
| includedCSList = Arrays.asList(_includeCipherSuites); |
| } |
| else |
| { |
| includedCSList = new ArrayList<String>(); |
| } |
| List<String> excludedCSList; |
| if (_excludeCipherSuites != null) |
| { |
| excludedCSList = Arrays.asList(_excludeCipherSuites); |
| } |
| else |
| { |
| excludedCSList = new ArrayList<String>(); |
| } |
| String[] enabledCipherSuites = engine.getEnabledCipherSuites(); |
| List<String> enabledCSList = new ArrayList<String>(Arrays.asList(enabledCipherSuites)); |
| |
| String[] supportedCipherSuites = engine.getSupportedCipherSuites(); |
| List<String> supportedCSList = Arrays.asList(supportedCipherSuites); |
| |
| for (String cipherName : includedCSList) |
| { |
| if ((!enabledCSList.contains(cipherName)) && supportedCSList.contains(cipherName)) |
| { |
| enabledCSList.add(cipherName); |
| } |
| } |
| |
| for (String cipherName : excludedCSList) |
| { |
| if (enabledCSList.contains(cipherName)) |
| { |
| enabledCSList.remove(cipherName); |
| } |
| } |
| enabledCipherSuites = enabledCSList.toArray(new String[0]); |
| |
| engine.setEnabledCipherSuites(enabledCipherSuites); |
| } |
| } |
| catch (Exception e) |
| { |
| Log.warn("Error creating sslEngine -- closing this connector",e); |
| close(); |
| throw new IllegalStateException(e); |
| } |
| return engine; |
| } |
| } |