1 /*
   2  * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.util;
  27 
  28 import java.io.*;
  29 import java.math.BigInteger;
  30 import java.util.Arrays;
  31 
  32 /**
  33  * Represent an ISO Object Identifier.
  34  *
  35  * <P>Object Identifiers are arbitrary length hierarchical identifiers.
  36  * The individual components are numbers, and they define paths from the
  37  * root of an ISO-managed identifier space.  You will sometimes see a
  38  * string name used instead of (or in addition to) the numerical id.
  39  * These are synonyms for the numerical IDs, but are not widely used
  40  * since most sites do not know all the requisite strings, while all
  41  * sites can parse the numeric forms.
  42  *
  43  * <P>So for example, JavaSoft has the sole authority to assign the
  44  * meaning to identifiers below the 1.3.6.1.4.1.42.2.17 node in the
  45  * hierarchy, and other organizations can easily acquire the ability
  46  * to assign such unique identifiers.
  47  *
  48  * @author David Brownell
  49  * @author Amit Kapoor
  50  * @author Hemma Prafullchandra
  51  */
  52 
  53 final public
  54 class ObjectIdentifier implements Serializable
  55 {
  56     /**
  57      * We use the DER value (no tag, no length) as the internal format
  58      * @serial
  59      */
  60     private byte[] encoding = null;
  61 
  62     private transient volatile String stringForm;
  63 
  64     /*
  65      * IMPORTANT NOTES FOR CODE CHANGES (bug 4811968) IN JDK 1.7.0
  66      * ===========================================================
  67      *
  68      * (Almost) serialization compatibility with old versions:
  69      *
  70      * serialVersionUID is unchanged. Old field "component" is changed to
  71      * type Object so that "poison" (unknown object type for old versions)
  72      * can be put inside if there are huge components that cannot be saved
  73      * as integers.
  74      *
  75      * New version use the new filed "encoding" only.
  76      *
  77      * Below are all 4 cases in a serialization/deserialization process:
  78      *
  79      * 1. old -> old: Not covered here
  80      * 2. old -> new: There's no "encoding" field, new readObject() reads
  81      *    "components" and "componentLen" instead and inits correctly.
  82      * 3. new -> new: "encoding" field exists, new readObject() uses it
  83      *    (ignoring the other 2 fields) and inits correctly.
  84      * 4. new -> old: old readObject() only recognizes "components" and
  85      *    "componentLen" fields. If no huge components are involved, they
  86      *    are serialized as legal values and old object can init correctly.
  87      *    Otherwise, old object cannot recognize the form (component not int[])
  88      *    and throw a ClassNotFoundException at deserialization time.
  89      *
  90      * Therfore, for the first 3 cases, exact compatibility is preserved. In
  91      * the 4th case, non-huge OID is still supportable in old versions, while
  92      * huge OID is not.
  93      */
  94     private static final long serialVersionUID = 8697030238860181294L;
  95 
  96     /**
  97      * Changed to Object
  98      * @serial
  99      */
 100     private Object      components   = null;          // path from root
 101     /**
 102      * @serial
 103      */
 104     private int         componentLen = -1;            // how much is used.
 105 
 106     // Is the components field calculated?
 107     transient private boolean   componentsCalculated = false;
 108 
 109     private void readObject(ObjectInputStream is)
 110             throws IOException, ClassNotFoundException {
 111         is.defaultReadObject();
 112 
 113         if (encoding == null) {  // from an old version
 114             int[] comp = (int[])components;
 115             if (componentLen > comp.length) {
 116                 componentLen = comp.length;
 117             }
 118             init(comp, componentLen);
 119         }
 120     }
 121 
 122     private void writeObject(ObjectOutputStream os)
 123             throws IOException {
 124         if (!componentsCalculated) {
 125             int[] comps = toIntArray();
 126             if (comps != null) {    // every one understands this
 127                 components = comps;
 128                 componentLen = comps.length;
 129             } else {
 130                 components = HugeOidNotSupportedByOldJDK.theOne;
 131             }
 132             componentsCalculated = true;
 133         }
 134         os.defaultWriteObject();
 135     }
 136 
 137     static class HugeOidNotSupportedByOldJDK implements Serializable {
 138         private static final long serialVersionUID = 1L;
 139         static HugeOidNotSupportedByOldJDK theOne = new HugeOidNotSupportedByOldJDK();
 140     }
 141 
 142     /**
 143      * Constructs, from a string.  This string should be of the form 1.23.56.
 144      * Validity check included.
 145      */
 146     public ObjectIdentifier (String oid) throws IOException
 147     {
 148         int ch = '.';
 149         int start = 0;
 150         int end = 0;
 151 
 152         int pos = 0;
 153         byte[] tmp = new byte[oid.length()];
 154         int first = 0, second;
 155         int count = 0;
 156 
 157         try {
 158             String comp = null;
 159             do {
 160                 int length = 0; // length of one section
 161                 end = oid.indexOf(ch,start);
 162                 if (end == -1) {
 163                     comp = oid.substring(start);
 164                     length = oid.length() - start;
 165                 } else {
 166                     comp = oid.substring(start,end);
 167                     length = end - start;
 168                 }
 169 
 170                 if (length > 9) {
 171                     BigInteger bignum = new BigInteger(comp);
 172                     if (count == 0) {
 173                         checkFirstComponent(bignum);
 174                         first = bignum.intValue();
 175                     } else {
 176                         if (count == 1) {
 177                             checkSecondComponent(first, bignum);
 178                             bignum = bignum.add(BigInteger.valueOf(40*first));
 179                         } else {
 180                             checkOtherComponent(count, bignum);
 181                         }
 182                         pos += pack7Oid(bignum, tmp, pos);
 183                     }
 184                 } else {
 185                     int num = Integer.parseInt(comp);
 186                     if (count == 0) {
 187                         checkFirstComponent(num);
 188                         first = num;
 189                     } else {
 190                         if (count == 1) {
 191                             checkSecondComponent(first, num);
 192                             num += 40 * first;
 193                         } else {
 194                             checkOtherComponent(count, num);
 195                         }
 196                         pos += pack7Oid(num, tmp, pos);
 197                     }
 198                 }
 199                 start = end + 1;
 200                 count++;
 201             } while (end != -1);
 202 
 203             checkCount(count);
 204             encoding = new byte[pos];
 205             System.arraycopy(tmp, 0, encoding, 0, pos);
 206             this.stringForm = oid;
 207         } catch (IOException ioe) { // already detected by checkXXX
 208             throw ioe;
 209         } catch (Exception e) {
 210             throw new IOException("ObjectIdentifier() -- Invalid format: "
 211                     + e.toString(), e);
 212         }
 213     }
 214 
 215     /**
 216      * Constructor, from an array of integers.
 217      * Validity check included.
 218      */
 219     public ObjectIdentifier (int values []) throws IOException
 220     {
 221         checkCount(values.length);
 222         checkFirstComponent(values[0]);
 223         checkSecondComponent(values[0], values[1]);
 224         for (int i=2; i<values.length; i++)
 225             checkOtherComponent(i, values[i]);
 226         init(values, values.length);
 227     }
 228 
 229     /**
 230      * Constructor, from an ASN.1 encoded input stream.
 231      * Validity check NOT included.
 232      * The encoding of the ID in the stream uses "DER", a BER/1 subset.
 233      * In this case, that means a triple { typeId, length, data }.
 234      *
 235      * <P><STRONG>NOTE:</STRONG>  When an exception is thrown, the
 236      * input stream has not been returned to its "initial" state.
 237      *
 238      * @param in DER-encoded data holding an object ID
 239      * @exception IOException indicates a decoding error
 240      */
 241     public ObjectIdentifier (DerInputStream in) throws IOException
 242     {
 243         byte    type_id;
 244         int     bufferEnd;
 245 
 246         /*
 247          * Object IDs are a "universal" type, and their tag needs only
 248          * one byte of encoding.  Verify that the tag of this datum
 249          * is that of an object ID.
 250          *
 251          * Then get and check the length of the ID's encoding.  We set
 252          * up so that we can use in.available() to check for the end of
 253          * this value in the data stream.
 254          */
 255         type_id = (byte) in.getByte ();
 256         if (type_id != DerValue.tag_ObjectId)
 257             throw new IOException (
 258                 "ObjectIdentifier() -- data isn't an object ID"
 259                 + " (tag = " +  type_id + ")"
 260                 );
 261 
 262         int len = in.getLength();
 263         if (len > in.available()) {
 264             throw new IOException("ObjectIdentifier() -- length exceeds" +
 265                     "data available.  Length: " + len + ", Available: " +
 266                     in.available());
 267         }
 268         encoding = new byte[len];
 269         in.getBytes(encoding);
 270         check(encoding);
 271     }
 272 
 273     /*
 274      * Constructor, from the rest of a DER input buffer;
 275      * the tag and length have been removed/verified
 276      * Validity check NOT included.
 277      */
 278     ObjectIdentifier (DerInputBuffer buf) throws IOException
 279     {
 280         DerInputStream in = new DerInputStream(buf);
 281         encoding = new byte[in.available()];
 282         in.getBytes(encoding);
 283         check(encoding);
 284     }
 285 
 286     private void init(int[] components, int length) {
 287         int pos = 0;
 288         byte[] tmp = new byte[length*5+1];  // +1 for empty input
 289 
 290         if (components[1] < Integer.MAX_VALUE - components[0]*40)
 291             pos += pack7Oid(components[0]*40+components[1], tmp, pos);
 292         else {
 293             BigInteger big = BigInteger.valueOf(components[1]);
 294             big = big.add(BigInteger.valueOf(components[0]*40));
 295             pos += pack7Oid(big, tmp, pos);
 296         }
 297 
 298         for (int i=2; i<length; i++) {
 299             pos += pack7Oid(components[i], tmp, pos);
 300         }
 301         encoding = new byte[pos];
 302         System.arraycopy(tmp, 0, encoding, 0, pos);
 303     }
 304 
 305     /**
 306      * This method is kept for compatibility reasons. The new implementation
 307      * does the check and conversion. All around the JDK, the method is called
 308      * in static blocks to initialize pre-defined ObjectIdentifieies. No
 309      * obvious performance hurt will be made after this change.
 310      *
 311      * Old doc: Create a new ObjectIdentifier for internal use. The values are
 312      * neither checked nor cloned.
 313      */
 314     public static ObjectIdentifier newInternal(int[] values) {
 315         try {
 316             return new ObjectIdentifier(values);
 317         } catch (IOException ex) {
 318             throw new RuntimeException(ex);
 319             // Should not happen, internal calls always uses legal values.
 320         }
 321     }
 322 
 323     /*
 324      * n.b. the only public interface is DerOutputStream.putOID()
 325      */
 326     void encode (DerOutputStream out) throws IOException
 327     {
 328         out.write (DerValue.tag_ObjectId, encoding);
 329     }
 330 
 331     /**
 332      * @deprecated Use equals((Object)oid)
 333      */
 334     @Deprecated
 335     public boolean equals(ObjectIdentifier other) {
 336         return equals((Object)other);
 337     }
 338 
 339     /**
 340      * Compares this identifier with another, for equality.
 341      *
 342      * @return true iff the names are identical.
 343      */
 344     @Override
 345     public boolean equals(Object obj) {
 346         if (this == obj) {
 347             return true;
 348         }
 349         if (obj instanceof ObjectIdentifier == false) {
 350             return false;
 351         }
 352         ObjectIdentifier other = (ObjectIdentifier)obj;
 353         return Arrays.equals(encoding, other.encoding);
 354     }
 355 
 356     @Override
 357     public int hashCode() {
 358         return Arrays.hashCode(encoding);
 359     }
 360 
 361     /**
 362      * Private helper method for serialization. To be compatible with old
 363      * versions of JDK.
 364      * @return components in an int array, if all the components are less than
 365      *         Integer.MAX_VALUE. Otherwise, null.
 366      */
 367     private int[] toIntArray() {
 368         int length = encoding.length;
 369         int[] result = new int[20];
 370         int which = 0;
 371         int fromPos = 0;
 372         for (int i = 0; i < length; i++) {
 373             if ((encoding[i] & 0x80) == 0) {
 374                 // one section [fromPos..i]
 375                 if (i - fromPos + 1 > 4) {
 376                     BigInteger big = new BigInteger(pack(encoding, fromPos, i-fromPos+1, 7, 8));
 377                     if (fromPos == 0) {
 378                         result[which++] = 2;
 379                         BigInteger second = big.subtract(BigInteger.valueOf(80));
 380                         if (second.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) == 1) {
 381                             return null;
 382                         } else {
 383                             result[which++] = second.intValue();
 384                         }
 385                     } else {
 386                         if (big.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) == 1) {
 387                             return null;
 388                         } else {
 389                             result[which++] = big.intValue();
 390                         }
 391                     }
 392                 } else {
 393                     int retval = 0;
 394                     for (int j = fromPos; j <= i; j++) {
 395                         retval <<= 7;
 396                         byte tmp = encoding[j];
 397                         retval |= (tmp & 0x07f);
 398                     }
 399                     if (fromPos == 0) {
 400                         if (retval < 80) {
 401                             result[which++] = retval / 40;
 402                             result[which++] = retval % 40;
 403                         } else {
 404                             result[which++] = 2;
 405                             result[which++] = retval - 80;
 406                         }
 407                     } else {
 408                         result[which++] = retval;
 409                     }
 410                 }
 411                 fromPos = i+1;
 412             }
 413             if (which >= result.length) {
 414                 result = Arrays.copyOf(result, which + 10);
 415             }
 416         }
 417         return Arrays.copyOf(result, which);
 418     }
 419 
 420     /**
 421      * Returns a string form of the object ID.  The format is the
 422      * conventional "dot" notation for such IDs, without any
 423      * user-friendly descriptive strings, since those strings
 424      * will not be understood everywhere.
 425      */
 426     @Override
 427     public String toString() {
 428         String s = stringForm;
 429         if (s == null) {
 430             int length = encoding.length;
 431             StringBuffer sb = new StringBuffer(length * 4);
 432 
 433             int fromPos = 0;
 434             for (int i = 0; i < length; i++) {
 435                 if ((encoding[i] & 0x80) == 0) {
 436                     // one section [fromPos..i]
 437                     if (fromPos != 0) {  // not the first segment
 438                         sb.append('.');
 439                     }
 440                     if (i - fromPos + 1 > 4) { // maybe big integer
 441                         BigInteger big = new BigInteger(pack(encoding, fromPos, i-fromPos+1, 7, 8));
 442                         if (fromPos == 0) {
 443                             // first section encoded with more than 4 bytes,
 444                             // must be 2.something
 445                             sb.append("2.");
 446                             sb.append(big.subtract(BigInteger.valueOf(80)));
 447                         } else {
 448                             sb.append(big);
 449                         }
 450                     } else { // small integer
 451                         int retval = 0;
 452                         for (int j = fromPos; j <= i; j++) {
 453                             retval <<= 7;
 454                             byte tmp = encoding[j];
 455                             retval |= (tmp & 0x07f);
 456                         }
 457                         if (fromPos == 0) {
 458                             if (retval < 80) {
 459                                 sb.append(retval/40);
 460                                 sb.append('.');
 461                                 sb.append(retval%40);
 462                             } else {
 463                                 sb.append("2.");
 464                                 sb.append(retval - 80);
 465                             }
 466                         } else {
 467                             sb.append(retval);
 468                         }
 469                     }
 470                     fromPos = i+1;
 471                 }
 472             }
 473             s = sb.toString();
 474             stringForm = s;
 475         }
 476         return s;
 477     }
 478 
 479     /**
 480      * Repack all bits from input to output. On the both sides, only a portion
 481      * (from the least significant bit) of the 8 bits in a byte is used. This
 482      * number is defined as the number of useful bits (NUB) for the array. All the
 483      * used bits from the input byte array and repacked into the output in the
 484      * exactly same order. The output bits are aligned so that the final bit of
 485      * the input (the least significant bit in the last byte), when repacked as
 486      * the final bit of the output, is still at the least significant position.
 487      * Zeroes will be padded on the left side of the first output byte if
 488      * necessary. All unused bits in the output are also zeroed.
 489      *
 490      * For example: if the input is 01001100 with NUB 8, the output which
 491      * has a NUB 6 will look like:
 492      *      00000001 00001100
 493      * The first 2 bits of the output bytes are unused bits. The other bits
 494      * turn out to be 000001 001100. While the 8 bits on the right are from
 495      * the input, the left 4 zeroes are padded to fill the 6 bits space.
 496      *
 497      * @param in        the input byte array
 498      * @param ioffset   start point inside <code>in</code>
 499      * @param ilength   number of bytes to repack
 500      * @param iw        NUB for input
 501      * @param ow        NUB for output
 502      * @return          the repacked bytes
 503      */
 504     private static byte[] pack(byte[] in, int ioffset, int ilength, int iw, int ow) {
 505         assert (iw > 0 && iw <= 8): "input NUB must be between 1 and 8";
 506         assert (ow > 0 && ow <= 8): "output NUB must be between 1 and 8";
 507 
 508         if (iw == ow) {
 509             return in.clone();
 510         }
 511 
 512         int bits = ilength * iw;    // number of all used bits
 513         byte[] out = new byte[(bits+ow-1)/ow];
 514 
 515         // starting from the 0th bit in the input
 516         int ipos = 0;
 517 
 518         // the number of padding 0's needed in the output, skip them
 519         int opos = (bits+ow-1)/ow*ow-bits;
 520 
 521         while(ipos < bits) {
 522             int count = iw - ipos%iw;   // unpacked bits in current input byte
 523             if (count > ow - opos%ow) { // free space available in output byte
 524                 count = ow - opos%ow;   // choose the smaller number
 525             }
 526             // and move them!
 527             out[opos/ow] |=                         // paste!
 528                 (((in[ioffset+ipos/iw]+256)         // locate the byte (+256 so that it's never negative)
 529                     >> (iw-ipos%iw-count))          // move to the end of a byte
 530                         & ((1 << (count))-1))       // zero out all other bits
 531                             << (ow-opos%ow-count);  // move to the output position
 532             ipos += count;  // advance
 533             opos += count;  // advance
 534         }
 535         return out;
 536     }
 537 
 538     /**
 539      * Repack from NUB 8 to a NUB 7 OID sub-identifier, remove all
 540      * unnecessary 0 headings, set the first bit of all non-tail
 541      * output bytes to 1 (as ITU-T Rec. X.690 8.19.2 says), and
 542      * paste it into an existing byte array.
 543      * @param out the existing array to be pasted into
 544      * @param ooffset the starting position to paste
 545      * @return the number of bytes pasted
 546      */
 547     private static int pack7Oid(byte[] in, int ioffset, int ilength, byte[] out, int ooffset) {
 548         byte[] pack = pack(in, ioffset, ilength, 8, 7);
 549         int firstNonZero = pack.length-1;   // paste at least one byte
 550         for (int i=pack.length-2; i>=0; i--) {
 551             if (pack[i] != 0) {
 552                 firstNonZero = i;
 553             }
 554             pack[i] |= 0x80;
 555         }
 556         System.arraycopy(pack, firstNonZero, out, ooffset, pack.length-firstNonZero);
 557         return pack.length-firstNonZero;
 558     }
 559 
 560     /**
 561      * Repack from NUB 7 to NUB 8, remove all unnecessary 0
 562      * headings, and paste it into an existing byte array.
 563      * @param out the existing array to be pasted into
 564      * @param ooffset the starting position to paste
 565      * @return the number of bytes pasted
 566      */
 567     private static int pack8(byte[] in, int ioffset, int ilength, byte[] out, int ooffset) {
 568         byte[] pack = pack(in, ioffset, ilength, 7, 8);
 569         int firstNonZero = pack.length-1;   // paste at least one byte
 570         for (int i=pack.length-2; i>=0; i--) {
 571             if (pack[i] != 0) {
 572                 firstNonZero = i;
 573             }
 574         }
 575         System.arraycopy(pack, firstNonZero, out, ooffset, pack.length-firstNonZero);
 576         return pack.length-firstNonZero;
 577     }
 578 
 579     /**
 580      * Pack the int into a OID sub-identifier DER encoding
 581      */
 582     private static int pack7Oid(int input, byte[] out, int ooffset) {
 583         byte[] b = new byte[4];
 584         b[0] = (byte)(input >> 24);
 585         b[1] = (byte)(input >> 16);
 586         b[2] = (byte)(input >> 8);
 587         b[3] = (byte)(input);
 588         return pack7Oid(b, 0, 4, out, ooffset);
 589     }
 590 
 591     /**
 592      * Pack the BigInteger into a OID subidentifier DER encoding
 593      */
 594     private static int pack7Oid(BigInteger input, byte[] out, int ooffset) {
 595         byte[] b = input.toByteArray();
 596         return pack7Oid(b, 0, b.length, out, ooffset);
 597     }
 598 
 599     /**
 600      * Private methods to check validity of OID. They must be --
 601      * 1. at least 2 components
 602      * 2. all components must be non-negative
 603      * 3. the first must be 0, 1 or 2
 604      * 4. if the first is 0 or 1, the second must be <40
 605      */
 606 
 607     /**
 608      * Check the DER encoding. Since DER encoding defines that the integer bits
 609      * are unsigned, so there's no need to check the MSB.
 610      */
 611     private static void check(byte[] encoding) throws IOException {
 612         int length = encoding.length;
 613         if (length < 1 ||      // too short
 614                 (encoding[length - 1] & 0x80) != 0) {  // not ended
 615             throw new IOException("ObjectIdentifier() -- " +
 616                     "Invalid DER encoding, not ended");
 617         }
 618         for (int i=0; i<length; i++) {
 619             // 0x80 at the beginning of a subidentifier
 620             if (encoding[i] == (byte)0x80 &&
 621                     (i==0 || (encoding[i-1] & 0x80) == 0)) {
 622                 throw new IOException("ObjectIdentifier() -- " +
 623                         "Invalid DER encoding, useless extra octet detected");
 624             }
 625         }
 626     }
 627     private static void checkCount(int count) throws IOException {
 628         if (count < 2) {
 629             throw new IOException("ObjectIdentifier() -- " +
 630                     "Must be at least two oid components ");
 631         }
 632     }
 633     private static void checkFirstComponent(int first) throws IOException {
 634         if (first < 0 || first > 2) {
 635             throw new IOException("ObjectIdentifier() -- " +
 636                     "First oid component is invalid ");
 637         }
 638     }
 639     private static void checkFirstComponent(BigInteger first) throws IOException {
 640         if (first.signum() == -1 ||
 641                 first.compareTo(BigInteger.valueOf(2)) == 1) {
 642             throw new IOException("ObjectIdentifier() -- " +
 643                     "First oid component is invalid ");
 644         }
 645     }
 646     private static void checkSecondComponent(int first, int second) throws IOException {
 647         if (second < 0 || first != 2 && second > 39) {
 648             throw new IOException("ObjectIdentifier() -- " +
 649                     "Second oid component is invalid ");
 650         }
 651     }
 652     private static void checkSecondComponent(int first, BigInteger second) throws IOException {
 653         if (second.signum() == -1 ||
 654                 first != 2 &&
 655                 second.compareTo(BigInteger.valueOf(39)) == 1) {
 656             throw new IOException("ObjectIdentifier() -- " +
 657                     "Second oid component is invalid ");
 658         }
 659     }
 660     private static void checkOtherComponent(int i, int num) throws IOException {
 661         if (num < 0) {
 662             throw new IOException("ObjectIdentifier() -- " +
 663                     "oid component #" + (i+1) + " must be non-negative ");
 664         }
 665     }
 666     private static void checkOtherComponent(int i, BigInteger num) throws IOException {
 667         if (num.signum() == -1) {
 668             throw new IOException("ObjectIdentifier() -- " +
 669                     "oid component #" + (i+1) + " must be non-negative ");
 670         }
 671     }
 672 }