001: /*
002: * @(#)DNSName.java 1.16 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.security.x509;
029:
030: import java.io.IOException;
031:
032: import sun.security.util.*;
033:
034: /**
035: * This class implements the DNSName as required by the GeneralNames
036: * ASN.1 object.
037: * <p>
038: * [RFC2459] When the subjectAltName extension contains a domain name service
039: * label, the domain name MUST be stored in the dNSName (an IA5String).
040: * The name MUST be in the "preferred name syntax," as specified by RFC
041: * 1034 [RFC 1034]. Note that while upper and lower case letters are
042: * allowed in domain names, no signifigance is attached to the case. In
043: * addition, while the string " " is a legal domain name, subjectAltName
044: * extensions with a dNSName " " are not permitted. Finally, the use of
045: * the DNS representation for Internet mail addresses (wpolk.nist.gov
046: * instead of wpolk@nist.gov) is not permitted; such identities are to
047: * be encoded as rfc822Name.
048: * <p>
049: * @author Amit Kapoor
050: * @author Hemma Prafullchandra
051: * @version 1.9
052: */
053: public class DNSName implements GeneralNameInterface {
054: private String name;
055:
056: private static final String alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
057: private static final String digitsAndHyphen = "0123456789-";
058: private static final String alphaDigitsAndHyphen = alpha
059: + digitsAndHyphen;
060:
061: /**
062: * Create the DNSName object from the passed encoded Der value.
063: *
064: * @param derValue the encoded DER DNSName.
065: * @exception IOException on error.
066: */
067: public DNSName(DerValue derValue) throws IOException {
068: name = derValue.getIA5String();
069: }
070:
071: /**
072: * Create the DNSName object with the specified name.
073: *
074: * @param name the DNSName.
075: * @throws IOException if the name is not a valid DNSName subjectAltName
076: */
077: public DNSName(String name) throws IOException {
078: if (name == null || name.length() == 0)
079: throw new IOException("DNS name must not be null");
080: if (name.indexOf(' ') != -1)
081: throw new IOException(
082: "DNS names or NameConstraints with blank components are not permitted");
083: if (name.charAt(0) == '.'
084: || name.charAt(name.length() - 1) == '.')
085: throw new IOException(
086: "DNS names or NameConstraints may not begin or end with a .");
087: //Name will consist of label components separated by "."
088: //startIndex is the index of the first character of a component
089: //endIndex is the index of the last character of a component plus 1
090: for (int endIndex, startIndex = 0; startIndex < name.length(); startIndex = endIndex + 1) {
091: endIndex = name.indexOf('.', startIndex);
092: if (endIndex < 0) {
093: endIndex = name.length();
094: }
095: if ((endIndex - startIndex) < 1)
096: throw new IOException(
097: "DNSName SubjectAltNames with empty components are not permitted");
098:
099: //DNSName components must begin with a letter A-Z or a-z
100: if (alpha.indexOf(name.charAt(startIndex)) < 0)
101: throw new IOException(
102: "DNSName components must begin with a letter");
103: //nonStartIndex: index for characters in the component beyond the first one
104: for (int nonStartIndex = startIndex + 1; nonStartIndex < endIndex; nonStartIndex++) {
105: char x = name.charAt(nonStartIndex);
106: if ((alphaDigitsAndHyphen).indexOf(x) < 0)
107: throw new IOException(
108: "DNSName components must consist of letters, digits, and hyphens");
109: }
110: }
111: this .name = name;
112: }
113:
114: /**
115: * Return the type of the GeneralName.
116: */
117: public int getType() {
118: return (GeneralNameInterface.NAME_DNS);
119: }
120:
121: /**
122: * Return the actual name value of the GeneralName.
123: */
124: public String getName() {
125: return name;
126: }
127:
128: /**
129: * Encode the DNS name into the DerOutputStream.
130: *
131: * @param out the DER stream to encode the DNSName to.
132: * @exception IOException on encoding errors.
133: */
134: public void encode(DerOutputStream out) throws IOException {
135: out.putIA5String(name);
136: }
137:
138: /**
139: * Convert the name into user readable string.
140: */
141: public String toString() {
142: return ("DNSName: " + name);
143: }
144:
145: /**
146: * Compares this name with another, for equality.
147: *
148: * @return true iff the names are equivalent
149: * according to RFC2459.
150: */
151: public boolean equals(Object obj) {
152: if (this == obj)
153: return true;
154:
155: if (!(obj instanceof DNSName))
156: return false;
157:
158: DNSName other = (DNSName) obj;
159:
160: // RFC2459 mandates that these names are
161: // not case-sensitive
162: return name.equalsIgnoreCase(other.name);
163: }
164:
165: /**
166: * Returns the hash code value for this object.
167: *
168: * @return a hash code value for this object.
169: */
170: public int hashCode() {
171: return name.toUpperCase().hashCode();
172: }
173:
174: /**
175: * Return type of constraint inputName places on this name:<ul>
176: * <li>NAME_DIFF_TYPE = -1: input name is different type from name (i.e. does not constrain).
177: * <li>NAME_MATCH = 0: input name matches name.
178: * <li>NAME_NARROWS = 1: input name narrows name (is lower in the naming subtree)
179: * <li>NAME_WIDENS = 2: input name widens name (is higher in the naming subtree)
180: * <li>NAME_SAME_TYPE = 3: input name does not match or narrow name, but is same type.
181: * </ul>. These results are used in checking NameConstraints during
182: * certification path verification.
183: * <p>
184: * RFC2459: DNS name restrictions are expressed as foo.bar.com. Any subdomain
185: * satisfies the name constraint. For example, www.foo.bar.com would
186: * satisfy the constraint but bigfoo.bar.com would not.
187: * <p>
188: * draft-ietf-pkix-new-part1-00.txt: DNS name restrictions are expressed as foo.bar.com.
189: * Any DNS name that
190: * can be constructed by simply adding to the left hand side of the name
191: * satisfies the name constraint. For example, www.foo.bar.com would
192: * satisfy the constraint but foo1.bar.com would not.
193: * <p>
194: * RFC1034: By convention, domain names can be stored with arbitrary case, but
195: * domain name comparisons for all present domain functions are done in a
196: * case-insensitive manner, assuming an ASCII character set, and a high
197: * order zero bit.
198: * <p>
199: * @param inputName to be checked for being constrained
200: * @returns constraint type above
201: * @throws UnsupportedOperationException if name is not exact match, but narrowing and widening are
202: * not supported for this name type.
203: */
204: public int constrains(GeneralNameInterface inputName)
205: throws UnsupportedOperationException {
206: int constraintType;
207: if (inputName == null)
208: constraintType = NAME_DIFF_TYPE;
209: else if (inputName.getType() != NAME_DNS)
210: constraintType = NAME_DIFF_TYPE;
211: else {
212: String inName = (((DNSName) inputName).getName())
213: .toLowerCase();
214: String this Name = name.toLowerCase();
215: if (inName.equals(this Name))
216: constraintType = NAME_MATCH;
217: else if (this Name.endsWith(inName)) {
218: int inNdx = this Name.lastIndexOf(inName);
219: if (this Name.charAt(inNdx - 1) == '.')
220: constraintType = NAME_WIDENS;
221: else
222: constraintType = NAME_SAME_TYPE;
223: } else if (inName.endsWith(this Name)) {
224: int ndx = inName.lastIndexOf(this Name);
225: if (inName.charAt(ndx - 1) == '.')
226: constraintType = NAME_NARROWS;
227: else
228: constraintType = NAME_SAME_TYPE;
229: } else {
230: constraintType = NAME_SAME_TYPE;
231: }
232: }
233: return constraintType;
234: }
235:
236: /**
237: * Return subtree depth of this name for purposes of determining
238: * NameConstraints minimum and maximum bounds and for calculating
239: * path lengths in name subtrees.
240: *
241: * @returns distance of name from root
242: * @throws UnsupportedOperationException if not supported for this name type
243: */
244: public int subtreeDepth() throws UnsupportedOperationException {
245: String subtree = name;
246: int i = 1;
247:
248: /* count dots */
249: for (; subtree.lastIndexOf('.') >= 0; i++) {
250: subtree = subtree.substring(0, subtree.lastIndexOf('.'));
251: }
252:
253: return i;
254: }
255:
256: }
|