395 lines · 13.9 KB · Java
RawDownload
/* * Copyright (c) 1997, 2004, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.x509; import java.io.IOException;import java.net.URI;import java.net.URISyntaxException; import sun.security.util.*; /** * This class implements the URIName as required by the GeneralNames * ASN.1 object. * <p> * [RFC3280] When the subjectAltName extension contains a URI, the name MUST be * stored in the uniformResourceIdentifier (an IA5String). The name MUST * be a non-relative URL, and MUST follow the URL syntax and encoding * rules specified in [RFC 1738].  The name must include both a scheme * (e.g., "http" or "ftp") and a scheme-specific-part.  The scheme- * specific-part must include a fully qualified domain name or IP * address as the host. * <p> * As specified in [RFC 1738], the scheme name is not case-sensitive * (e.g., "http" is equivalent to "HTTP").  The host part is also not * case-sensitive, but other components of the scheme-specific-part may * be case-sensitive. When comparing URIs, conforming implementations * MUST compare the scheme and host without regard to case, but assume * the remainder of the scheme-specific-part is case sensitive. * <p> * [RFC1738] In general, URLs are written as follows: * <pre> * <scheme>:<scheme-specific-part> * </pre> * A URL contains the name of the scheme being used (<scheme>) followed * by a colon and then a string (the <scheme-specific-part>) whose * interpretation depends on the scheme. * <p> * While the syntax for the rest of the URL may vary depending on the * particular scheme selected, URL schemes that involve the direct use * of an IP-based protocol to a specified host on the Internet use a * common syntax for the scheme-specific data: * <pre> * //<user>:<password>@<host>:<port>/<url-path> * </pre> * [RFC2732] specifies that an IPv6 address contained inside a URL * must be enclosed in square brackets (to allow distinguishing the * colons that separate IPv6 components from the colons that separate * scheme-specific data. * <p> * @author Amit Kapoor * @author Hemma Prafullchandra * @author Sean Mullan * @author Steve Hanna * @see GeneralName * @see GeneralNames * @see GeneralNameInterface */public class URIName implements GeneralNameInterface {     // private attributes    private URI uri;    private String host;    private DNSName hostDNS;    private IPAddressName hostIP;     /**     * Create the URIName object from the passed encoded Der value.     *     * @param derValue the encoded DER URIName.     * @exception IOException on error.     */    public URIName(DerValue derValue) throws IOException {        this(derValue.getIA5String());    }     /**     * Create the URIName object with the specified name.     *     * @param name the URIName.     * @throws IOException if name is not a proper URIName     */    public URIName(String name) throws IOException {        try {            uri = new URI(name);        } catch (URISyntaxException use) {            throw (IOException) new IOException                ("invalid URI name:" + name).initCause(use);        }        if (uri.getScheme() == null) {            throw new IOException("URI name must include scheme:" + name);        }         host = uri.getHost();        // RFC 3280 says that the host should be non-null, but we allow it to        // be null because some widely deployed certificates contain CDP        // extensions with URIs that have no hostname (see bugs 4802236 and        // 5107944).        if (host != null) {            if (host.charAt(0) == '[') {                // Verify host is a valid IPv6 address name                String ipV6Host = host.substring(1, host.length()-1);                try {                    hostIP = new IPAddressName(ipV6Host);                } catch (IOException ioe) {                    throw new IOException("invalid URI name (host " +                        "portion is not a valid IPv6 address):" + name);                }            } else {                try {                    hostDNS = new DNSName(host);                } catch (IOException ioe) {                    // Not a valid DNS Name; see if it is a valid IPv4                    // IPAddressName                    try {                        hostIP = new IPAddressName(host);                    } catch (Exception ioe2) {                        throw new IOException("invalid URI name (host " +                            "portion is not a valid DNS name, IPv4 address," +                            " or IPv6 address):" + name);                    }                }            }        }    }     /**     * Create the URIName object with the specified name constraint. URI     * name constraints syntax is different than SubjectAltNames, etc. See     * 4.2.1.11 of RFC 3280.     *     * @param value the URI name constraint     * @throws IOException if name is not a proper URI name constraint     */    public static URIName nameConstraint(DerValue value) throws IOException {        URI uri;        String name = value.getIA5String();        try {            uri = new URI(name);        } catch (URISyntaxException use) {            throw (IOException) new IOException                ("invalid URI name constraint:" + name).initCause(use);        }        if (uri.getScheme() == null) {            String host = uri.getSchemeSpecificPart();            try {                DNSName hostDNS;                if (host.charAt(0) == '.') {                    hostDNS = new DNSName(host.substring(1));                } else {                    hostDNS = new DNSName(host);                }                return new URIName(uri, host, hostDNS);            } catch (IOException ioe) {                throw (IOException) new IOException                    ("invalid URI name constraint:" + name).initCause(ioe);            }        } else {            throw new IOException("invalid URI name constraint (should not " +                "include scheme):" + name);        }    }     URIName(URI uri, String host, DNSName hostDNS) {        this.uri = uri;        this.host = host;        this.hostDNS = hostDNS;    }     /**     * Return the type of the GeneralName.     */    public int getType() {        return GeneralNameInterface.NAME_URI;    }     /**     * Encode the URI name into the DerOutputStream.     *     * @param out the DER stream to encode the URIName to.     * @exception IOException on encoding errors.     */    public void encode(DerOutputStream out) throws IOException {        out.putIA5String(uri.toASCIIString());    }     /**     * Convert the name into user readable string.     */    public String toString() {        return "URIName: " + uri.toString();    }     /**     * Compares this name with another, for equality.     *     * @return true iff the names are equivalent according to RFC2459.     */    public boolean equals(Object obj) {        if (this == obj) {            return true;        }         if (!(obj instanceof URIName)) {            return false;        }         URIName other = (URIName) obj;         return uri.equals(other.getURI());    }     /**     * Returns the URIName as a java.net.URI object     */    public URI getURI() {        return uri;    }     /**     * Returns this URI name.     */    public String getName() {        return uri.toString();    }     /**     * Return the scheme name portion of a URIName     *     * @returns scheme portion of full name     */    public String getScheme() {        return uri.getScheme();    }     /**     * Return the host name or IP address portion of the URIName     *     * @returns host name or IP address portion of full name     */    public String getHost() {        return host;    }     /**     * Return the host object type; if host name is a     * DNSName, then this host object does not include any     * initial "." on the name.     *     * @returns host name as DNSName or IPAddressName     */    public Object getHostObject() {        if (hostIP != null) {            return hostIP;        } else {            return hostDNS;        }    }     /**     * Returns the hash code value for this object.     *     * @return a hash code value for this object.     */    public int hashCode() {        return uri.hashCode();    }     /**     * Return type of constraint inputName places on this name:<ul>     *   <li>NAME_DIFF_TYPE = -1: input name is different type from name     *       (i.e. does not constrain).     *   <li>NAME_MATCH = 0: input name matches name.     *   <li>NAME_NARROWS = 1: input name narrows name (is lower in the naming     *       subtree)     *   <li>NAME_WIDENS = 2: input name widens name (is higher in the naming     *       subtree)     *   <li>NAME_SAME_TYPE = 3: input name does not match or narrow name, but     *       is same type.     * </ul>.     * These results are used in checking NameConstraints during     * certification path verification.     * <p>     * RFC3280: For URIs, the constraint applies to the host part of the name.     * The constraint may specify a host or a domain.  Examples would be     * "foo.bar.com";  and ".xyz.com".  When the the constraint begins with     * a period, it may be expanded with one or more subdomains.  That is,     * the constraint ".xyz.com" is satisfied by both abc.xyz.com and     * abc.def.xyz.com.  However, the constraint ".xyz.com" is not satisfied     * by "xyz.com".  When the constraint does not begin with a period, it     * specifies a host.     * <p>     * @param inputName to be checked for being constrained     * @returns constraint type above     * @throws UnsupportedOperationException if name is not exact match, but     *  narrowing and widening are not supported for this name type.     */    public int constrains(GeneralNameInterface inputName)        throws UnsupportedOperationException {        int constraintType;        if (inputName == null) {            constraintType = NAME_DIFF_TYPE;        } else if (inputName.getType() != NAME_URI) {            constraintType = NAME_DIFF_TYPE;        } else {            // Assuming from here on that one or both of these is            // actually a URI name constraint (not a URI), so we            // only need to compare the host portion of the name             String otherHost = ((URIName)inputName).getHost();             // Quick check for equality            if (otherHost.equalsIgnoreCase(host)) {                constraintType = NAME_MATCH;            } else {                Object otherHostObject = ((URIName)inputName).getHostObject();                 if ((hostDNS == null) ||                    !(otherHostObject instanceof DNSName)) {                    // If one (or both) is an IP address, only same type                    constraintType = NAME_SAME_TYPE;                } else {                    // Both host portions are DNS names. Are they domains?                    boolean thisDomain = (host.charAt(0) == '.');                    boolean otherDomain = (otherHost.charAt(0) == '.');                    DNSName otherDNS = (DNSName) otherHostObject;                     // Run DNSName.constrains.                    constraintType = hostDNS.constrains(otherDNS);                    // If neither one is a domain, then they can't                    // widen or narrow. That's just SAME_TYPE.                    if ((!thisDomain && !otherDomain) &&                        ((constraintType == NAME_WIDENS) ||                         (constraintType == NAME_NARROWS))) {                        constraintType = NAME_SAME_TYPE;                    }                     // If one is a domain and the other isn't,                    // then they can't match. The one that's a                    // domain doesn't include the one that's                    // not a domain.                    if ((thisDomain != otherDomain) &&                        (constraintType == NAME_MATCH)) {                        if (thisDomain) {                            constraintType = NAME_WIDENS;                        } else {                            constraintType = NAME_NARROWS;                        }                    }                }            }        }        return constraintType;    }     /**     * Return subtree depth of this name for purposes of determining     * NameConstraints minimum and maximum bounds and for calculating     * path lengths in name subtrees.     *     * @returns distance of name from root     * @throws UnsupportedOperationException if not supported for this name type     */    public int subtreeDepth() throws UnsupportedOperationException {        DNSName dnsName = null;        try {            dnsName = new DNSName(host);        } catch (IOException ioe) {            throw new UnsupportedOperationException(ioe.getMessage());        }        return dnsName.subtreeDepth();    }}