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.util.ssl;
20  
21  import java.security.cert.CertificateParsingException;
22  import java.security.cert.X509Certificate;
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Set;
28  
29  import javax.naming.InvalidNameException;
30  import javax.naming.ldap.LdapName;
31  import javax.naming.ldap.Rdn;
32  import javax.security.auth.x500.X500Principal;
33  
34  import org.eclipse.jetty.util.StringUtil;
35  import org.eclipse.jetty.util.log.Log;
36  import org.eclipse.jetty.util.log.Logger;
37  
38  public class X509
39  {
40      private static final Logger LOG = Log.getLogger(X509.class);
41  
42      /*
43       * @see {@link X509Certificate#getKeyUsage()}
44       */
45      private static final int KEY_USAGE__KEY_CERT_SIGN=5;
46  
47      /*
48       *
49       * @see {@link X509Certificate#getSubjectAlternativeNames()}
50       */
51      private static final int SUBJECT_ALTERNATIVE_NAMES__DNS_NAME=2;
52  
53      public static boolean isCertSign(X509Certificate x509)
54      {
55          boolean[] key_usage=x509.getKeyUsage();
56          return key_usage!=null && key_usage[KEY_USAGE__KEY_CERT_SIGN];
57      }
58  
59      private final X509Certificate _x509;
60      private final String _alias;
61      private final List<String> _hosts=new ArrayList<>();
62      private final List<String> _wilds=new ArrayList<>();
63  
64      public X509(String alias,X509Certificate x509) throws CertificateParsingException, InvalidNameException
65      {
66          _alias=alias;
67          _x509 = x509;
68  
69          // Look for alternative name extensions
70          boolean named=false;
71          Collection<List<?>> altNames = x509.getSubjectAlternativeNames();
72          if (altNames!=null)
73          {
74              for (List<?> list : altNames)
75              {
76                  if (((Number)list.get(0)).intValue() == SUBJECT_ALTERNATIVE_NAMES__DNS_NAME)
77                  {
78                      String cn = list.get(1).toString();
79                      if (LOG.isDebugEnabled())
80                          LOG.debug("Certificate SAN alias={} CN={} in {}",alias,cn,this);
81                      if (cn!=null)
82                      {
83                          named=true;
84                          addName(cn);
85                      }
86                  }
87              }
88          }
89  
90          // If no names found, look up the CN from the subject
91          if (!named)
92          {
93              LdapName name=new LdapName(x509.getSubjectX500Principal().getName(X500Principal.RFC2253));
94              for (Rdn rdn : name.getRdns())
95              {
96                  if (rdn.getType().equalsIgnoreCase("CN"))
97                  {
98                      String cn = rdn.getValue().toString();
99                      if (LOG.isDebugEnabled())
100                         LOG.debug("Certificate CN alias={} CN={} in {}",alias,cn,this);
101                     if (cn!=null && cn.contains(".") && !cn.contains(" "))
102                         addName(cn);
103                 }
104             }
105         }
106     }
107 
108     protected void addName(String cn)
109     {
110         cn=StringUtil.asciiToLowerCase(cn);
111         if (cn.startsWith("*."))
112             _wilds.add(cn.substring(2));
113         else
114             _hosts.add(cn);
115     }
116 
117     public String getAlias()
118     {
119         return _alias;
120     }
121 
122     public X509Certificate getCertificate()
123     {
124         return _x509;
125     }
126 
127     public Set<String> getHosts()
128     {
129         return new HashSet<>(_hosts);
130     }
131 
132     public Set<String> getWilds()
133     {
134         return new HashSet<>(_wilds);
135     }
136 
137     public boolean matches(String host)
138     {
139         host=StringUtil.asciiToLowerCase(host);
140         if (_hosts.contains(host) || _wilds.contains(host))
141             return true;
142 
143         int dot = host.indexOf('.');
144         if (dot>=0)
145         {
146             String domain=host.substring(dot+1);
147             if (_wilds.contains(domain))
148                 return true;
149         }
150         return false;
151     }
152 
153     @Override
154     public String toString()
155     {
156         return String.format("%s@%x(%s,h=%s,w=%s)",
157                 getClass().getSimpleName(),
158                 hashCode(),
159                 _alias,
160                 _hosts,
161                 _wilds);
162     }
163 }