Frames | No Frames |
1: /* OID.java -- numeric representation of an object identifier 2: Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package gnu.java.security; 40: 41: import gnu.java.lang.CPStringBuilder; 42: 43: import gnu.java.security.der.DEREncodingException; 44: 45: import java.io.ByteArrayOutputStream; 46: import java.io.IOException; 47: import java.io.InputStream; 48: import java.util.StringTokenizer; 49: 50: /** 51: * This immutable class represents an object identifier, or OID. 52: * 53: * <p>OIDs are represented as a series of hierarchical tokens, each of 54: * which is usually represented as a single, unsigned integer. The 55: * hierarchy works so that later tokens are considered within the group 56: * of earlier tokens. Thus, the OID for the Serpent block cipher, 57: * 1.3.6.1.4.1.11591.13.2, is maintained by the GNU project, whose OID 58: * is 1.3.6.1.4.1.11591 (which is, in turn, part of bigger, more general 59: * bodies; the topmost, 1, stands for the OIDs assigned by the 60: * International Standards Organization, ISO). 61: * 62: * <p>OIDs can be represented in a variety of ways, including the 63: * dotted-decimal form we use here. 64: * 65: * <p>OIDs may be relative, in which case the first two elements of the 66: * OID are omitted. 67: * 68: * @author Casey Marshall (csm@gnu.org) 69: */ 70: public class OID implements Cloneable, Comparable, java.io.Serializable 71: { 72: 73: // Fields. 74: // ------------------------------------------------------------------------ 75: 76: /* Serial version id for serialization. */ 77: static final long serialVersionUID = 5722492029044597779L; 78: 79: /** 80: * The numeric ID structure. 81: */ 82: private int[] components; 83: 84: /** 85: * The string representation of this OID, in dotted-decimal format. 86: */ 87: private transient String strRep; 88: 89: /** 90: * The DER encoding of this OID. 91: */ 92: private transient byte[] der; 93: 94: /** 95: * Whether or not this OID is relative. 96: */ 97: private boolean relative; 98: 99: // Constructors. 100: // ------------------------------------------------------------------------ 101: 102: /** 103: * Create a new OID from the given byte array. The argument (which can 104: * neither be null nor zero-length) is copied to prevent subsequent 105: * modification. 106: * 107: * @param components The numeric IDs. 108: * @throws IllegalArgumentException If <i>components</i> is null or empty. 109: */ 110: public OID(int[] components) 111: { 112: this(components, false); 113: } 114: 115: /** 116: * Create a new OID from the given byte array. The argument (which can 117: * neither be null nor zero-length) is copied to prevent subsequent 118: * modification. 119: * 120: * @param components The numeric IDs. 121: * @param relative The relative flag. 122: * @throws IllegalArgumentException If <i>components</i> is null or empty. 123: */ 124: public OID(int[] components, boolean relative) 125: { 126: if (components == null || components.length == 0) 127: throw new IllegalArgumentException(); 128: this.components = (int[]) components.clone(); 129: this.relative = relative; 130: } 131: 132: /** 133: * Create a new OID from the given dotted-decimal representation. 134: * 135: * @param strRep The string representation of the OID. 136: * @throws IllegalArgumentException If the string does not contain at 137: * least one integer. 138: * @throws NumberFormatException If the string does not contain only 139: * numbers and periods ('.'). 140: */ 141: public OID(String strRep) 142: { 143: this(strRep, false); 144: } 145: 146: /** 147: * Create a new OID from the given dotted-decimal representation. 148: * 149: * @param strRep The string representation of the OID. 150: * @param relative The relative flag. 151: * @throws IllegalArgumentException If the string does not contain at 152: * least one integer. 153: * @throws NumberFormatException If the string does not contain only 154: * numbers and periods ('.'). 155: */ 156: public OID(String strRep, boolean relative) 157: { 158: this.relative = relative; 159: this.strRep = strRep; 160: components = fromString(strRep); 161: } 162: 163: /** 164: * Construct a new OID from the DER bytes in an input stream. This method 165: * does not read the tag or the length field from the input stream, so 166: * the caller must supply the number of octets in this OID's encoded 167: * form. 168: * 169: * @param derIn The DER input stream. 170: * @param len The number of bytes in the encoded form. 171: * @throws IOException If an error occurs reading the OID. 172: */ 173: public OID(InputStream derIn, int len) throws IOException 174: { 175: this(derIn, len, false); 176: } 177: 178: /** 179: * Construct a new OID from the DER bytes in an input stream. This method 180: * does not read the tag or the length field from the input stream, so 181: * the caller must supply the number of octets in this OID's encoded 182: * form. 183: * 184: * @param derIn The DER input stream. 185: * @param len The number of bytes in the encoded form. 186: * @param relative The relative flag. 187: * @throws IOException If an error occurs reading the OID. 188: */ 189: public OID(InputStream derIn, int len, boolean relative) throws IOException 190: { 191: der = new byte[len]; 192: derIn.read(der); 193: this.relative = relative; 194: try 195: { 196: components = fromDER(der, relative); 197: } 198: catch (ArrayIndexOutOfBoundsException aioobe) 199: { 200: aioobe.printStackTrace(); 201: throw aioobe; 202: } 203: } 204: 205: /** 206: * Construct a new OID from the given DER bytes. 207: * 208: * @param encoded The DER encoded OID. 209: * @throws IOException If an error occurs reading the OID. 210: */ 211: public OID(byte[] encoded) throws IOException 212: { 213: this(encoded, false); 214: } 215: 216: /** 217: * Construct a new OID from the given DER bytes. 218: * 219: * @param encoded The encoded relative OID. 220: * @param relative The relative flag. 221: */ 222: public OID(byte[] encoded, boolean relative) throws IOException 223: { 224: der = (byte[]) encoded.clone(); 225: this.relative = relative; 226: try 227: { 228: components = fromDER(der, relative); 229: } 230: catch (ArrayIndexOutOfBoundsException aioobe) 231: { 232: aioobe.printStackTrace(); 233: throw aioobe; 234: } 235: } 236: 237: // Instance methods. 238: // ------------------------------------------------------------------------ 239: 240: /** 241: * Return the numeric IDs of this OID. The value returned is copied to 242: * prevent modification. 243: * 244: * @return The IDs in a new integer array. 245: */ 246: public int[] getIDs() 247: { 248: return (int[]) components.clone(); 249: } 250: 251: /** 252: * Get the DER encoding of this OID, minus the tag and length fields. 253: * 254: * @return The DER bytes. 255: */ 256: public byte[] getDER() 257: { 258: if (der == null) 259: { 260: ByteArrayOutputStream bout = new ByteArrayOutputStream(); 261: int i = 0; 262: if (!relative) 263: { 264: int b = components[i++] * 40 + (components.length > 1 265: ? components[i++] : 0); 266: encodeSubID(bout, b); 267: } 268: for ( ; i < components.length; i++) 269: encodeSubID(bout, components[i]); 270: der = bout.toByteArray(); 271: } 272: return (byte[]) der.clone(); 273: } 274: 275: /** 276: * Get the parent OID of this OID. That is, if this OID is "1.2.3.4", 277: * then the parent OID will be "1.2.3". If this OID is a top-level 278: * OID, this method returns null. 279: * 280: * @return The parent OID, or null. 281: */ 282: public OID getParent() 283: { 284: if (components.length == 1) 285: return null; 286: int[] parent = new int[components.length - 1]; 287: System.arraycopy(components, 0, parent, 0, parent.length); 288: return new OID(parent); 289: } 290: 291: public OID getChild(int id) 292: { 293: int[] child = new int[components.length + 1]; 294: System.arraycopy(components, 0, child, 0, components.length); 295: child[child.length - 1] = id; 296: return new OID(child); 297: } 298: 299: /** 300: * Get the root OID of this OID. That is, the first two components. 301: * 302: * @return The root OID. 303: */ 304: public OID getRoot() 305: { 306: if (components.length <= 2) 307: return this; 308: int[] root = new int[2]; 309: root[0] = components[0]; 310: root[1] = components[1]; 311: return new OID(root); 312: } 313: 314: public boolean isRelative() 315: { 316: return relative; 317: } 318: 319: /** 320: * Returns a copy of this OID. 321: * 322: * @return The copy. 323: */ 324: public Object clone() 325: { 326: try 327: { 328: return super.clone(); 329: } 330: catch (CloneNotSupportedException cnse) 331: { 332: InternalError ie = new InternalError(); 333: ie.initCause(cnse); 334: throw ie; 335: } 336: } 337: 338: /* Nice idea, but possibly too expensive for whatever benefit it 339: * provides. 340: 341: public String getShortName() 342: { 343: return OIDTable.getShortName(this); 344: } 345: 346: public String getLongName() 347: { 348: return OIDTable.getLongName(this); 349: } 350: 351: */ 352: 353: /** 354: * Returns the value of this OID in dotted-decimal format. 355: * 356: * @return The string representation. 357: */ 358: public String toString() 359: { 360: if (strRep != null) 361: return strRep; 362: else 363: { 364: CPStringBuilder buf = new CPStringBuilder(); 365: for (int i = 0; i < components.length; i++) 366: { 367: buf.append((long) components[i] & 0xFFFFFFFFL); 368: if (i < components.length - 1) 369: buf.append('.'); 370: } 371: return (strRep = buf.toString()); 372: } 373: } 374: 375: /** 376: * Computes a hash code for this OID. 377: * 378: * @return The hash code. 379: */ 380: public int hashCode() 381: { 382: int ret = 0; 383: for (int i = 0; i < components.length; i++) 384: ret += components[i] << (i & 31); 385: return ret; 386: } 387: 388: /** 389: * Tests whether or not this OID equals another. 390: * 391: * @return Whether or not this OID equals the other. 392: */ 393: public boolean equals(Object o) 394: { 395: if (!(o instanceof OID)) 396: return false; 397: return java.util.Arrays.equals(components, ((OID) o).components); 398: } 399: 400: /** 401: * Compares this OID to another. The comparison is essentially 402: * lexicographic, where the two OIDs are compared until their 403: * first difference, then that difference is returned. If one OID is 404: * shorter, but all elements equal between the two for the shorter 405: * length, then the shorter OID is lesser than the longer. 406: * 407: * @param o The object to compare. 408: * @return An integer less than, equal to, or greater than zero if 409: * this object is less than, equal to, or greater than the 410: * argument. 411: * @throws ClassCastException If <i>o</i> is not an OID. 412: */ 413: public int compareTo(Object o) 414: { 415: if (equals(o)) 416: return 0; 417: int[] components2 = ((OID) o).components; 418: int len = Math.min(components.length, components2.length); 419: for (int i = 0; i < len; i++) 420: { 421: if (components[i] != components2[i]) 422: return (components[i] < components2[i]) ? -1 : 1; 423: } 424: if (components.length == components2.length) 425: return 0; 426: return (components.length < components2.length) ? -1 : 1; 427: } 428: 429: // Own methods. 430: // ------------------------------------------------------------------------ 431: 432: private static int[] fromDER(byte[] der, boolean relative) 433: throws DEREncodingException 434: { 435: // cannot be longer than this. 436: int[] components = new int[der.length + 1]; 437: int count = 0; 438: int i = 0; 439: if (!relative && i < der.length) 440: { 441: // Non-relative OIDs have the first two arcs coded as: 442: // 443: // i = first_arc * 40 + second_arc; 444: // 445: int j = (der[i] & 0xFF); 446: components[count++] = j / 40; 447: components[count++] = j % 40; 448: i++; 449: } 450: while (i < der.length) 451: { 452: int j = 0; 453: do 454: { 455: j = der[i++] & 0xFF; 456: components[count] <<= 7; 457: components[count] |= j & 0x7F; 458: if (i >= der.length && (j & 0x80) != 0) 459: throw new DEREncodingException("malformed OID"); 460: } 461: while ((j & 0x80) != 0); 462: count++; 463: } 464: if (count == components.length) 465: return components; 466: int[] ret = new int[count]; 467: System.arraycopy(components, 0, ret, 0, count); 468: return ret; 469: } 470: 471: private static int[] fromString(String strRep) throws NumberFormatException 472: { 473: if (strRep.startsWith("OID.") || strRep.startsWith("oid.")) 474: strRep = strRep.substring(4); 475: StringTokenizer tok = new StringTokenizer(strRep, "."); 476: if (tok.countTokens() == 0) 477: throw new IllegalArgumentException(); 478: int[] components = new int[tok.countTokens()]; 479: int i = 0; 480: while (tok.hasMoreTokens()) 481: { 482: components[i++] = Integer.parseInt(tok.nextToken()); 483: } 484: return components; 485: } 486: 487: private static void encodeSubID(ByteArrayOutputStream out, int id) 488: { 489: if (id < 128) 490: { 491: out.write(id); 492: } 493: else if (id < 16384) 494: { 495: out.write((id >>> 7) | 0x80); 496: out.write(id & 0x7F); 497: } 498: else if (id < 2097152) 499: { 500: out.write((id >>> 14) | 0x80); 501: out.write(((id >>> 7) | 0x80) & 0xFF); 502: out.write(id & 0x7F); 503: } 504: else if (id < 268435456) 505: { 506: out.write( (id >>> 21) | 0x80); 507: out.write(((id >>> 14) | 0x80) & 0xFF); 508: out.write(((id >>> 7) | 0x80) & 0xFF); 509: out.write(id & 0x7F); 510: } 511: } 512: }