Home | History | Annotate | Download | only in x509
      1 /*
      2  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
      3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      4  *
      5  * This code is free software; you can redistribute it and/or modify it
      6  * under the terms of the GNU General Public License version 2 only, as
      7  * published by the Free Software Foundation.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle in the LICENSE file that accompanied this code.
     10  *
     11  * This code is distributed in the hope that it will be useful, but WITHOUT
     12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  * version 2 for more details (a copy is included in the LICENSE file that
     15  * accompanied this code).
     16  *
     17  * You should have received a copy of the GNU General Public License version
     18  * 2 along with this work; if not, write to the Free Software Foundation,
     19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     20  *
     21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * questions.
     24  */
     25 
     26 package sun.security.x509;
     27 
     28 import java.io.IOException;
     29 import java.net.URI;
     30 import java.net.URISyntaxException;
     31 
     32 import sun.security.util.*;
     33 
     34 /**
     35  * This class implements the URIName as required by the GeneralNames
     36  * ASN.1 object.
     37  * <p>
     38  * [RFC3280] When the subjectAltName extension contains a URI, the name MUST be
     39  * stored in the uniformResourceIdentifier (an IA5String). The name MUST
     40  * be a non-relative URL, and MUST follow the URL syntax and encoding
     41  * rules specified in [RFC 1738].  The name must include both a scheme
     42  * (e.g., "http" or "ftp") and a scheme-specific-part.  The scheme-
     43  * specific-part must include a fully qualified domain name or IP
     44  * address as the host.
     45  * <p>
     46  * As specified in [RFC 1738], the scheme name is not case-sensitive
     47  * (e.g., "http" is equivalent to "HTTP").  The host part is also not
     48  * case-sensitive, but other components of the scheme-specific-part may
     49  * be case-sensitive. When comparing URIs, conforming implementations
     50  * MUST compare the scheme and host without regard to case, but assume
     51  * the remainder of the scheme-specific-part is case sensitive.
     52  * <p>
     53  * [RFC1738] In general, URLs are written as follows:
     54  * <pre>
     55  * <scheme>:<scheme-specific-part>
     56  * </pre>
     57  * A URL contains the name of the scheme being used (<scheme>) followed
     58  * by a colon and then a string (the <scheme-specific-part>) whose
     59  * interpretation depends on the scheme.
     60  * <p>
     61  * While the syntax for the rest of the URL may vary depending on the
     62  * particular scheme selected, URL schemes that involve the direct use
     63  * of an IP-based protocol to a specified host on the Internet use a
     64  * common syntax for the scheme-specific data:
     65  * <pre>
     66  * //<user>:<password>@<host>:<port>/<url-path>
     67  * </pre>
     68  * [RFC2732] specifies that an IPv6 address contained inside a URL
     69  * must be enclosed in square brackets (to allow distinguishing the
     70  * colons that separate IPv6 components from the colons that separate
     71  * scheme-specific data.
     72  * <p>
     73  * @author Amit Kapoor
     74  * @author Hemma Prafullchandra
     75  * @author Sean Mullan
     76  * @author Steve Hanna
     77  * @see GeneralName
     78  * @see GeneralNames
     79  * @see GeneralNameInterface
     80  */
     81 public class URIName implements GeneralNameInterface {
     82 
     83     // private attributes
     84     private URI uri;
     85     private String host;
     86     private DNSName hostDNS;
     87     private IPAddressName hostIP;
     88 
     89     /**
     90      * Create the URIName object from the passed encoded Der value.
     91      *
     92      * @param derValue the encoded DER URIName.
     93      * @exception IOException on error.
     94      */
     95     public URIName(DerValue derValue) throws IOException {
     96         this(derValue.getIA5String());
     97     }
     98 
     99     /**
    100      * Create the URIName object with the specified name.
    101      *
    102      * @param name the URIName.
    103      * @throws IOException if name is not a proper URIName
    104      */
    105     public URIName(String name) throws IOException {
    106         try {
    107             uri = new URI(name);
    108         } catch (URISyntaxException use) {
    109             throw new IOException("invalid URI name:" + name, use);
    110         }
    111         if (uri.getScheme() == null) {
    112             throw new IOException("URI name must include scheme:" + name);
    113         }
    114 
    115         host = uri.getHost();
    116         // RFC 3280 says that the host should be non-null, but we allow it to
    117         // be null because some widely deployed certificates contain CDP
    118         // extensions with URIs that have no hostname (see bugs 4802236 and
    119         // 5107944).
    120         if (host != null) {
    121             if (host.charAt(0) == '[') {
    122                 // Verify host is a valid IPv6 address name
    123                 String ipV6Host = host.substring(1, host.length()-1);
    124                 try {
    125                     hostIP = new IPAddressName(ipV6Host);
    126                 } catch (IOException ioe) {
    127                     throw new IOException("invalid URI name (host " +
    128                         "portion is not a valid IPv6 address):" + name);
    129                 }
    130             } else {
    131                 try {
    132                     hostDNS = new DNSName(host);
    133                 } catch (IOException ioe) {
    134                     // Not a valid DNS Name; see if it is a valid IPv4
    135                     // IPAddressName
    136                     try {
    137                         hostIP = new IPAddressName(host);
    138                     } catch (Exception ioe2) {
    139                         throw new IOException("invalid URI name (host " +
    140                             "portion is not a valid DNS name, IPv4 address," +
    141                             " or IPv6 address):" + name);
    142                     }
    143                 }
    144             }
    145         }
    146     }
    147 
    148     /**
    149      * Create the URIName object with the specified name constraint. URI
    150      * name constraints syntax is different than SubjectAltNames, etc. See
    151      * 4.2.1.11 of RFC 3280.
    152      *
    153      * @param value the URI name constraint
    154      * @throws IOException if name is not a proper URI name constraint
    155      */
    156     public static URIName nameConstraint(DerValue value) throws IOException {
    157         URI uri;
    158         String name = value.getIA5String();
    159         try {
    160             uri = new URI(name);
    161         } catch (URISyntaxException use) {
    162             throw new IOException("invalid URI name constraint:" + name, use);
    163         }
    164         if (uri.getScheme() == null) {
    165             String host = uri.getSchemeSpecificPart();
    166             try {
    167                 DNSName hostDNS;
    168                 if (host.startsWith(".")) {
    169                     hostDNS = new DNSName(host.substring(1));
    170                 } else {
    171                     hostDNS = new DNSName(host);
    172                 }
    173                 return new URIName(uri, host, hostDNS);
    174             } catch (IOException ioe) {
    175                 throw new IOException("invalid URI name constraint:" + name, ioe);
    176             }
    177         } else {
    178             throw new IOException("invalid URI name constraint (should not " +
    179                 "include scheme):" + name);
    180         }
    181     }
    182 
    183     URIName(URI uri, String host, DNSName hostDNS) {
    184         this.uri = uri;
    185         this.host = host;
    186         this.hostDNS = hostDNS;
    187     }
    188 
    189     /**
    190      * Return the type of the GeneralName.
    191      */
    192     public int getType() {
    193         return GeneralNameInterface.NAME_URI;
    194     }
    195 
    196     /**
    197      * Encode the URI name into the DerOutputStream.
    198      *
    199      * @param out the DER stream to encode the URIName to.
    200      * @exception IOException on encoding errors.
    201      */
    202     public void encode(DerOutputStream out) throws IOException {
    203         out.putIA5String(uri.toASCIIString());
    204     }
    205 
    206     /**
    207      * Convert the name into user readable string.
    208      */
    209     public String toString() {
    210         return "URIName: " + uri.toString();
    211     }
    212 
    213     /**
    214      * Compares this name with another, for equality.
    215      *
    216      * @return true iff the names are equivalent according to RFC2459.
    217      */
    218     public boolean equals(Object obj) {
    219         if (this == obj) {
    220             return true;
    221         }
    222 
    223         if (!(obj instanceof URIName)) {
    224             return false;
    225         }
    226 
    227         URIName other = (URIName) obj;
    228 
    229         return uri.equals(other.getURI());
    230     }
    231 
    232     /**
    233      * Returns the URIName as a java.net.URI object
    234      */
    235     public URI getURI() {
    236         return uri;
    237     }
    238 
    239     /**
    240      * Returns this URI name.
    241      */
    242     public String getName() {
    243         return uri.toString();
    244     }
    245 
    246     /**
    247      * Return the scheme name portion of a URIName
    248      *
    249      * @returns scheme portion of full name
    250      */
    251     public String getScheme() {
    252         return uri.getScheme();
    253     }
    254 
    255     /**
    256      * Return the host name or IP address portion of the URIName
    257      *
    258      * @returns host name or IP address portion of full name
    259      */
    260     public String getHost() {
    261         return host;
    262     }
    263 
    264     /**
    265      * Return the host object type; if host name is a
    266      * DNSName, then this host object does not include any
    267      * initial "." on the name.
    268      *
    269      * @returns host name as DNSName or IPAddressName
    270      */
    271     public Object getHostObject() {
    272         if (hostIP != null) {
    273             return hostIP;
    274         } else {
    275             return hostDNS;
    276         }
    277     }
    278 
    279     /**
    280      * Returns the hash code value for this object.
    281      *
    282      * @return a hash code value for this object.
    283      */
    284     public int hashCode() {
    285         return uri.hashCode();
    286     }
    287 
    288     /**
    289      * Return type of constraint inputName places on this name:<ul>
    290      *   <li>NAME_DIFF_TYPE = -1: input name is different type from name
    291      *       (i.e. does not constrain).
    292      *   <li>NAME_MATCH = 0: input name matches name.
    293      *   <li>NAME_NARROWS = 1: input name narrows name (is lower in the naming
    294      *       subtree)
    295      *   <li>NAME_WIDENS = 2: input name widens name (is higher in the naming
    296      *       subtree)
    297      *   <li>NAME_SAME_TYPE = 3: input name does not match or narrow name, but
    298      *       is same type.
    299      * </ul>.
    300      * These results are used in checking NameConstraints during
    301      * certification path verification.
    302      * <p>
    303      * RFC3280: For URIs, the constraint applies to the host part of the name.
    304      * The constraint may specify a host or a domain.  Examples would be
    305      * "foo.bar.com";  and ".xyz.com".  When the the constraint begins with
    306      * a period, it may be expanded with one or more subdomains.  That is,
    307      * the constraint ".xyz.com" is satisfied by both abc.xyz.com and
    308      * abc.def.xyz.com.  However, the constraint ".xyz.com" is not satisfied
    309      * by "xyz.com".  When the constraint does not begin with a period, it
    310      * specifies a host.
    311      * <p>
    312      * @param inputName to be checked for being constrained
    313      * @returns constraint type above
    314      * @throws UnsupportedOperationException if name is not exact match, but
    315      *  narrowing and widening are not supported for this name type.
    316      */
    317     public int constrains(GeneralNameInterface inputName)
    318         throws UnsupportedOperationException {
    319         int constraintType;
    320         if (inputName == null) {
    321             constraintType = NAME_DIFF_TYPE;
    322         } else if (inputName.getType() != NAME_URI) {
    323             constraintType = NAME_DIFF_TYPE;
    324         } else {
    325             // Assuming from here on that one or both of these is
    326             // actually a URI name constraint (not a URI), so we
    327             // only need to compare the host portion of the name
    328 
    329             String otherHost = ((URIName)inputName).getHost();
    330 
    331             // Quick check for equality
    332             if (otherHost.equalsIgnoreCase(host)) {
    333                 constraintType = NAME_MATCH;
    334             } else {
    335                 Object otherHostObject = ((URIName)inputName).getHostObject();
    336 
    337                 if ((hostDNS == null) ||
    338                     !(otherHostObject instanceof DNSName)) {
    339                     // If one (or both) is an IP address, only same type
    340                     constraintType = NAME_SAME_TYPE;
    341                 } else {
    342                     // Both host portions are DNS names. Are they domains?
    343                     boolean thisDomain = (host.charAt(0) == '.');
    344                     boolean otherDomain = (otherHost.charAt(0) == '.');
    345                     DNSName otherDNS = (DNSName) otherHostObject;
    346 
    347                     // Run DNSName.constrains.
    348                     constraintType = hostDNS.constrains(otherDNS);
    349                     // If neither one is a domain, then they can't
    350                     // widen or narrow. That's just SAME_TYPE.
    351                     if ((!thisDomain && !otherDomain) &&
    352                         ((constraintType == NAME_WIDENS) ||
    353                          (constraintType == NAME_NARROWS))) {
    354                         constraintType = NAME_SAME_TYPE;
    355                     }
    356 
    357                     // If one is a domain and the other isn't,
    358                     // then they can't match. The one that's a
    359                     // domain doesn't include the one that's
    360                     // not a domain.
    361                     if ((thisDomain != otherDomain) &&
    362                         (constraintType == NAME_MATCH)) {
    363                         if (thisDomain) {
    364                             constraintType = NAME_WIDENS;
    365                         } else {
    366                             constraintType = NAME_NARROWS;
    367                         }
    368                     }
    369                 }
    370             }
    371         }
    372         return constraintType;
    373     }
    374 
    375     /**
    376      * Return subtree depth of this name for purposes of determining
    377      * NameConstraints minimum and maximum bounds and for calculating
    378      * path lengths in name subtrees.
    379      *
    380      * @returns distance of name from root
    381      * @throws UnsupportedOperationException if not supported for this name type
    382      */
    383     public int subtreeDepth() throws UnsupportedOperationException {
    384         DNSName dnsName = null;
    385         try {
    386             dnsName = new DNSName(host);
    387         } catch (IOException ioe) {
    388             throw new UnsupportedOperationException(ioe.getMessage());
    389         }
    390         return dnsName.subtreeDepth();
    391     }
    392 }
    393