1 /* 2 * Copyright (c) 1997, 2004, 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 (IOException) new IOException 110 ("invalid URI name:" + name).initCause(use); 111 } 112 if (uri.getScheme() == null) { 113 throw new IOException("URI name must include scheme:" + name); 114 } 115 116 host = uri.getHost(); 117 // RFC 3280 says that the host should be non-null, but we allow it to 118 // be null because some widely deployed certificates contain CDP 119 // extensions with URIs that have no hostname (see bugs 4802236 and 120 // 5107944). 121 if (host != null) { 122 if (host.charAt(0) == '[') { 123 // Verify host is a valid IPv6 address name 124 String ipV6Host = host.substring(1, host.length()-1); 125 try { 126 hostIP = new IPAddressName(ipV6Host); 127 } catch (IOException ioe) { 128 throw new IOException("invalid URI name (host " + 129 "portion is not a valid IPv6 address):" + name); 130 } 131 } else { 132 try { 133 hostDNS = new DNSName(host); 134 } catch (IOException ioe) { 135 // Not a valid DNS Name; see if it is a valid IPv4 136 // IPAddressName 137 try { 138 hostIP = new IPAddressName(host); 139 } catch (Exception ioe2) { 140 throw new IOException("invalid URI name (host " + 141 "portion is not a valid DNS name, IPv4 address," + 142 " or IPv6 address):" + name); 143 } 144 } 145 } 146 } 147 } 148 149 /** 150 * Create the URIName object with the specified name constraint. URI 151 * name constraints syntax is different than SubjectAltNames, etc. See 152 * 4.2.1.11 of RFC 3280. 153 * 154 * @param value the URI name constraint 155 * @throws IOException if name is not a proper URI name constraint 156 */ nameConstraint(DerValue value)157 public static URIName nameConstraint(DerValue value) throws IOException { 158 URI uri; 159 String name = value.getIA5String(); 160 try { 161 uri = new URI(name); 162 } catch (URISyntaxException use) { 163 throw (IOException) new IOException 164 ("invalid URI name constraint:" + name).initCause(use); 165 } 166 if (uri.getScheme() == null) { 167 String host = uri.getSchemeSpecificPart(); 168 try { 169 DNSName hostDNS; 170 if (host.charAt(0) == '.') { 171 hostDNS = new DNSName(host.substring(1)); 172 } else { 173 hostDNS = new DNSName(host); 174 } 175 return new URIName(uri, host, hostDNS); 176 } catch (IOException ioe) { 177 throw (IOException) new IOException 178 ("invalid URI name constraint:" + name).initCause(ioe); 179 } 180 } else { 181 throw new IOException("invalid URI name constraint (should not " + 182 "include scheme):" + name); 183 } 184 } 185 URIName(URI uri, String host, DNSName hostDNS)186 URIName(URI uri, String host, DNSName hostDNS) { 187 this.uri = uri; 188 this.host = host; 189 this.hostDNS = hostDNS; 190 } 191 192 /** 193 * Return the type of the GeneralName. 194 */ getType()195 public int getType() { 196 return GeneralNameInterface.NAME_URI; 197 } 198 199 /** 200 * Encode the URI name into the DerOutputStream. 201 * 202 * @param out the DER stream to encode the URIName to. 203 * @exception IOException on encoding errors. 204 */ encode(DerOutputStream out)205 public void encode(DerOutputStream out) throws IOException { 206 out.putIA5String(uri.toASCIIString()); 207 } 208 209 /** 210 * Convert the name into user readable string. 211 */ toString()212 public String toString() { 213 return "URIName: " + uri.toString(); 214 } 215 216 /** 217 * Compares this name with another, for equality. 218 * 219 * @return true iff the names are equivalent according to RFC2459. 220 */ equals(Object obj)221 public boolean equals(Object obj) { 222 if (this == obj) { 223 return true; 224 } 225 226 if (!(obj instanceof URIName)) { 227 return false; 228 } 229 230 URIName other = (URIName) obj; 231 232 return uri.equals(other.getURI()); 233 } 234 235 /** 236 * Returns the URIName as a java.net.URI object 237 */ getURI()238 public URI getURI() { 239 return uri; 240 } 241 242 /** 243 * Returns this URI name. 244 */ getName()245 public String getName() { 246 return uri.toString(); 247 } 248 249 /** 250 * Return the scheme name portion of a URIName 251 * 252 * @returns scheme portion of full name 253 */ getScheme()254 public String getScheme() { 255 return uri.getScheme(); 256 } 257 258 /** 259 * Return the host name or IP address portion of the URIName 260 * 261 * @returns host name or IP address portion of full name 262 */ getHost()263 public String getHost() { 264 return host; 265 } 266 267 /** 268 * Return the host object type; if host name is a 269 * DNSName, then this host object does not include any 270 * initial "." on the name. 271 * 272 * @returns host name as DNSName or IPAddressName 273 */ getHostObject()274 public Object getHostObject() { 275 if (hostIP != null) { 276 return hostIP; 277 } else { 278 return hostDNS; 279 } 280 } 281 282 /** 283 * Returns the hash code value for this object. 284 * 285 * @return a hash code value for this object. 286 */ hashCode()287 public int hashCode() { 288 return uri.hashCode(); 289 } 290 291 /** 292 * Return type of constraint inputName places on this name:<ul> 293 * <li>NAME_DIFF_TYPE = -1: input name is different type from name 294 * (i.e. does not constrain). 295 * <li>NAME_MATCH = 0: input name matches name. 296 * <li>NAME_NARROWS = 1: input name narrows name (is lower in the naming 297 * subtree) 298 * <li>NAME_WIDENS = 2: input name widens name (is higher in the naming 299 * subtree) 300 * <li>NAME_SAME_TYPE = 3: input name does not match or narrow name, but 301 * is same type. 302 * </ul>. 303 * These results are used in checking NameConstraints during 304 * certification path verification. 305 * <p> 306 * RFC3280: For URIs, the constraint applies to the host part of the name. 307 * The constraint may specify a host or a domain. Examples would be 308 * "foo.bar.com"; and ".xyz.com". When the the constraint begins with 309 * a period, it may be expanded with one or more subdomains. That is, 310 * the constraint ".xyz.com" is satisfied by both abc.xyz.com and 311 * abc.def.xyz.com. However, the constraint ".xyz.com" is not satisfied 312 * by "xyz.com". When the constraint does not begin with a period, it 313 * specifies a host. 314 * <p> 315 * @param inputName to be checked for being constrained 316 * @returns constraint type above 317 * @throws UnsupportedOperationException if name is not exact match, but 318 * narrowing and widening are not supported for this name type. 319 */ constrains(GeneralNameInterface inputName)320 public int constrains(GeneralNameInterface inputName) 321 throws UnsupportedOperationException { 322 int constraintType; 323 if (inputName == null) { 324 constraintType = NAME_DIFF_TYPE; 325 } else if (inputName.getType() != NAME_URI) { 326 constraintType = NAME_DIFF_TYPE; 327 } else { 328 // Assuming from here on that one or both of these is 329 // actually a URI name constraint (not a URI), so we 330 // only need to compare the host portion of the name 331 332 String otherHost = ((URIName)inputName).getHost(); 333 334 // Quick check for equality 335 if (otherHost.equalsIgnoreCase(host)) { 336 constraintType = NAME_MATCH; 337 } else { 338 Object otherHostObject = ((URIName)inputName).getHostObject(); 339 340 if ((hostDNS == null) || 341 !(otherHostObject instanceof DNSName)) { 342 // If one (or both) is an IP address, only same type 343 constraintType = NAME_SAME_TYPE; 344 } else { 345 // Both host portions are DNS names. Are they domains? 346 boolean thisDomain = (host.charAt(0) == '.'); 347 boolean otherDomain = (otherHost.charAt(0) == '.'); 348 DNSName otherDNS = (DNSName) otherHostObject; 349 350 // Run DNSName.constrains. 351 constraintType = hostDNS.constrains(otherDNS); 352 // If neither one is a domain, then they can't 353 // widen or narrow. That's just SAME_TYPE. 354 if ((!thisDomain && !otherDomain) && 355 ((constraintType == NAME_WIDENS) || 356 (constraintType == NAME_NARROWS))) { 357 constraintType = NAME_SAME_TYPE; 358 } 359 360 // If one is a domain and the other isn't, 361 // then they can't match. The one that's a 362 // domain doesn't include the one that's 363 // not a domain. 364 if ((thisDomain != otherDomain) && 365 (constraintType == NAME_MATCH)) { 366 if (thisDomain) { 367 constraintType = NAME_WIDENS; 368 } else { 369 constraintType = NAME_NARROWS; 370 } 371 } 372 } 373 } 374 } 375 return constraintType; 376 } 377 378 /** 379 * Return subtree depth of this name for purposes of determining 380 * NameConstraints minimum and maximum bounds and for calculating 381 * path lengths in name subtrees. 382 * 383 * @returns distance of name from root 384 * @throws UnsupportedOperationException if not supported for this name type 385 */ subtreeDepth()386 public int subtreeDepth() throws UnsupportedOperationException { 387 DNSName dnsName = null; 388 try { 389 dnsName = new DNSName(host); 390 } catch (IOException ioe) { 391 throw new UnsupportedOperationException(ioe.getMessage()); 392 } 393 return dnsName.subtreeDepth(); 394 } 395 } 396