diff options
author | Greg Wilkins | 2009-03-24 21:07:27 +0000 |
---|---|---|
committer | Greg Wilkins | 2009-03-24 21:07:27 +0000 |
commit | da627b843fe81fa0fe52a046c1be8595630e9ae7 (patch) | |
tree | 5dd3804b874cf01be38575a02b5658a02113f78f /jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java | |
parent | bc1e0bd10201d8a14f20a81e3b93076af6408fe4 (diff) | |
download | org.eclipse.jetty.project-da627b843fe81fa0fe52a046c1be8595630e9ae7.tar.gz org.eclipse.jetty.project-da627b843fe81fa0fe52a046c1be8595630e9ae7.tar.xz org.eclipse.jetty.project-da627b843fe81fa0fe52a046c1be8595630e9ae7.zip |
jetty @ eclipse initial commit
git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@8 7e9141cc-0065-0410-87d8-b60c137991c4
Diffstat (limited to 'jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java')
-rw-r--r-- | jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java | 703 |
1 files changed, 703 insertions, 0 deletions
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java new file mode 100644 index 0000000000..4169cc3b65 --- /dev/null +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java @@ -0,0 +1,703 @@ +// ======================================================================== +// 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.server.ssl; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +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.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; + +import org.eclipse.jetty.http.HttpParser; +import org.eclipse.jetty.http.HttpSchemes; +import org.eclipse.jetty.http.security.Password; +import org.eclipse.jetty.http.ssl.SslSelectChannelEndPoint; +import org.eclipse.jetty.io.Buffer; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.EndPoint; +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.NIOBuffer; +import org.eclipse.jetty.io.nio.SelectChannelEndPoint; +import org.eclipse.jetty.io.nio.SelectorManager.SelectSet; +import org.eclipse.jetty.server.HttpConnection; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.eclipse.jetty.util.TypeUtil; +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 SslSelectChannelConnector extends SelectChannelConnector +{ + /** + * The name of the SSLSession attribute that will contain any cached + * information. + */ + static final String CACHED_INFO_ATTR=CachedInfo.class.getName(); + + /** Default value for the keystore location path. */ + public static final String DEFAULT_KEYSTORE=System.getProperty("user.home")+File.separator+".keystore"; + + /** String name of key password property. */ + public static final String KEYPASSWORD_PROPERTY="jetty.ssl.keypassword"; + + /** String name of keystore password property. */ + public static final String PASSWORD_PROPERTY="jetty.ssl.password"; + + /** Default value for the cipher Suites. */ + private String _excludeCipherSuites[]=null; + + /** Default value for the keystore location path. */ + private String _keystore=DEFAULT_KEYSTORE; + private String _keystoreType="JKS"; // type of the key store + + /** Set to true if we require client certificate authentication. */ + private boolean _needClientAuth=false; + private boolean _wantClientAuth=false; + + private transient Password _password; + private transient Password _keyPassword; + private transient Password _trustPassword; + private String _protocol="TLS"; + private String _algorithm="SunX509"; // cert algorithm + private String _provider; + private String _secureRandomAlgorithm; // cert algorithm + private String _sslKeyManagerFactoryAlgorithm=(Security.getProperty("ssl.KeyManagerFactory.algorithm")==null?"SunX509":Security + .getProperty("ssl.KeyManagerFactory.algorithm")); // cert + // algorithm + + private String _sslTrustManagerFactoryAlgorithm=(Security.getProperty("ssl.TrustManagerFactory.algorithm")==null?"SunX509":Security + .getProperty("ssl.TrustManagerFactory.algorithm")); // cert + // algorithm + + private String _truststore; + private String _truststoreType="JKS"; // type of the key store + private SSLContext _context; + + private int _packetBufferSize; + private int _applicationBufferSize; + private ConcurrentLinkedQueue<Buffer> _packetBuffers = new ConcurrentLinkedQueue<Buffer>(); + private ConcurrentLinkedQueue<Buffer> _applicationBuffers = new ConcurrentLinkedQueue<Buffer>(); + + /* ------------------------------------------------------------ */ + /* (non-Javadoc) + * @see org.eclipse.jetty.io.AbstractBuffers#getBuffer(int) + */ + public Buffer getBuffer(int size) + { + // TODO why is this reimplemented? + Buffer buffer; + if (size==_applicationBufferSize) + { + buffer = _applicationBuffers.poll(); + if (buffer==null) + buffer=new IndirectNIOBuffer(size); + } + else if (size==_packetBufferSize) + { + buffer = _packetBuffers.poll(); + if (buffer==null) + buffer=getUseDirectBuffers() + ?(NIOBuffer)new DirectNIOBuffer(size) + :(NIOBuffer)new IndirectNIOBuffer(size); + } + else + buffer=super.getBuffer(size); + + return buffer; + } + + /* ------------------------------------------------------------ */ + /* (non-Javadoc) + * @see org.eclipse.jetty.io.AbstractBuffers#returnBuffer(org.eclipse.io.Buffer) + */ + public void returnBuffer(Buffer buffer) + { + buffer.clear(); + int size=buffer.capacity(); + ByteBuffer bbuf = ((NIOBuffer)buffer).getByteBuffer(); + bbuf.position(0); + bbuf.limit(size); + + if (size==_applicationBufferSize) + _applicationBuffers.add(buffer); + else if (size==_packetBufferSize) + _packetBuffers.add(buffer); + else + super.returnBuffer(buffer); + } + + + + /** + * Return the chain of X509 certificates used to negotiate the SSL Session. + * <p> + * Note: in order to do this we must convert a + * javax.security.cert.X509Certificate[], as used by JSSE to a + * java.security.cert.X509Certificate[],as required by the Servlet specs. + * + * @param sslSession + * the javax.net.ssl.SSLSession to use as the source of the + * cert chain. + * @return the chain of java.security.cert.X509Certificates used to + * negotiate the SSL connection. <br> + * Will be null if the chain is missing or empty. + */ + private static X509Certificate[] getCertChain(SSLSession sslSession) + { + try + { + javax.security.cert.X509Certificate javaxCerts[]=sslSession.getPeerCertificateChain(); + if (javaxCerts==null||javaxCerts.length==0) + return null; + + int length=javaxCerts.length; + X509Certificate[] javaCerts=new X509Certificate[length]; + + java.security.cert.CertificateFactory cf=java.security.cert.CertificateFactory.getInstance("X.509"); + for (int i=0; i<length; i++) + { + byte bytes[]=javaxCerts[i].getEncoded(); + ByteArrayInputStream stream=new ByteArrayInputStream(bytes); + javaCerts[i]=(X509Certificate)cf.generateCertificate(stream); + } + + return javaCerts; + } + catch (SSLPeerUnverifiedException pue) + { + return null; + } + catch (Exception e) + { + Log.warn(Log.EXCEPTION,e); + return null; + } + } + + + /* ------------------------------------------------------------ */ + /** + * 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 + { + super.customize(endpoint,request); + request.setScheme(HttpSchemes.HTTPS); + + SslSelectChannelEndPoint sslHttpChannelEndpoint=(SslSelectChannelEndPoint)endpoint; + + SSLEngine sslEngine=sslHttpChannelEndpoint.getSSLEngine(); + + try + { + SSLSession sslSession=sslEngine.getSession(); + String cipherSuite=sslSession.getCipherSuite(); + Integer keySize; + X509Certificate[] certs; + String idStr; + + CachedInfo cachedInfo=(CachedInfo)sslSession.getValue(CACHED_INFO_ATTR); + if (cachedInfo!=null) + { + keySize=cachedInfo.getKeySize(); + certs=cachedInfo.getCerts(); + idStr=cachedInfo.getIdStr(); + } + else + { + keySize=new Integer(ServletSSL.deduceKeyLength(cipherSuite)); + certs=getCertChain(sslSession); + byte[] bytes = sslSession.getId(); + idStr = TypeUtil.toHexString(bytes); + cachedInfo=new CachedInfo(keySize,certs,idStr); + sslSession.putValue(CACHED_INFO_ATTR,cachedInfo); + } + + if (certs!=null) + request.setAttribute("javax.servlet.request.X509Certificate",certs); + + request.setAttribute("javax.servlet.request.cipher_suite",cipherSuite); + request.setAttribute("javax.servlet.request.key_size",keySize); + request.setAttribute("javax.servlet.request.ssl_session_id", idStr); + } + catch (Exception e) + { + Log.warn(Log.EXCEPTION,e); + } + } + + /* ------------------------------------------------------------ */ + public SslSelectChannelConnector() + { + } + + /** + * + * @deprecated As of Java Servlet API 2.0, with no replacement. + * + */ + public String[] getCipherSuites() + { + return getExcludeCipherSuites(); + } + + public String[] getExcludeCipherSuites() + { + return _excludeCipherSuites; + } + + /** + * + * @deprecated As of Java Servlet API 2.0, with no replacement. + * + * + */ + public void setCipherSuites(String[] cipherSuites) + { + setExcludeCipherSuites(cipherSuites); + } + + public void setExcludeCipherSuites(String[] cipherSuites) + { + this._excludeCipherSuites=cipherSuites; + } + + /* ------------------------------------------------------------ */ + public void setPassword(String password) + { + _password=Password.getPassword(PASSWORD_PROPERTY,password,null); + } + + /* ------------------------------------------------------------ */ + public void setTrustPassword(String password) + { + _trustPassword=Password.getPassword(PASSWORD_PROPERTY,password,null); + } + + /* ------------------------------------------------------------ */ + public void setKeyPassword(String password) + { + _keyPassword=Password.getPassword(KEYPASSWORD_PROPERTY,password,null); + } + + /* ------------------------------------------------------------ */ + public String getAlgorithm() + { + return (this._algorithm); + } + + /* ------------------------------------------------------------ */ + public void setAlgorithm(String algorithm) + { + this._algorithm=algorithm; + } + + /* ------------------------------------------------------------ */ + public String getProtocol() + { + return _protocol; + } + + /* ------------------------------------------------------------ */ + public void setProtocol(String protocol) + { + _protocol=protocol; + } + + /* ------------------------------------------------------------ */ + public void setKeystore(String keystore) + { + _keystore=keystore; + } + + /* ------------------------------------------------------------ */ + public String getKeystore() + { + return _keystore; + } + + /* ------------------------------------------------------------ */ + public String getKeystoreType() + { + return (_keystoreType); + } + + /* ------------------------------------------------------------ */ + public boolean getNeedClientAuth() + { + return _needClientAuth; + } + + /* ------------------------------------------------------------ */ + public boolean getWantClientAuth() + { + return _wantClientAuth; + } + + /* ------------------------------------------------------------ */ + /** + * Set the value of the needClientAuth property + * + * @param needClientAuth + * true iff we require client certificate authentication. + */ + public void setNeedClientAuth(boolean needClientAuth) + { + _needClientAuth=needClientAuth; + } + + public void setWantClientAuth(boolean wantClientAuth) + { + _wantClientAuth=wantClientAuth; + } + + /* ------------------------------------------------------------ */ + public void setKeystoreType(String keystoreType) + { + _keystoreType=keystoreType; + } + + /* ------------------------------------------------------------ */ + public String getProvider() + { + return _provider; + } + + public String getSecureRandomAlgorithm() + { + return (this._secureRandomAlgorithm); + } + + /* ------------------------------------------------------------ */ + public String getSslKeyManagerFactoryAlgorithm() + { + return (this._sslKeyManagerFactoryAlgorithm); + } + + /* ------------------------------------------------------------ */ + public String getSslTrustManagerFactoryAlgorithm() + { + return (this._sslTrustManagerFactoryAlgorithm); + } + + /* ------------------------------------------------------------ */ + public String getTruststore() + { + return _truststore; + } + + /* ------------------------------------------------------------ */ + public String getTruststoreType() + { + return _truststoreType; + } + + /* ------------------------------------------------------------ */ + public void setProvider(String _provider) + { + this._provider=_provider; + } + + /* ------------------------------------------------------------ */ + public void setSecureRandomAlgorithm(String algorithm) + { + this._secureRandomAlgorithm=algorithm; + } + + /* ------------------------------------------------------------ */ + public void setSslKeyManagerFactoryAlgorithm(String algorithm) + { + this._sslKeyManagerFactoryAlgorithm=algorithm; + } + + /* ------------------------------------------------------------ */ + public void setSslTrustManagerFactoryAlgorithm(String algorithm) + { + this._sslTrustManagerFactoryAlgorithm=algorithm; + } + + public void setTruststore(String truststore) + { + _truststore=truststore; + } + + public void setTruststoreType(String truststoreType) + { + _truststoreType=truststoreType; + } + + public void setSslContext(SSLContext sslContext) { + this._context = sslContext; + } + + /* ------------------------------------------------------------ */ + /** + * 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. + */ + 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. + */ + public boolean isIntegral(Request request) + { + final int integralPort=getIntegralPort(); + return integralPort==0||integralPort==request.getServerPort(); + } + + /* ------------------------------------------------------------------------------- */ + protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException + { + return new SslSelectChannelEndPoint(this,channel,selectSet,key,createSSLEngine()) + { + // TODO remove this hack + public boolean isReadyForDispatch() + { + Request request = ((HttpConnection)getConnection()).getRequest(); + return super.isReadyForDispatch() && !(request.getAsyncRequest().isSuspended()); + } + }; + } + + /* ------------------------------------------------------------------------------- */ + protected Connection newConnection(SocketChannel channel, SelectChannelEndPoint endpoint) + { + HttpConnection connection=(HttpConnection)super.newConnection(channel,endpoint); + ((HttpParser)connection.getParser()).setForceContentBuffer(true); + return connection; + } + + /* ------------------------------------------------------------ */ + 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) + { + List<String> excludedCSList=Arrays.asList(_excludeCipherSuites); + String[] enabledCipherSuites=engine.getEnabledCipherSuites(); + List<String> enabledCSList=new ArrayList<String>(Arrays.asList(enabledCipherSuites)); + + for (String cipherName : excludedCSList) + { + if (enabledCSList.contains(cipherName)) + { + enabledCSList.remove(cipherName); + } + } + enabledCipherSuites=enabledCSList.toArray(new String[enabledCSList.size()]); + + engine.setEnabledCipherSuites(enabledCipherSuites); + } + + } + catch (Exception e) + { + Log.warn("Error creating sslEngine -- closing this connector",e); + close(); + throw new IllegalStateException(e); + } + return engine; + } + + + protected void doStart() throws Exception + { + if (_context == null) { + _context=createSSLContext(); + } + + SSLEngine engine=createSSLEngine(); + SSLSession ssl_session=engine.getSession(); + + setHeaderBufferSize(ssl_session.getApplicationBufferSize()); + setRequestBufferSize(ssl_session.getApplicationBufferSize()); + setResponseBufferSize(ssl_session.getApplicationBufferSize()); + + super.doStart(); + } + + protected SSLContext createSSLContext() throws Exception + { + if (_truststore==null) + { + _truststore=_keystore; + _truststoreType=_keystoreType; + } + + InputStream keystoreInputStream = null; + + KeyManager[] keyManagers=null; + KeyStore keyStore = null; + try + { + if (_keystore!=null) + { + keystoreInputStream=Resource.newResource(_keystore).getInputStream(); + keyStore = KeyStore.getInstance(_keystoreType); + keyStore.load(keystoreInputStream,_password==null?null:_password.toString().toCharArray()); + } + } + finally + { + if (keystoreInputStream != null) + keystoreInputStream.close(); + } + + KeyManagerFactory keyManagerFactory=KeyManagerFactory.getInstance(_sslKeyManagerFactoryAlgorithm); + keyManagerFactory.init(keyStore,_keyPassword==null?(_password==null?null:_password.toString().toCharArray()):_keyPassword.toString().toCharArray()); + keyManagers=keyManagerFactory.getKeyManagers(); + + + TrustManager[] trustManagers=null; + InputStream truststoreInputStream = null; + KeyStore trustStore = null; + try + { + if (_truststore!=null) + { + truststoreInputStream = Resource.newResource(_truststore).getInputStream(); + trustStore=KeyStore.getInstance(_truststoreType); + trustStore.load(truststoreInputStream,_trustPassword==null?null:_trustPassword.toString().toCharArray()); + } + } + finally + { + if (truststoreInputStream != null) + truststoreInputStream.close(); + } + + + TrustManagerFactory trustManagerFactory=TrustManagerFactory.getInstance(_sslTrustManagerFactoryAlgorithm); + trustManagerFactory.init(trustStore); + trustManagers=trustManagerFactory.getTrustManagers(); + + SecureRandom secureRandom=_secureRandomAlgorithm==null?null:SecureRandom.getInstance(_secureRandomAlgorithm); + SSLContext context=_provider==null?SSLContext.getInstance(_protocol):SSLContext.getInstance(_protocol,_provider); + context.init(keyManagers,trustManagers,secureRandom); + return context; + } + + /** + * Simple bundle of information that is cached in the SSLSession. Stores the + * effective keySize and the client certificate chain. + */ + private class CachedInfo + { + private X509Certificate[] _certs; + private Integer _keySize; + private String _idStr; + + CachedInfo(Integer keySize, X509Certificate[] certs,String idStr) + { + this._keySize=keySize; + this._certs=certs; + this._idStr=idStr; + } + + X509Certificate[] getCerts() + { + return _certs; + } + + Integer getKeySize() + { + return _keySize; + } + + String getIdStr() + { + return _idStr; + } + } + +} |