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.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: }