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