nl.nikhef.slcshttps.trust
Class HostnameChecker

java.lang.Object
  extended by nl.nikhef.slcshttps.trust.HostnameChecker

public class HostnameChecker
extends Object

Class to check whether a certain certificate is valid for a certain hostname, either using TLS or LDAP scheme. This class is roughly a combination of the JDK1.6 internal sun.security.util.HostnameChecker and sun.net.util.IPAddressUtil. At the same time it uses a method getCNS(), roughly adapted from the not-yet-commons-ssl-0.3.10 package, instead of getSubjectX500Name().findMostSpecificAttribute(X500Name.commonName_oid).


Field Summary
private static int ALTNAME_DNS
          constant for subject alt names of type DNS.
private static int ALTNAME_IP
          constant for subject alt names of type IP.
private  byte checkType
          the algorithm to follow to perform the check.
private static int INADDR16SZ
          Number of bytes for a IPv6 address.
private static int INADDR4SZ
          Number of bytes for a IPv4 address.
private static HostnameChecker INSTANCE_LDAP
          Contains the HostnameChecker for type LDAP.
private static HostnameChecker INSTANCE_TLS
          Contains the HostnameChecker for type TLS.
private static int INT16SZ
          used in IPv6 checking.
static byte TYPE_LDAP
          Constant for a HostnameChecker for LDAP.
static byte TYPE_TLS
          Constant for a HostnameChecker for TLS.
 
Constructor Summary
private HostnameChecker(byte checkType)
          Constructs a HostnameChecker for type checkType.
 
Method Summary
private static byte[] convertFromIPv4MappedAddress(byte[] addr)
          Converts IPv4-Mapped address to IPv4 address.
private static String[] getCNs(X509Certificate cert)
          Method to obtain all the CNs from a certificate.
static HostnameChecker getInstance(byte checkType)
          Returns a HostnameChecker instance of the right type.
private static boolean isIpAddress(String name)
          Test whether the given hostname looks like a literal IPv4 or IPv6 address.
private static boolean isIPv4LiteralAddress(String src)
          Checks whether src is an IPv4 address.
private static boolean isIPv4MappedAddress(byte[] addr)
          Utility routine to check if the InetAddress is an IPv4 mapped IPv6 address.
private static boolean isIPv6LiteralAddress(String src)
          Checks whether src is an IPv6 address.
private  boolean isMatched(String name, String template)
          Returns true if name matches against template.
 void match(String expectedName, X509Certificate cert)
          Tries to match the X509Certificate against the given expectedName.
private static boolean matchAllWildcards(String name, String template)
          Returns true if name matches against template.
private  void matchDNS(String expectedName, X509Certificate cert)
          Check if the certificate allows use of the given DNS name.
private  void matchDNS(String expectedName, X509Certificate cert, boolean allCN)
          Check if the certificate allows use of the given DNS name.
private static void matchIP(String expectedIP, X509Certificate cert)
          Check if the certificate allows use of the given IP address.
private static boolean matchLeftmostWildcard(String name, String template)
          Returns true if name matches against template.
private static boolean matchWildCards(String name, String template)
          Returns true if the name matches against the template that may contain wildcard char *.
private static byte[] textToNumericFormatV4(String src)
          Converts IPv4 address in its textual presentation form into its numeric binary form.
private static byte[] textToNumericFormatV6(String src)
          Convert IPv6 presentation level address to network order binary form.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

INADDR4SZ

private static final int INADDR4SZ
Number of bytes for a IPv4 address.

See Also:
Constant Field Values

INADDR16SZ

private static final int INADDR16SZ
Number of bytes for a IPv6 address.

See Also:
Constant Field Values

INT16SZ

private static final int INT16SZ
used in IPv6 checking.

See Also:
Constant Field Values

TYPE_TLS

public static final byte TYPE_TLS
Constant for a HostnameChecker for TLS.

See Also:
Constant Field Values

INSTANCE_TLS

private static final HostnameChecker INSTANCE_TLS
Contains the HostnameChecker for type TLS.


TYPE_LDAP

