Source for gnu.java.security.OID

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