View Javadoc

1   /*
2    *   Copyright 2005 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.asn1new.primitives;
18  
19  import java.io.Serializable;
20  
21  import org.apache.asn1.codec.DecoderException;
22  
23  
24  /***
25   * This class implement an OID (Object Identifier).
26   * 
27   * An OID is encoded as a list of bytes representing integers. 
28   * 
29   * An OID has a numeric representation where number are separated with dots :
30   * SPNEGO Oid = 1.3.6.1.5.5.2
31   * 
32   * Translating from a byte list to a dot separated list of number follows the rules :
33   * - the first number is in [0..2]
34   * - the second number is in [0..39] if the first number is 0 or 1
35   * - the first byte has a value equal to : number 1 * 40 + number two
36   * - the upper bit of a byte is set if the next byte is a part of the number
37   * 
38   * For instance, the SPNEGO Oid (1.3.6.1.5.5.2) will be encoded : 
39   * 1.3 -> 0x2B (1*40 + 3 = 43 = 0x2B) 
40   * .6  -> 0x06 
41   * .1  -> 0x01 
42   * .5  -> 0x05 
43   * .5  -> 0x05 
44   * .2  -> 0x02 
45   * 
46   * The Kerberos V5 Oid (1.2.840.48018.1.2.2)  will be encoded :
47   * 1.2   -> 0x2A (1*40 + 2 = 42 = 0x2A) 
48   * 840   -> 0x86 0x48 (840 = 6 * 128 + 72 = (0x06 | 0x80) 0x48 = 0x86 0x48
49   * 48018 -> 0x82 0xF7 0x12 (2 * 128 * 128 + 119 * 128 + 18 = (0x02 | 0x80) (0x77 | 0x80) 0x12
50   * 
51   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
52   */
53  public class OID implements Serializable
54  {
55      private static final long serialVersionUID = 1L;
56      
57      //~ Instance fields ----------------------------------------------------------------------------
58  
59      /*** The OID as a array of int */
60      private int[] oidValues;
61  
62      //~ Constructors -------------------------------------------------------------------------------
63  
64      /***
65       * Creates a new OID object.
66       */
67      public OID()
68      {
69  
70          // We should not create this kind of object directly, it must
71          // be created through the factory.
72      }
73  
74      /***
75       * Create a new OID object from a byte array
76       * @param oid
77       */
78      public OID( byte[] oid ) throws DecoderException
79      {
80          setOID(oid);
81      }
82  
83      /***
84       * Create a new OID object from a String
85       * @param oid The String which is supposed to be an OID
86       */
87      public OID( String oid ) throws DecoderException
88      {
89          setOID( oid );
90      }
91  
92      //~ Methods ------------------------------------------------------------------------------------
93      /***
94       * Set the OID. It will be translated from a byte array to an internal representation.
95       * @param oid The bytes containing the OID
96       */
97      public void setOID( byte[] oid ) throws DecoderException
98      {
99  
100         if ( oid == null )
101         {
102             throw new DecoderException( "Null OID" );
103         }
104 
105         if ( oid.length < 1 )
106         {
107             throw new DecoderException( "Invalid OID : " + oid );
108         }
109 
110         // First, we have to calculate the number of int to allocate
111         int nbValues = 1;
112 
113         int pos      = 0;
114 
115         while ( pos < oid.length )
116         {
117 
118             if ( oid[pos] >= 0 )
119             {
120                 nbValues++;
121             }
122 
123             pos++;
124         }
125 
126         oidValues = new int[nbValues];
127 
128         nbValues  = 0;
129         pos       = 0;
130 
131         int accumulator = 0;
132 
133 
134         if ( ( oid[0] < 0 ) || ( oid[0] >= 80 ) )
135         {
136             oidValues[nbValues++] = 2;
137 
138             while ( pos < oid.length )
139             {
140 
141                 if ( oid[pos] >= 0 )
142                 {
143                     oidValues[nbValues++] = ( ( accumulator << 7 ) + oid[pos] ) - 80;
144                     accumulator           = 0;
145                     pos++;
146                     break;
147                 }
148                 else
149                 {
150                     accumulator = ( accumulator << 7 ) + ( oid[pos] & 0x007F );
151                 }
152 
153                 pos++;
154             }
155         }
156         else if ( oid[0] < 40 )
157         {
158             oidValues[nbValues++] = 0;
159             oidValues[nbValues++] = oid[pos++]; // itu-t
160         }
161         else // oid[0] is < 80
162         {
163             oidValues[nbValues++] = 1;
164             oidValues[nbValues++] = oid[pos++] - 40; // iso
165         }
166 
167         while ( pos < oid.length )
168         {
169 
170             if ( oid[pos] >= 0 )
171             {
172                 oidValues[nbValues++] = ( accumulator << 7 ) + oid[pos];
173                 accumulator           = 0;
174             }
175             else
176             {
177                 accumulator = ( accumulator << 7 ) + ( oid[pos] & 0x007F );
178             }
179 
180             pos++;
181         }
182     }
183 
184     /***
185      * Set the OID. It will be translated from a String to an internal representation.
186      * The syntax will be controled in respect with this rule :
187      *  OID = ( [ '0' | '1' ] '.' [ 0 .. 39 ] | '2' '.' int) ( '.' int )* 
188      * @param oid The String containing the OID
189      */
190     public void setOID( String oid ) throws DecoderException
191     {
192 
193         if ( ( oid == null ) || ( oid.length() == 0 ) )
194         {
195             throw new DecoderException( "Null OID" );
196         }
197 
198         int     nbInts  = 1;
199         byte[]  bytes   = oid.getBytes();
200         boolean dotSeen = false;
201 
202         // Count the number of int to allocate.
203         for ( int i = 0; i < bytes.length; i++ )
204         {
205 
206             if ( bytes[i] == '.' )
207             {
208 
209                 if ( dotSeen )
210                 {
211 
212                     // Two dots, that's an error !
213                     throw new DecoderException( "Invalid OID : " + oid );
214                 }
215 
216                 nbInts++;
217                 dotSeen = true;
218             }
219             else
220             {
221                 dotSeen = false;
222             }
223         }
224 
225         // We must have at least 2 ints
226         if ( nbInts < 2 )
227         {
228             throw new DecoderException( "Invalid OID : " + oid );
229         }
230 
231         oidValues = new int[nbInts];
232 
233         int pos    = 0;
234         int intPos = 0;
235         
236         // This flag is used to forbid a second value above 39 if the 
237         // first value is 0 or 1 (itu_t or iso arcs)
238         boolean ituOrIso = false;
239 
240         // The first value
241         switch ( bytes[pos] )
242         {
243 
244             case '0' : // itu-t
245             case '1' : // iso
246                 ituOrIso = true;
247                 // fallthrough
248                 
249             case '2' : // joint-iso-itu-t
250                 oidValues[intPos++] = bytes[pos++] - '0';
251                 break;
252 
253             default : // error, this value is not allowed
254                 throw new DecoderException( "Invalid OID : " + oid );
255         }
256 
257         // We must have a dot
258         if ( bytes[pos++] != '.' )
259         {
260             throw new DecoderException( "Invalid OID : " + oid );
261         }
262 
263         dotSeen = true;
264 
265         int value = 0;
266 
267         for ( int i = pos; i < bytes.length; i++ )
268         {
269 
270             if ( bytes[i] == '.' )
271             {
272 
273                 if ( dotSeen )
274                 {
275 
276                     // Two dots, that's an error !
277                     throw new DecoderException( "Invalid OID : " + oid );
278                 }
279 
280                 if (ituOrIso && value > 39)
281                 {
282                     throw new DecoderException( "Invalid OID : " + oid );
283                 }
284                 else
285                 {
286                     ituOrIso = false;
287                 }
288                 
289                 nbInts++;
290                 dotSeen             = true;
291                 oidValues[intPos++] = value;
292                 value               = 0;
293             }
294             else if ( ( bytes[i] >= 0x30 ) && ( bytes[i] <= 0x39 ) )
295             {
296                 dotSeen = false;
297                 value   = ( ( value * 10 ) + bytes[i] ) - '0';
298                 
299             }
300             else
301             {
302 
303                 // We don't have a number, this is an error
304                 throw new DecoderException( "Invalid OID : " + oid );
305             }
306         }
307 
308         oidValues[intPos++] = value;
309     }
310 
311     /***
312      * Get an array of int from the OID
313      * @return An array of int representing the OID
314      */
315     public int[] getOIDValues()
316     {
317         return oidValues;
318     }
319 
320     /***
321      * Get the number of bytes necessary to store the OID
322      * @return An int representing the length of the OID
323      */
324     public int getOIDLength()
325     {
326         int value = oidValues[0] * 40 + oidValues[1];
327         int nbBytes = 0;
328 
329         if (value < 128)
330         {
331             nbBytes = 1;
332         } 
333         else if (value < 16384)
334         {
335             nbBytes = 2;
336         }
337         else if (value < 2097152)
338         {
339             nbBytes = 3;
340         }
341         else if (value < 268435456)
342         {
343             nbBytes = 4;
344         }
345         else 
346         {
347             nbBytes = 5;
348         }
349 
350         for (int i = 2; i < oidValues.length; i++ )
351         {
352             value = oidValues[i];
353             
354             if (value < 128)
355             {
356                 nbBytes += 1;
357             } 
358             else if (value < 16384)
359             {
360                 nbBytes += 2;
361             }
362             else if (value < 2097152)
363             {
364                 nbBytes += 3;
365             }
366             else if (value < 268435456)
367             {
368                 nbBytes += 4;
369             }
370             else 
371             {
372                 nbBytes += 5;
373             }
374         }
375         
376         return nbBytes;
377     }
378 
379     /***
380      * Get an array of bytes from the OID
381      * @return An array of int representing the OID
382      */
383     public byte[] getOID()
384     {
385         int value = oidValues[0] * 40 + oidValues[1];
386         int firstValues = value;
387         
388         byte[] bytes = new byte[getOIDLength()];
389         int pos = 0;
390         
391         if (oidValues[0] < 2)
392         {
393             bytes[pos++] = (byte)(oidValues[0] * 40 + oidValues[1]);
394         } 
395         else
396         {
397             if (firstValues < 128)
398             {
399                 bytes[pos++] = (byte)(firstValues);
400             } 
401             else if (firstValues < 16384)
402             {
403                 bytes[pos++] = (byte)( ( firstValues >> 7 ) | 0x0080 );
404                 bytes[pos++] = (byte)( firstValues & 0x007F);
405             }
406             else if (value < 2097152)
407             {
408                 bytes[pos++] = (byte)( ( firstValues >> 14 ) | 0x0080);
409                 bytes[pos++] = (byte)( ( ( firstValues >> 7 ) & 0x007F) | 0x0080);
410                 bytes[pos++] = (byte)( firstValues & 0x007F);
411             }
412             else if (value < 268435456)
413             {
414                 bytes[pos++] = (byte)( ( firstValues >> 21 ) | 0x0080);
415                 bytes[pos++] = (byte)( ( ( firstValues >> 14 ) & 0x007F) | 0x0080);
416                 bytes[pos++] = (byte)( ( ( firstValues >> 7 ) & 0x007F) | 0x0080);
417                 bytes[pos++] = (byte)( firstValues & 0x007F);
418             }
419             else 
420             {
421                 bytes[pos++] = (byte)( ( firstValues >> 28 ) | 0x0080);
422                 bytes[pos++] = (byte)( ( ( firstValues >> 21 ) & 0x007F) | 0x0080);
423                 bytes[pos++] = (byte)( ( ( firstValues >> 14 ) & 0x007F) | 0x0080);
424                 bytes[pos++] = (byte)( ( ( firstValues >> 7 ) & 0x007F) | 0x0080);
425                 bytes[pos++] = (byte)( firstValues & 0x007F);
426             }
427         }
428         
429         for (int i = 2; i < oidValues.length; i++ )
430         {
431             value = oidValues[i];
432             
433             if (value < 128)
434             {
435                 bytes[pos++] = (byte)(value);
436             } 
437             else if (value < 16384)
438             {
439                 bytes[pos++] = (byte)( ( value >> 7 ) | 0x0080 );
440                 bytes[pos++] = (byte)( value & 0x007F);
441             }
442             else if (value < 2097152)
443             {
444                 bytes[pos++] = (byte)( ( value >> 14 ) | 0x0080);
445                 bytes[pos++] = (byte)( ( ( value >> 7 ) & 0x007F) | 0x0080);
446                 bytes[pos++] = (byte)( value & 0x007F);
447             }
448             else if (value < 268435456)
449             {
450                 bytes[pos++] = (byte)( ( value >> 21 ) | 0x0080);
451                 bytes[pos++] = (byte)( ( ( value >> 14 ) & 0x007F) | 0x0080);
452                 bytes[pos++] = (byte)( ( ( value >> 7 ) & 0x007F) | 0x0080);
453                 bytes[pos++] = (byte)( value & 0x007F);
454             }
455             else 
456             {
457                 bytes[pos++] = (byte)( ( value >> 28 ) | 0x0080);
458                 bytes[pos++] = (byte)( ( ( value >> 21 ) & 0x007F) | 0x0080);
459                 bytes[pos++] = (byte)( ( ( value >> 14 ) & 0x007F) | 0x0080);
460                 bytes[pos++] = (byte)( ( ( value >> 7 ) & 0x007F) | 0x0080);
461                 bytes[pos++] = (byte)( value & 0x007F);
462             }
463         }
464         
465         return bytes;
466     }
467 
468     /***
469      * Get the OID as a String
470      * @return A String representing the OID
471      */
472     public String toString()
473     {
474 
475         StringBuffer sb = new StringBuffer();
476         
477         if (oidValues != null)
478         {
479 	        sb.append( oidValues[0] );
480 	
481 	        for ( int i = 1; i < oidValues.length; i++ )
482 	        {
483 	            sb.append( '.' ).append( oidValues[i] );
484 	        }
485         }
486 
487         return sb.toString();
488     }
489 }