001: /*
002: * @(#)IPAddressName.java 1.15 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: import java.lang.Integer;
032: import java.net.InetAddress;
033: import java.util.Arrays;
034: import sun.misc.HexDumpEncoder;
035: import sun.security.util.BitArray;
036: import sun.security.util.DerOutputStream;
037: import sun.security.util.DerValue;
038:
039: /**
040: * This class implements the IPAddressName as required by the GeneralNames
041: * ASN.1 object. Both IPv4 and IPv6 addresses are supported using the
042: * formats specified in IETF PKIX RFC2459.
043: * <p>
044: * [RFC2459 4.2.1.7 Subject Alternative Name]
045: * When the subjectAltName extension contains a iPAddress, the address
046: * MUST be stored in the octet string in "network byte order," as
047: * specified in RFC 791. The least significant bit (LSB) of
048: * each octet is the LSB of the corresponding byte in the network
049: * address. For IP Version 4, as specified in RFC 791, the octet string
050: * MUST contain exactly four octets. For IP Version 6, as specified in
051: * RFC 1883, the octet string MUST contain exactly sixteen octets.
052: * <p>
053: * [RFC2459 4.2.1.11 Name Constraints]
054: * The syntax of iPAddress MUST be as described in section 4.2.1.7 with
055: * the following additions specifically for Name Constraints. For IPv4
056: * addresses, the ipAddress field of generalName MUST contain eight (8)
057: * octets, encoded in the style of RFC 1519 (CIDR) to represent an
058: * address range.[RFC 1519] For IPv6 addresses, the ipAddress field
059: * MUST contain 32 octets similarly encoded. For example, a name
060: * constraint for "class C" subnet 10.9.8.0 shall be represented as the
061: * octets 0A 09 08 00 FF FF FF 00, representing the CIDR notation
062: * 10.9.8.0/255.255.255.0.
063: * <p>
064: * @see GeneralName
065: * @see GeneralNameInterface
066: * @see GeneralNames
067: *
068: * @version 1.8
069: *
070: * @author Amit Kapoor
071: * @author Hemma Prafullchandra
072: */
073: public class IPAddressName implements GeneralNameInterface {
074: private byte[] address;
075: private boolean isIPv4;
076: private String name;
077:
078: /**
079: * Create the IPAddressName object from the passed encoded Der value.
080: *
081: * @params derValue the encoded DER IPAddressName.
082: * @exception IOException on error.
083: */
084: public IPAddressName(DerValue derValue) throws IOException {
085: this (derValue.getOctetString());
086: }
087:
088: /**
089: * Create the IPAddressName object with the specified octets.
090: *
091: * @params address the IP address
092: * @throws IOException if address is not a valid IPv4 or IPv6 address
093: */
094: public IPAddressName(byte[] address) throws IOException {
095: /*
096: * A valid address must consist of 4 bytes of address and
097: * optional 4 bytes of 4 bytes of mask, or 16 bytes of address
098: * and optional 16 bytes of mask.
099: */
100: if (address.length == 4 || address.length == 8) {
101: isIPv4 = true;
102: } else if (address.length == 16 || address.length == 32) {
103: isIPv4 = false;
104: } else {
105: throw new IOException("Invalid IPAddressName");
106: }
107: this .address = address;
108: }
109:
110: /**
111: * Create an IPAddressName from a String.
112: * [IETF RFC1338 Supernetting & IETF RFC1519 Classless Inter-Domain
113: * Routing (CIDR)] For IPv4 addresses, the forms are
114: * "b1.b2.b3.b4" or "b1.b2.b3.b4/m1.m2.m3.m4", where b1 - b4 are decimal
115: * byte values 0-255 and m1 - m4 are decimal mask values
116: * 0 - 255.
117: * <p>
118: * [IETF RFC2373 IP Version 6 Addressing Architecture]
119: * For IPv6 addresses, the forms are "a1:a2:...:a8" or "a1:a2:...:a8/n",
120: * where a1-a8 are hexadecimal values representing the eight 16-bit pieces
121: * of the address. If /n is used, n is a decimal number indicating how many
122: * of the leftmost contiguous bits of the address comprise the prefix for
123: * this subnet. Internally, a mask value is created using the prefix length.
124: * <p>
125: * @param name String form of IPAddressName
126: * @throws IOException if name can not be converted to a valid IPv4 or IPv6
127: * address
128: */
129: public IPAddressName(String name) throws IOException {
130:
131: if (name == null || name.length() == 0) {
132: throw new IOException("IPAddress cannot be null or empty");
133: }
134: if (name.charAt(name.length() - 1) == '/') {
135: throw new IOException("Invalid IPAddress: " + name);
136: }
137:
138: if (name.indexOf(':') >= 0) {
139: // name is IPv6: uses colons as value separators
140: // Parse name into byte-value address components and optional
141: // prefix
142: parseIPv6(name);
143: isIPv4 = false;
144: } else if (name.indexOf('.') >= 0) {
145: //name is IPv4: uses dots as value separators
146: parseIPv4(name);
147: isIPv4 = true;
148: } else {
149: throw new IOException("Invalid IPAddress: " + name);
150: }
151: }
152:
153: /**
154: * Parse an IPv4 address.
155: *
156: * @param name IPv4 address with optional mask values
157: * @throws IOException on error
158: */
159: private void parseIPv4(String name) throws IOException {
160:
161: // Parse name into byte-value address components
162: int slashNdx = name.indexOf('/');
163: if (slashNdx == -1) {
164: address = InetAddress.getByName(name).getAddress();
165: } else {
166: address = new byte[8];
167:
168: // parse mask
169: byte[] mask = InetAddress.getByName(
170: name.substring(slashNdx + 1)).getAddress();
171:
172: // parse base address
173: byte[] host = InetAddress.getByName(
174: name.substring(0, slashNdx)).getAddress();
175:
176: System.arraycopy(host, 0, address, 0, 4);
177: System.arraycopy(mask, 0, address, 4, 4);
178: }
179: }
180:
181: /**
182: * Parse an IPv6 address.
183: *
184: * @param name String IPv6 address with optional /<prefix length>
185: * If /<prefix length> is present, address[] array will
186: * be 32 bytes long, otherwise 16.
187: * @throws IOException on error
188: */
189: private final static int MASKSIZE = 16;
190:
191: private void parseIPv6(String name) throws IOException {
192:
193: int slashNdx = name.indexOf('/');
194: if (slashNdx == -1) {
195: address = InetAddress.getByName(name).getAddress();
196: } else {
197: address = new byte[32];
198: byte[] base = InetAddress.getByName(
199: name.substring(0, slashNdx)).getAddress();
200: System.arraycopy(base, 0, address, 0, 16);
201:
202: // append a mask corresponding to the num of prefix bits specified
203: int prefixLen = Integer.parseInt(name
204: .substring(slashNdx + 1));
205: if (prefixLen > 128)
206: throw new IOException(
207: "IPv6Address prefix is longer than 128");
208:
209: // create new bit array initialized to zeros
210: BitArray bitArray = new BitArray(MASKSIZE * 8);
211:
212: // set all most significant bits up to prefix length
213: for (int i = 0; i < prefixLen; i++)
214: bitArray.set(i, true);
215: byte[] maskArray = bitArray.toByteArray();
216:
217: // copy mask bytes into mask portion of address
218: for (int i = 0; i < MASKSIZE; i++)
219: address[MASKSIZE + i] = maskArray[i];
220: }
221: }
222:
223: /**
224: * Return the type of the GeneralName.
225: */
226: public int getType() {
227: return NAME_IP;
228: }
229:
230: /**
231: * Encode the IPAddress name into the DerOutputStream.
232: *
233: * @params out the DER stream to encode the IPAddressName to.
234: * @exception IOException on encoding errors.
235: */
236: public void encode(DerOutputStream out) throws IOException {
237: out.putOctetString(address);
238: }
239:
240: /**
241: * Return a printable string of IPaddress
242: */
243: public String toString() {
244: try {
245: return "IPAddress: " + getName();
246: } catch (IOException ioe) {
247: // dump out hex rep for debugging purposes
248: HexDumpEncoder enc = new HexDumpEncoder();
249: return "IPAddress: " + enc.encodeBuffer(address);
250: }
251: }
252:
253: /**
254: * Return a standard String representation of IPAddress.
255: * See IPAddressName(String) for the formats used for IPv4
256: * and IPv6 addresses.
257: *
258: * @throws IOException if the IPAddress cannot be converted to a String
259: */
260: public String getName() throws IOException {
261: if (name != null)
262: return name;
263:
264: if (isIPv4) {
265: //IPv4 address or subdomain
266: byte[] host = new byte[4];
267: System.arraycopy(address, 0, host, 0, 4);
268: name = InetAddress.getByAddress(host).getHostAddress();
269: if (address.length == 8) {
270: byte[] mask = new byte[4];
271: System.arraycopy(address, 4, mask, 0, 4);
272: name = name
273: + "/"
274: + InetAddress.getByAddress(mask)
275: .getHostAddress();
276: }
277: } else {
278: //IPv6 address or subdomain
279: byte[] host = new byte[16];
280: System.arraycopy(address, 0, host, 0, 16);
281: name = InetAddress.getByAddress(host).getHostAddress();
282: if (address.length == 32) {
283: // IPv6 subdomain: display prefix length
284:
285: // copy subdomain into new array and convert to BitArray
286: byte[] maskBytes = new byte[16];
287: for (int i = 16; i < 32; i++)
288: maskBytes[i - 16] = address[i];
289: BitArray ba = new BitArray(16 * 8, maskBytes);
290: // Find first zero bit
291: int i = 0;
292: for (; i < 16 * 8; i++) {
293: if (!ba.get(i))
294: break;
295: }
296: name = name + "/" + i;
297: // Verify remaining bits 0
298: for (; i < 16 * 8; i++) {
299: if (ba.get(i)) {
300: throw new IOException(
301: "Invalid IPv6 subdomain - set "
302: + "bit " + i
303: + " not contiguous");
304: }
305: }
306: }
307: }
308: return name;
309: }
310:
311: /**
312: * Returns this IPAddress name as a byte array.
313: */
314: public byte[] getBytes() {
315: return (byte[]) address.clone();
316: }
317:
318: /**
319: * Compares this name with another, for equality.
320: *
321: * @return true iff the names are identical.
322: */
323: public boolean equals(Object obj) {
324: if (this == obj)
325: return true;
326:
327: if (!(obj instanceof IPAddressName))
328: return false;
329:
330: byte[] other = ((IPAddressName) obj).getBytes();
331:
332: if (other.length != address.length)
333: return false;
334:
335: if (address.length == 8 || address.length == 32) {
336: // Two subnet addresses
337: // Mask each and compare masked values
338: int maskLen = address.length / 2;
339: byte[] maskedThis = new byte[maskLen];
340: byte[] maskedOther = new byte[maskLen];
341: for (int i = 0; i < maskLen; i++) {
342: maskedThis[i] = (byte) (address[i] & address[i
343: + maskLen]);
344: maskedOther[i] = (byte) (other[i] & other[i + maskLen]);
345: if (maskedThis[i] != maskedOther[i]) {
346: return false;
347: }
348: }
349: // Now compare masks
350: for (int i = maskLen; i < address.length; i++)
351: if (address[i] != other[i])
352: return false;
353: return true;
354: } else {
355: // Two IPv4 host addresses or two IPv6 host addresses
356: // Compare bytes
357: return Arrays.equals(other, address);
358: }
359: }
360:
361: /**
362: * Returns the hash code value for this object.
363: *
364: * @return a hash code value for this object.
365: */
366: public int hashCode() {
367: int retval = 0;
368:
369: for (int i = 0; i < address.length; i++)
370: retval += address[i] * i;
371:
372: return retval;
373: }
374:
375: /**
376: * Return type of constraint inputName places on this name:<ul>
377: * <li>NAME_DIFF_TYPE = -1: input name is different type from name
378: * (i.e. does not constrain).
379: * <li>NAME_MATCH = 0: input name matches name.
380: * <li>NAME_NARROWS = 1: input name narrows name (is lower in the naming
381: * subtree)
382: * <li>NAME_WIDENS = 2: input name widens name (is higher in the naming
383: * subtree)
384: * <li>NAME_SAME_TYPE = 3: input name does not match or narrow name, but
385: * is same type.
386: * </ul>. These results are used in checking NameConstraints during
387: * certification path verification.
388: * <p>
389: * [RFC2459] The syntax of iPAddress MUST be as described in section
390: * 4.2.1.7 with the following additions specifically for Name Constraints.
391: * For IPv4 addresses, the ipAddress field of generalName MUST contain
392: * eight (8) octets, encoded in the style of RFC 1519 (CIDR) to represent an
393: * address range.[RFC 1519] For IPv6 addresses, the ipAddress field
394: * MUST contain 32 octets similarly encoded. For example, a name
395: * constraint for "class C" subnet 10.9.8.0 shall be represented as the
396: * octets 0A 09 08 00 FF FF FF 00, representing the CIDR notation
397: * 10.9.8.0/255.255.255.0.
398: * <p>
399: * @param inputName to be checked for being constrained
400: * @returns constraint type above
401: * @throws UnsupportedOperationException if name is not exact match, but
402: * narrowing and widening are not supported for this name type.
403: */
404: public int constrains(GeneralNameInterface inputName)
405: throws UnsupportedOperationException {
406: int constraintType;
407: if (inputName == null)
408: constraintType = NAME_DIFF_TYPE;
409: else if (inputName.getType() != NAME_IP)
410: constraintType = NAME_DIFF_TYPE;
411: else if (((IPAddressName) inputName).equals(this ))
412: constraintType = NAME_MATCH;
413: else {
414: byte[] otherAddress = ((IPAddressName) inputName)
415: .getBytes();
416: if (otherAddress.length == 4 && address.length == 4)
417: // Two host addresses
418: constraintType = NAME_SAME_TYPE;
419: else if ((otherAddress.length == 8 && address.length == 8)
420: || (otherAddress.length == 32 && address.length == 32)) {
421: // Two subnet addresses
422: // See if one address fully encloses the other address
423: boolean otherSubsetOfThis = true;
424: boolean this SubsetOfOther = true;
425: boolean this Empty = false;
426: boolean otherEmpty = false;
427: int maskOffset = address.length / 2;
428: for (int i = 0; i < maskOffset; i++) {
429: if ((byte) (address[i] & address[i + maskOffset]) != address[i])
430: this Empty = true;
431: if ((byte) (otherAddress[i] & otherAddress[i
432: + maskOffset]) != otherAddress[i])
433: otherEmpty = true;
434: if (!(((byte) (address[i + maskOffset] & otherAddress[i
435: + maskOffset]) == address[i + maskOffset]) && ((byte) (address[i] & address[i
436: + maskOffset]) == (byte) (otherAddress[i] & address[i
437: + maskOffset])))) {
438: otherSubsetOfThis = false;
439: }
440: if (!(((byte) (otherAddress[i + maskOffset] & address[i
441: + maskOffset]) == otherAddress[i
442: + maskOffset]) && ((byte) (otherAddress[i] & otherAddress[i
443: + maskOffset]) == (byte) (address[i] & otherAddress[i
444: + maskOffset])))) {
445: this SubsetOfOther = false;
446: }
447: }
448: if (this Empty || otherEmpty) {
449: if (this Empty && otherEmpty)
450: constraintType = NAME_MATCH;
451: else if (this Empty)
452: constraintType = NAME_WIDENS;
453: else
454: constraintType = NAME_NARROWS;
455: } else if (otherSubsetOfThis)
456: constraintType = NAME_NARROWS;
457: else if (this SubsetOfOther)
458: constraintType = NAME_WIDENS;
459: else
460: constraintType = NAME_SAME_TYPE;
461: } else if (otherAddress.length == 8
462: || otherAddress.length == 32) {
463: //Other is a subnet, this is a host address
464: int i = 0;
465: int maskOffset = otherAddress.length / 2;
466: for (; i < maskOffset; i++) {
467: // Mask this address by other address mask and compare to other address
468: // If all match, then this address is in other address subnet
469: if ((address[i] & otherAddress[i + maskOffset]) != otherAddress[i])
470: break;
471: }
472: if (i == maskOffset)
473: constraintType = NAME_WIDENS;
474: else
475: constraintType = NAME_SAME_TYPE;
476: } else if (address.length == 8 || address.length == 32) {
477: //This is a subnet, other is a host address
478: int i = 0;
479: int maskOffset = address.length / 2;
480: for (; i < maskOffset; i++) {
481: // Mask other address by this address mask and compare to this address
482: if ((otherAddress[i] & address[i + maskOffset]) != address[i])
483: break;
484: }
485: if (i == maskOffset)
486: constraintType = NAME_NARROWS;
487: else
488: constraintType = NAME_SAME_TYPE;
489: } else {
490: constraintType = NAME_SAME_TYPE;
491: }
492: }
493: return constraintType;
494: }
495:
496: /**
497: * Return subtree depth of this name for purposes of determining
498: * NameConstraints minimum and maximum bounds and for calculating
499: * path lengths in name subtrees.
500: *
501: * @returns distance of name from root
502: * @throws UnsupportedOperationException if not supported for this name type
503: */
504: public int subtreeDepth() throws UnsupportedOperationException {
505: throw new UnsupportedOperationException(
506: "subtreeDepth() not defined for IPAddressName");
507: }
508: }
|