View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.server;
20  
21  import java.security.cert.X509Certificate;
22  
23  import javax.net.ssl.SSLContext;
24  import javax.net.ssl.SSLEngine;
25  import javax.net.ssl.SSLSession;
26  import javax.servlet.ServletRequest;
27  
28  import org.eclipse.jetty.http.BadMessageException;
29  import org.eclipse.jetty.http.HttpScheme;
30  import org.eclipse.jetty.io.ssl.SslConnection;
31  import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint;
32  import org.eclipse.jetty.util.TypeUtil;
33  import org.eclipse.jetty.util.log.Log;
34  import org.eclipse.jetty.util.log.Logger;
35  import org.eclipse.jetty.util.ssl.SslContextFactory;
36  
37  
38  /* ------------------------------------------------------------ */
39  /** Customizer that extracts the attribute from an {@link SSLContext}
40   * and sets them on the request with {@link ServletRequest#setAttribute(String, Object)}
41   * according to Servlet Specification Requirements.
42   */
43  public class SecureRequestCustomizer implements HttpConfiguration.Customizer
44  {
45      private static final Logger LOG = Log.getLogger(SecureRequestCustomizer.class);
46      
47      /**
48       * The name of the SSLSession attribute that will contain any cached information.
49       */
50      public static final String CACHED_INFO_ATTR = CachedInfo.class.getName();
51  
52      private boolean _sniHostCheck;
53      
54      
55      public SecureRequestCustomizer()
56      {
57          this(true);
58      }
59      
60      public SecureRequestCustomizer(boolean sniHostCheck)
61      {
62          _sniHostCheck=sniHostCheck;
63      }
64      
65      @Override
66      public void customize(Connector connector, HttpConfiguration channelConfig, Request request)
67      {
68          if (request.getHttpChannel().getEndPoint() instanceof DecryptedEndPoint)
69          {
70              request.setScheme(HttpScheme.HTTPS.asString());
71              request.setSecure(true);
72              SslConnection.DecryptedEndPoint ssl_endp = (DecryptedEndPoint)request.getHttpChannel().getEndPoint();
73              SslConnection sslConnection = ssl_endp.getSslConnection();
74              SSLEngine sslEngine=sslConnection.getSSLEngine();
75              customize(sslEngine,request);
76          }
77      }
78  
79      /* ------------------------------------------------------------ */
80      /*
81       * Customise the request attributes to be set for SSL requests. <br>
82       * The requirements of the Servlet specs are:
83       * <ul>
84       * <li> an attribute named "javax.servlet.request.ssl_session_id" of type
85       * String (since Servlet Spec 3.0).</li>
86       * <li> an attribute named "javax.servlet.request.cipher_suite" of type
87       * String.</li>
88       * <li> an attribute named "javax.servlet.request.key_size" of type Integer.</li>
89       * <li> an attribute named "javax.servlet.request.X509Certificate" of type
90       * java.security.cert.X509Certificate[]. This is an array of objects of type
91       * X509Certificate, the order of this array is defined as being in ascending
92       * order of trust. The first certificate in the chain is the one set by the
93       * client, the next is the one used to authenticate the first, and so on.
94       * </li>
95       * </ul>
96       *
97       * @param request
98       *                HttpRequest to be customised.
99       */
100     public void customize(SSLEngine sslEngine, Request request)
101     {
102         request.setScheme(HttpScheme.HTTPS.asString());
103         SSLSession sslSession = sslEngine.getSession();
104 
105         if (_sniHostCheck)
106         {
107             String sniName = (String)sslSession.getValue("org.eclipse.jetty.util.ssl.sniname");
108             if (sniName!=null && !sniName.equalsIgnoreCase(request.getServerName()))
109             {
110                 LOG.warn("Host does not match SNI Name: {}!={}",sniName,request.getServerName());
111                 throw new BadMessageException(400,"Host does not match SNI");
112             }
113         }
114         
115         try
116         {
117             String cipherSuite=sslSession.getCipherSuite();
118             Integer keySize;
119             X509Certificate[] certs;
120             String idStr;
121 
122             CachedInfo cachedInfo=(CachedInfo)sslSession.getValue(CACHED_INFO_ATTR);
123             if (cachedInfo!=null)
124             {
125                 keySize=cachedInfo.getKeySize();
126                 certs=cachedInfo.getCerts();
127                 idStr=cachedInfo.getIdStr();
128             }
129             else 
130             {
131                 keySize=new Integer(SslContextFactory.deduceKeyLength(cipherSuite));
132                 certs=SslContextFactory.getCertChain(sslSession);
133                 byte[] bytes = sslSession.getId();
134                 idStr = TypeUtil.toHexString(bytes);
135                 cachedInfo=new CachedInfo(keySize,certs,idStr);
136                 sslSession.putValue(CACHED_INFO_ATTR,cachedInfo);
137             }
138 
139             if (certs!=null)
140                 request.setAttribute("javax.servlet.request.X509Certificate",certs);
141 
142             request.setAttribute("javax.servlet.request.cipher_suite",cipherSuite);
143             request.setAttribute("javax.servlet.request.key_size",keySize);
144             request.setAttribute("javax.servlet.request.ssl_session_id", idStr);
145         }
146         catch (Exception e)
147         {
148             LOG.warn(Log.EXCEPTION,e);
149         }
150     }
151 
152     @Override
153     public String toString()
154     {
155         return String.format("%s@%x",this.getClass().getSimpleName(),hashCode());
156     }
157     
158     /* ------------------------------------------------------------ */
159     /* ------------------------------------------------------------ */
160     /* ------------------------------------------------------------ */
161     /**
162      * Simple bundle of information that is cached in the SSLSession. Stores the
163      * effective keySize and the client certificate chain.
164      */
165     private static class CachedInfo
166     {
167         private final X509Certificate[] _certs;
168         private final Integer _keySize;
169         private final String _idStr;
170 
171         CachedInfo(Integer keySize, X509Certificate[] certs,String idStr)
172         {
173             this._keySize=keySize;
174             this._certs=certs;
175             this._idStr=idStr;
176         }
177 
178         X509Certificate[] getCerts()
179         {
180             return _certs;
181         }
182 
183         Integer getKeySize()
184         {
185             return _keySize;
186         }
187 
188         String getIdStr()
189         {
190             return _idStr;
191         }
192     }
193 
194 
195 
196 }