public static final byte TYPE_LDAP
Constant for a HostnameChecker for LDAP.

See Also:
Constant Field Values

INSTANCE_LDAP

private static final HostnameChecker INSTANCE_LDAP
Contains the HostnameChecker for type LDAP.


ALTNAME_DNS

private static final int ALTNAME_DNS
constant for subject alt names of type DNS.

See Also:
Constant Field Values

ALTNAME_IP

private static final int ALTNAME_IP
constant for subject alt names of type IP.

See Also:
Constant Field Values

checkType

private final byte checkType
the algorithm to follow to perform the check.

Constructor Detail

HostnameChecker

private HostnameChecker(byte checkType)
Constructs a HostnameChecker for type checkType.

Parameters:
checkType - specifies which type to use, TYPE_TLS or TYPE_LDAP
Method Detail

getInstance

public static HostnameChecker getInstance(byte checkType)
Returns a HostnameChecker instance of the right type. Note that no new instance is created! checkType should be one of the TYPE_* constants defined in this class.

Parameters:
checkType - specifies which type to return, TYPE_TLS or TYPE_LDAP
Returns:
HostnameChecker of the correct type;

match

public void match(String expectedName,
                  X509Certificate cert)
           throws CertificateException
Tries to match the X509Certificate against the given expectedName.

Parameters:
expectedName - String containing the hostname or IP to check
cert - X509Certificate
Throws:
CertificateException - if the name does not match any of the names specified in the certificate

isIpAddress

private static boolean isIpAddress(String name)
Test whether the given hostname looks like a literal IPv4 or IPv6 address. The hostname does not need to be a fully qualified name. This is not a strict check that performs full input validation. That means if the method returns true, name need not be a correct IP address, rather that it does not represent a valid DNS hostname. Likewise for IP addresses when it returns false.

Parameters:
name - String to check
Returns:
boolean true if name looks like an IP address.

matchIP

private static void matchIP(String expectedIP,
                            X509Certificate cert)
                     throws CertificateException
Check if the certificate allows use of the given IP address. From RFC2818: In some cases, the URI is specified as an IP address rather than a hostname. In this case, the iPAddress subjectAltName must be present in the certificate and must exactly match the IP in the URI.

Parameters:
expectedIP - String containing the IP to check.
cert - X509Certificate.
Throws:
CertificateException - if the certificate is not valid for the given IP address.
See Also:
match(String,X509Certificate), matchDNS(String,X509Certificate)

matchDNS

private void matchDNS(String expectedName,
                      X509Certificate cert)
               throws CertificateException
Check if the certificate allows use of the given DNS name. Note: default Sun Java behaviour is to check only the first CN, only IE(6) checks all, we use the Java default.

Parameters:
expectedName - String containing the DNS name to check.
cert - X509Certificate.
Throws:
CertificateException - if the certificate is not valid for the given DNS name.
See Also:
match(String,X509Certificate), matchDNS(String,X509Certificate,boolean)

matchDNS

private void matchDNS(String expectedName,
                      X509Certificate cert,
                      boolean allCN)
               throws CertificateException
Check if the certificate allows use of the given DNS name. Below is comments from JDK 1.6 source, note that RFC2459 is superseded by RFC3280 and later RFC5280. From RFC2818: If a subjectAltName extension of type dNSName is present, that MUST be used as the identity. Otherwise, the (most specific) Common Name field in the Subject field of the certificate MUST be used. Although the use of the Common Name is existing practice, it is deprecated and Certification Authorities are encouraged to use the dNSName instead. Matching is performed using the matching rules specified by [RFC2459]. If more than one identity of a given type is present in the certificate (e.g., more than one dNSName name, a match in any one of the set is considered acceptable.)

Parameters:
expectedName - String containing the DNS name to check.
cert - X509Certificate.
allCN - boolean whether to check all CN's or just the first. Only IE uses all.
Throws:
CertificateException - if the certificate is not valid for the given DNS name.
See Also:
match(String,X509Certificate), matchDNS(String,X509Certificate)

getCNs

