View Javadoc

1   // ========================================================================
2   // Copyright (c) 2008-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.security.authentication;
15  
16  import java.io.InputStream;
17  import java.security.KeyStore;
18  import java.security.Principal;
19  import java.security.cert.CRL;
20  import java.security.cert.X509Certificate;
21  import java.util.Collection;
22  
23  import javax.servlet.ServletRequest;
24  import javax.servlet.ServletResponse;
25  import javax.servlet.http.HttpServletRequest;
26  import javax.servlet.http.HttpServletResponse;
27  
28  import org.eclipse.jetty.http.security.Constraint;
29  import org.eclipse.jetty.http.security.Password;
30  import org.eclipse.jetty.security.ServerAuthException;
31  import org.eclipse.jetty.security.UserAuthentication;
32  import org.eclipse.jetty.server.Authentication;
33  import org.eclipse.jetty.server.Authentication.User;
34  import org.eclipse.jetty.server.UserIdentity;
35  import org.eclipse.jetty.util.B64Code;
36  import org.eclipse.jetty.util.security.CertificateUtils;
37  import org.eclipse.jetty.util.security.CertificateValidator;
38  
39  /**
40   * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $
41   */
42  public class ClientCertAuthenticator extends LoginAuthenticator
43  {
44      /** String name of keystore password property. */
45      private static final String PASSWORD_PROPERTY = "org.eclipse.jetty.ssl.password";
46  
47      /** Truststore path */
48      private String _trustStorePath;
49      /** Truststore provider name */
50      private String _trustStoreProvider;
51      /** Truststore type */
52      private String _trustStoreType = "JKS";
53      /** Truststore password */
54      private transient Password _trustStorePassword;
55  
56      /** Set to true if SSL certificate validation is required */
57      private boolean _validateCerts;
58      /** Path to file that contains Certificate Revocation List */
59      private String _crlPath;
60      /** Maximum certification path length (n - number of intermediate certs, -1 for unlimited) */
61      private int _maxCertPathLength = -1;
62      /** CRL Distribution Points (CRLDP) support */
63      private boolean _enableCRLDP = false;
64      /** On-Line Certificate Status Protocol (OCSP) support */
65      private boolean _enableOCSP = false;
66      /** Location of OCSP Responder */
67      private String _ocspResponderURL;
68      
69      public ClientCertAuthenticator()
70      {
71          super();
72      }
73  
74      public String getAuthMethod()
75      {
76          return Constraint.__CERT_AUTH;
77      }
78      
79      /**
80       * @return Authentication for request
81       * @throws ServerAuthException
82       */
83      public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException
84      {
85          if (!mandatory)
86              return _deferred;
87          
88          HttpServletRequest request = (HttpServletRequest)req;
89          HttpServletResponse response = (HttpServletResponse)res;
90          X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
91  
92          try
93          {
94              // Need certificates.
95              if (certs != null && certs.length > 0)
96              {
97                  
98                  if (_validateCerts)
99                  {
100                     KeyStore trustStore = getKeyStore(null,
101                             _trustStorePath, _trustStoreType, _trustStoreProvider,
102                             _trustStorePassword == null ? null :_trustStorePassword.toString());
103                     Collection<? extends CRL> crls = loadCRL(_crlPath);
104                     CertificateValidator validator = new CertificateValidator(trustStore, crls);
105                     validator.validate(certs);
106                 }
107                 
108                 for (X509Certificate cert: certs)
109                 {
110                     if (cert==null)
111                         continue;
112 
113                     Principal principal = cert.getSubjectDN();
114                     if (principal == null) principal = cert.getIssuerDN();
115                     final String username = principal == null ? "clientcert" : principal.getName();
116 
117                     final char[] credential = B64Code.encode(cert.getSignature());
118 
119                     UserIdentity user = _loginService.login(username,credential);
120                     if (user!=null)
121                     {
122                         renewSessionOnAuthentication(request,response);
123                         return new UserAuthentication(getAuthMethod(),user);
124                     }
125                 }
126             }
127                 
128             if (!_deferred.isDeferred(response))
129             {
130                 response.sendError(HttpServletResponse.SC_FORBIDDEN);
131                 return Authentication.SEND_FAILURE;
132             }
133             
134             return Authentication.UNAUTHENTICATED;
135         }
136         catch (Exception e)
137         {
138             throw new ServerAuthException(e.getMessage());
139         }
140     }
141 
142     /* ------------------------------------------------------------ */
143     /**
144      * Loads keystore using an input stream or a file path in the same
145      * order of precedence.
146      *
147      * Required for integrations to be able to override the mechanism
148      * used to load a keystore in order to provide their own implementation.
149      *
150      * @param storeStream keystore input stream
151      * @param storePath path of keystore file
152      * @param storeType keystore type
153      * @param storeProvider keystore provider
154      * @param storePassword keystore password
155      * @return created keystore
156      * @throws Exception
157      */
158     protected KeyStore getKeyStore(InputStream storeStream, String storePath, String storeType, String storeProvider, String storePassword) throws Exception
159     {
160         return CertificateUtils.getKeyStore(storeStream, storePath, storeType, storeProvider, storePassword);
161     }
162 
163     /* ------------------------------------------------------------ */
164     /**
165      * Loads certificate revocation list (CRL) from a file.
166      *
167      * Required for integrations to be able to override the mechanism used to
168      * load CRL in order to provide their own implementation.
169      *
170      * @param crlPath path of certificate revocation list file
171      * @return a (possibly empty) collection view of java.security.cert.CRL objects initialized with the data from the
172      *         input stream.
173      * @throws Exception
174      */
175     protected Collection<? extends CRL> loadCRL(String crlPath) throws Exception
176     {
177         return CertificateUtils.loadCRL(crlPath);
178     }
179 
180     public boolean secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, User validatedUser) throws ServerAuthException
181     {
182         return true;
183     }
184 
185     /* ------------------------------------------------------------ */
186     /**
187      * @return true if SSL certificate has to be validated
188      */
189     public boolean isValidateCerts()
190     {
191         return _validateCerts;
192     }
193 
194     /* ------------------------------------------------------------ */
195     /**
196      * @param validateCerts
197      *            true if SSL certificates have to be validated
198      */
199     public void setValidateCerts(boolean validateCerts)
200     {
201         _validateCerts = validateCerts;
202     }
203 
204     /* ------------------------------------------------------------ */
205     /**
206      * @return The file name or URL of the trust store location
207      */
208     public String getTrustStore()
209     {
210         return _trustStorePath;
211     }
212 
213     /* ------------------------------------------------------------ */
214     /**
215      * @param trustStorePath
216      *            The file name or URL of the trust store location
217      */
218     public void setTrustStore(String trustStorePath)
219     {
220         _trustStorePath = trustStorePath;
221     }
222 
223     /* ------------------------------------------------------------ */
224     /**
225      * @return The provider of the trust store
226      */
227     public String getTrustStoreProvider()
228     {
229         return _trustStoreProvider;
230     }
231 
232     /* ------------------------------------------------------------ */
233     /**
234      * @param trustStoreProvider
235      *            The provider of the trust store
236      */
237     public void setTrustStoreProvider(String trustStoreProvider)
238     {
239         _trustStoreProvider = trustStoreProvider;
240     }
241 
242     /* ------------------------------------------------------------ */
243     /**
244      * @return The type of the trust store (default "JKS")
245      */
246     public String getTrustStoreType()
247     {
248         return _trustStoreType;
249     }
250 
251     /* ------------------------------------------------------------ */
252     /**
253      * @param trustStoreType
254      *            The type of the trust store (default "JKS")
255      */
256     public void setTrustStoreType(String trustStoreType)
257     {
258         _trustStoreType = trustStoreType;
259     }
260 
261     /* ------------------------------------------------------------ */
262     /**
263      * @param password
264      *            The password for the trust store
265      */
266     public void setTrustStorePassword(String password)
267     {
268         _trustStorePassword = Password.getPassword(PASSWORD_PROPERTY,password,null);
269     }
270 
271     /* ------------------------------------------------------------ */
272     /** Get the crlPath.
273      * @return the crlPath
274      */
275     public String getCrlPath()
276     {
277         return _crlPath;
278     }
279 
280     /* ------------------------------------------------------------ */
281     /** Set the crlPath.
282      * @param crlPath the crlPath to set
283      */
284     public void setCrlPath(String crlPath)
285     {
286         _crlPath = crlPath;
287     }
288 
289     /**
290      * @return Maximum number of intermediate certificates in
291      * the certification path (-1 for unlimited)
292      */
293     public int getMaxCertPathLength()
294     {
295         return _maxCertPathLength;
296     }
297 
298     /* ------------------------------------------------------------ */
299     /**
300      * @param maxCertPathLength
301      *            maximum number of intermediate certificates in
302      *            the certification path (-1 for unlimited)
303      */
304     public void setMaxCertPathLength(int maxCertPathLength)
305     {
306         _maxCertPathLength = maxCertPathLength;
307     }
308     
309     /* ------------------------------------------------------------ */
310     /** 
311      * @return true if CRL Distribution Points support is enabled
312      */
313     public boolean isEnableCRLDP()
314     {
315         return _enableCRLDP;
316     }
317 
318     /* ------------------------------------------------------------ */
319     /** Enables CRL Distribution Points Support
320      * @param enableCRLDP true - turn on, false - turns off
321      */
322     public void setEnableCRLDP(boolean enableCRLDP)
323     {
324         _enableCRLDP = enableCRLDP;
325     }
326 
327     /* ------------------------------------------------------------ */
328     /** 
329      * @return true if On-Line Certificate Status Protocol support is enabled
330      */
331     public boolean isEnableOCSP()
332     {
333         return _enableOCSP;
334     }
335 
336     /* ------------------------------------------------------------ */
337     /** Enables On-Line Certificate Status Protocol support
338      * @param enableOCSP true - turn on, false - turn off
339      */
340     public void setEnableOCSP(boolean enableOCSP)
341     {
342         _enableOCSP = enableOCSP;
343     }
344 
345     /* ------------------------------------------------------------ */
346     /** 
347      * @return Location of the OCSP Responder
348      */
349     public String getOcspResponderURL()
350     {
351         return _ocspResponderURL;
352     }
353 
354     /* ------------------------------------------------------------ */
355     /** Set the location of the OCSP Responder.
356      * @param ocspResponderURL location of the OCSP Responder
357      */
358     public void setOcspResponderURL(String ocspResponderURL)
359     {
360         _ocspResponderURL = ocspResponderURL;
361     }
362 }