package org.eclipse.jetty.server.ssl; import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.cert.X509Certificate; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import org.eclipse.jetty.http.HttpSchemes; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.bio.SocketEndPoint; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; public class SslCertificates { private static final Logger LOG = Log.getLogger(SslCertificates.class); /** * The name of the SSLSession attribute that will contain any cached information. */ static final String CACHED_INFO_ATTR = CachedInfo.class.getName(); public 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. */ public static void customize(SSLSession sslSession, EndPoint endpoint, Request request) throws IOException { request.setScheme(HttpSchemes.HTTPS); try { 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=SslCertificates.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); } } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /** * Simple bundle of information that is cached in the SSLSession. Stores the * effective keySize and the client certificate chain. */ private static class CachedInfo { private final X509Certificate[] _certs; private final Integer _keySize; private final 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; } } }