View Javadoc

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