Source for gnu.java.security.OID

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