private static String[] getCNs(X509Certificate cert)
Method to obtain all the CNs from a certificate. This method replaces the getSubjectX500Name().findMostSpecificAttribute(X500Name.commonName_oid) and is adapted from not-yet-commons-ssl-0.3.10.

Parameters:
cert - X509Certificate to use
Returns:
String[] array of CN's

isMatched

private boolean isMatched(String name,
                          String template)
Returns true if name matches against template. The matching is performed as per RFC 2818 rules for TLS and RFC 2830 rules for LDAP.

Parameters:
name - should represent a DNS name.
template - may contain the wildcard character *
Returns:
boolean whether name matches template
See Also:
matchAllWildcards(String,String), matchLeftmostWildcard(String,String), matchWildCards(String,String)

matchAllWildcards

private static boolean matchAllWildcards(String name,
                                         String template)
Returns true if name matches against template. According to RFC 2818, section 3.1 - Names may contain the wildcard character * which is considered to match any single domain name component or component fragment. E.g., *.a.com matches foo.a.com but not bar.foo.a.com. f*.com matches foo.com but not bar.com.

Parameters:
name - should represent a DNS name.
template - may contain the wildcard character *
Returns:
boolean whether name matches template
See Also:
isMatched(String,String), matchLeftmostWildcard(String,String), matchWildCards(String,String)

matchLeftmostWildcard

private static boolean matchLeftmostWildcard(String name,
                                             String template)
Returns true if name matches against template. As per RFC 2830, section 3.6 - The "*" wildcard character is allowed. If present, it applies only to the left-most name component. E.g. *.bar.com would match a.bar.com, b.bar.com, etc. but not bar.com.

Parameters:
name - should represent a DNS name.
template - may contain the wildcard character *
Returns:
boolean whether name matches template
See Also:
isMatched(String,String), matchAllWildcards(String,String), matchWildCards(String,String)

matchWildCards

private static boolean matchWildCards(String name,
                                      String template)
Returns true if the name matches against the template that may contain wildcard char *.

Parameters:
name - should represent a DNS name.
template - may contain the wildcard character *
Returns:
boolean whether name matches template
See Also:
isMatched(String,String), matchAllWildcards(String,String), matchLeftmostWildcard(String,String)

textToNumericFormatV4

private static byte[] textToNumericFormatV4(String src)
Converts IPv4 address in its textual presentation form into its numeric binary form.

Parameters:
src - a String representing an IPv4 address in standard format
Returns:
byte[] representing the IPv4 numeric address

textToNumericFormatV6

private static byte[] textToNumericFormatV6(String src)
Convert IPv6 presentation level address to network order binary form. credit: Converted from C code from Solaris 8 (inet_pton) Any component of the string following a per-cent % is ignored.

Parameters:
src - a String representing an IPv6 address in textual format
Returns:
byte[] representing the IPv6 numeric address

isIPv4LiteralAddress

private static boolean isIPv4LiteralAddress(String src)
Checks whether src is an IPv4 address.

Parameters:
src - String representing an IPv4 address in textual format.
Returns:
boolean indicating whether src is an IPv4 literal address

isIPv6LiteralAddress

private static boolean isIPv6LiteralAddress(String src)
Checks whether src is an IPv6 address.

Parameters:
src - String representing an IPv6 address in textual format.
Returns:
boolean indicating whether src is an IPv6 literal address.

convertFromIPv4MappedAddress

private static byte[] convertFromIPv4MappedAddress(byte[] addr)
Converts IPv4-Mapped address to IPv4 address. Both input and returned value are in network order binary form.

Parameters:
addr - byte[] representing an IPv4-Mapped address
Returns:
byte[] representing the IPv4 numeric address or null

isIPv4MappedAddress

private static boolean isIPv4MappedAddress(byte[] addr)
Utility routine to check if the InetAddress is an IPv4 mapped IPv6 address.

Parameters:
addr - byte[] describing the address.
Returns:
boolean: true if the InetAddress is an IPv4 mapped IPv6 address; or false if address is IPv4 address.


nl.nikhef.slcshttps Mischa Sallé - msalle(AT)nikhef.nl