| // |
| // ======================================================================== |
| // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| // |
| |
| package org.eclipse.jetty.util.ssl; |
| |
| import java.security.cert.CertificateParsingException; |
| import java.security.cert.X509Certificate; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import javax.naming.InvalidNameException; |
| import javax.naming.ldap.LdapName; |
| import javax.naming.ldap.Rdn; |
| import javax.security.auth.x500.X500Principal; |
| |
| import org.eclipse.jetty.util.StringUtil; |
| import org.eclipse.jetty.util.log.Log; |
| import org.eclipse.jetty.util.log.Logger; |
| |
| public class X509 |
| { |
| private static final Logger LOG = Log.getLogger(X509.class); |
| |
| /* |
| * @see {@link X509Certificate#getKeyUsage()} |
| */ |
| private static final int KEY_USAGE__KEY_CERT_SIGN=5; |
| |
| /* |
| * |
| * @see {@link X509Certificate#getSubjectAlternativeNames()} |
| */ |
| private static final int SUBJECT_ALTERNATIVE_NAMES__DNS_NAME=2; |
| |
| public static boolean isCertSign(X509Certificate x509) |
| { |
| boolean[] key_usage=x509.getKeyUsage(); |
| return key_usage!=null && key_usage[KEY_USAGE__KEY_CERT_SIGN]; |
| } |
| |
| private final X509Certificate _x509; |
| private final String _alias; |
| private final List<String> _hosts=new ArrayList<>(); |
| private final List<String> _wilds=new ArrayList<>(); |
| |
| public X509(String alias,X509Certificate x509) throws CertificateParsingException, InvalidNameException |
| { |
| _alias=alias; |
| _x509 = x509; |
| |
| // Look for alternative name extensions |
| boolean named=false; |
| Collection<List<?>> altNames = x509.getSubjectAlternativeNames(); |
| if (altNames!=null) |
| { |
| for (List<?> list : altNames) |
| { |
| if (((Number)list.get(0)).intValue() == SUBJECT_ALTERNATIVE_NAMES__DNS_NAME) |
| { |
| String cn = list.get(1).toString(); |
| if (LOG.isDebugEnabled()) |
| LOG.debug("Certificate SAN alias={} CN={} in {}",alias,cn,this); |
| if (cn!=null) |
| { |
| named=true; |
| addName(cn); |
| } |
| } |
| } |
| } |
| |
| // If no names found, look up the CN from the subject |
| if (!named) |
| { |
| LdapName name=new LdapName(x509.getSubjectX500Principal().getName(X500Principal.RFC2253)); |
| for (Rdn rdn : name.getRdns()) |
| { |
| if (rdn.getType().equalsIgnoreCase("CN")) |
| { |
| String cn = rdn.getValue().toString(); |
| if (LOG.isDebugEnabled()) |
| LOG.debug("Certificate CN alias={} CN={} in {}",alias,cn,this); |
| if (cn!=null && cn.contains(".") && !cn.contains(" ")) |
| addName(cn); |
| } |
| } |
| } |
| } |
| |
| protected void addName(String cn) |
| { |
| cn=StringUtil.asciiToLowerCase(cn); |
| if (cn.startsWith("*.")) |
| _wilds.add(cn.substring(2)); |
| else |
| _hosts.add(cn); |
| } |
| |
| public String getAlias() |
| { |
| return _alias; |
| } |
| |
| public X509Certificate getCertificate() |
| { |
| return _x509; |
| } |
| |
| public Set<String> getHosts() |
| { |
| return new HashSet<>(_hosts); |
| } |
| |
| public Set<String> getWilds() |
| { |
| return new HashSet<>(_wilds); |
| } |
| |
| public boolean matches(String host) |
| { |
| host=StringUtil.asciiToLowerCase(host); |
| if (_hosts.contains(host) || _wilds.contains(host)) |
| return true; |
| |
| int dot = host.indexOf('.'); |
| if (dot>=0) |
| { |
| String domain=host.substring(dot+1); |
| if (_wilds.contains(domain)) |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public String toString() |
| { |
| return String.format("%s@%x(%s,h=%s,w=%s)", |
| getClass().getSimpleName(), |
| hashCode(), |
| _alias, |
| _hosts, |
| _wilds); |
| } |
| } |