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 */ URIName(DerValue derValue)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 */ URIName(String name)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 */ nameConstraint(DerValue value)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 URIName(URI uri, String host, DNSName hostDNS)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 */ getType()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 */ encode(DerOutputStream out)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 */ toString()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 */ equals(Object obj)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 */ getURI()235 public URI getURI() { 236 return uri; 237 } 238 239 /** 240 * Returns this URI name. 241 */ getName()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 */ getScheme()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 */ getHost()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 */ getHostObject()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 */ hashCode()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 */ constrains(GeneralNameInterface inputName)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 */ subtreeDepth()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