// // ======================================================================== // 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); } }