package com.hwlcn.ldap.util.ssl;
import java.net.InetAddress;
import java.net.URI;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;
import com.hwlcn.ldap.ldap.sdk.DN;
import com.hwlcn.ldap.ldap.sdk.RDN;
import com.hwlcn.core.annotation.NotMutable;
import com.hwlcn.ldap.util.StaticUtils;
import com.hwlcn.core.annotation.ThreadSafety;
import com.hwlcn.ldap.util.ThreadSafetyLevel;
import com.hwlcn.ldap.util.Validator;
import static com.hwlcn.ldap.util.Debug.*;
import static com.hwlcn.ldap.util.ssl.SSLMessages.*;
@NotMutable()
@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
public final class HostNameTrustManager
implements X509TrustManager
{
private static final X509Certificate[] NO_CERTIFICATES =
new X509Certificate[0];
private final boolean allowWildcards;
private final Set<String> acceptableHostNames;
public HostNameTrustManager(final boolean allowWildcards,
final String... acceptableHostNames)
{
this(allowWildcards, StaticUtils.toList(acceptableHostNames));
}
public HostNameTrustManager(final boolean allowWildcards,
final Collection<String> acceptableHostNames)
{
Validator.ensureNotNull(acceptableHostNames);
Validator.ensureFalse(acceptableHostNames.isEmpty(),
"The set of acceptable host names must not be empty.");
this.allowWildcards = allowWildcards;
final LinkedHashSet<String> nameSet =
new LinkedHashSet<String>(acceptableHostNames.size());
for (final String s : acceptableHostNames)
{
nameSet.add(StaticUtils.toLowerCase(s));
}
this.acceptableHostNames = Collections.unmodifiableSet(nameSet);
}
public boolean allowWildcards()
{
return allowWildcards;
}
public Set<String> getAcceptableHostNames()
{
return acceptableHostNames;
}
public void checkClientTrusted(final X509Certificate[] chain,
final String authType)
throws CertificateException
{
checkCertificate(chain[0]);
}
public void checkServerTrusted(final X509Certificate[] chain,
final String authType)
throws CertificateException
{
checkCertificate(chain[0]);
}
private void checkCertificate(final X509Certificate c)
throws CertificateException
{
final String subjectDN =
c.getSubjectX500Principal().getName(X500Principal.RFC2253);
try
{
final DN dn = new DN(subjectDN);
for (final RDN rdn : dn.getRDNs())
{
final String[] names = rdn.getAttributeNames();
final String[] values = rdn.getAttributeValues();
for (int i=0; i < names.length; i++)
{
final String lowerName = StaticUtils.toLowerCase(names[i]);
if (lowerName.equals("cn") || lowerName.equals("commonname") ||
lowerName.equals("2.5.4.3"))
{
final String lowerValue = StaticUtils.toLowerCase(values[i]);
if (acceptableHostNames.contains(lowerValue))
{
return;
}
if (allowWildcards && lowerValue.startsWith("*."))
{
final String withoutWildcard = lowerValue.substring(1);
for (final String s : acceptableHostNames)
{
if (s.endsWith(withoutWildcard))
{
return;
}
}
}
}
}
}
}
catch (final Exception e)
{
debugException(e);
}
final Collection<List<?>> subjectAltNames = c.getSubjectAlternativeNames();
if (subjectAltNames != null)
{
for (final List<?> l : subjectAltNames)
{
try
{
final Integer type = (Integer) l.get(0);
switch (type)
{
case 2:
final String dnsName = StaticUtils.toLowerCase((String) l.get(1));
if (acceptableHostNames.contains(dnsName))
{
return;
}
if (allowWildcards && dnsName.startsWith("*."))
{
final String withoutWildcard = dnsName.substring(1);
for (final String s : acceptableHostNames)
{
if (s.endsWith(withoutWildcard))
{
return;
}
}
}
break;
case 6:
final URI uri = new URI((String) l.get(1));
if (acceptableHostNames.contains(
StaticUtils.toLowerCase(uri.getHost())))
{
return;
}
break;
case 7:
final InetAddress inetAddress =
InetAddress.getByName((String) l.get(1));
for (final String s : acceptableHostNames)
{
if (Character.isDigit(s.charAt(0)) || (s.indexOf(':') >= 0))
{
final InetAddress a = InetAddress.getByName(s);
if (inetAddress.equals(a))
{
return;
}
}
}
break;
case 0:
case 1:
case 3:
case 4:
case 5:
case 8:
default:
break;
}
}
catch (final Exception e)
{
debugException(e);
}
}
}
throw new CertificateException(ERR_HOSTNAME_NOT_FOUND.get(subjectDN));
}
public X509Certificate[] getAcceptedIssuers()
{
return NO_CERTIFICATES;
}
}