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