1 /* OID.java -- numeric representation of an object identifier
2 Copyright (C) 2003, 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu
.java
.security
;
41 import gnu
.java
.security
.der
.DEREncodingException
;
43 import java
.io
.ByteArrayOutputStream
;
44 import java
.io
.InputStream
;
45 import java
.io
.IOException
;
46 import java
.util
.StringTokenizer
;
49 * This immutable class represents an object identifier, or OID.
51 * <p>OIDs are represented as a series of hierarcical tokens, each of
52 * which is usually represented as a single, unsigned integer. The
53 * hierarchy works so that later tokens are considered within the group
54 * of earlier tokens. Thus, the OID for the Serpent block cipher,
55 * 1.3.6.1.4.1.11591.13.2, is maintained by the GNU project, whose OID
56 * is 1.3.6.1.4.1.11591 (which is, in turn, part of bigger, more general
57 * bodies; the topmost, 1, stands for the OIDs assigned by the
58 * International Standards Organization, ISO).
60 * <p>OIDs can be represented in a variety of ways, including the
61 * dotted-decimal form we use here.
63 * <p>OIDs may be relative, in which case the first two elements of the
66 * @author Casey Marshall (csm@gnu.org)
68 public class OID
implements Cloneable
, Comparable
, java
.io
.Serializable
72 // ------------------------------------------------------------------------
75 * The numeric ID structure.
77 private int[] components
;
80 * The string representation of this OID, in dotted-decimal format.
82 private transient String strRep
;
85 * The DER encoding of this OID.
87 private transient byte[] der
;
90 * Whether or not this OID is relative.
92 private boolean relative
;
95 // ------------------------------------------------------------------------
98 * Create a new OID from the given byte array. The argument (which can
99 * neither be null nor zero-length) is copied to prevent subsequent
102 * @param components The numeric IDs.
103 * @throws IllegalArgumentException If <i>components</i> is null or empty.
105 public OID(int[] components
)
107 this(components
, false);
111 * Create a new OID from the given byte array. The argument (which can
112 * neither be null nor zero-length) is copied to prevent subsequent
115 * @param components The numeric IDs.
116 * @param relative The relative flag.
117 * @throws IllegalArgumentException If <i>components</i> is null or empty.
119 public OID(int[] components
, boolean relative
)
121 if (components
== null || components
.length
== 0)
122 throw new IllegalArgumentException();
123 this.components
= (int[]) components
.clone();
124 this.relative
= relative
;
128 * Create a new OID from the given dotted-decimal representation.
130 * @param strRep The string representation of the OID.
131 * @throws IllegalArgumentException If the string does not contain at
133 * @throws NumberFormatException If the string does not contain only
134 * numbers and periods ('.').
136 public OID(String strRep
)
142 * Create a new OID from the given dotted-decimal representation.
144 * @param strRep The string representation of the OID.
145 * @param relative The relative flag.
146 * @throws IllegalArgumentException If the string does not contain at
148 * @throws NumberFormatException If the string does not contain only
149 * numbers and periods ('.').
151 public OID(String strRep
, boolean relative
)
153 this.relative
= relative
;
154 this.strRep
= strRep
;
155 components
= fromString(strRep
);
159 * Construct a new OID from the DER bytes in an input stream. This method
160 * does not read the tag or the length field from the input stream, so
161 * the caller must supply the number of octets in this OID's encoded
164 * @param derIn The DER input stream.
165 * @param len The number of bytes in the encoded form.
166 * @throws IOException If an error occurs reading the OID.
168 public OID(InputStream derIn
, int len
) throws IOException
170 this(derIn
, len
, false);
174 * Construct a new OID from the DER bytes in an input stream. This method
175 * does not read the tag or the length field from the input stream, so
176 * the caller must supply the number of octets in this OID's encoded
179 * @param derIn The DER input stream.
180 * @param len The number of bytes in the encoded form.
181 * @param relative The relative flag.
182 * @throws IOException If an error occurs reading the OID.
184 public OID(InputStream derIn
, int len
, boolean relative
) throws IOException
188 this.relative
= relative
;
191 components
= fromDER(der
, relative
);
193 catch (ArrayIndexOutOfBoundsException aioobe
)
195 aioobe
.printStackTrace();
201 * Construct a new OID from the given DER bytes.
203 * @param encoded The DER encoded OID.
204 * @throws IOException If an error occurs reading the OID.
206 public OID(byte[] encoded
) throws IOException
208 this(encoded
, false);
212 * Construct a new OID from the given DER bytes.
214 * @param root The root OID.
215 * @param encoded The encoded relative OID.
216 * @param relative The relative flag.
218 public OID(byte[] encoded
, boolean relative
) throws IOException
220 der
= (byte[]) encoded
.clone();
221 this.relative
= relative
;
224 components
= fromDER(der
, relative
);
226 catch (ArrayIndexOutOfBoundsException aioobe
)
228 aioobe
.printStackTrace();
234 * Our private constructor.
241 // ------------------------------------------------------------------------
244 * Return the numeric IDs of this OID. The value returned is copied to
245 * prevent modification.
247 * @return The IDs in a new integer array.
249 public int[] getIDs()
251 return (int[]) components
.clone();
255 * Get the DER encoding of this OID, minus the tag and length fields.
257 * @return The DER bytes.
259 public byte[] getDER()
263 ByteArrayOutputStream bout
= new ByteArrayOutputStream();
267 int b
= components
[i
++] * 40 + (components
.length
> 1
268 ? components
[i
++] : 0);
269 encodeSubID(bout
, b
);
271 for ( ; i
< components
.length
; i
++)
272 encodeSubID(bout
, components
[i
]);
273 der
= bout
.toByteArray();
275 return (byte[]) der
.clone();
279 * Get the parent OID of this OID. That is, if this OID is "1.2.3.4",
280 * then the parent OID will be "1.2.3". If this OID is a top-level
281 * OID, this method returns null.
283 * @return The parent OID, or null.
285 public OID
getParent()
287 if (components
.length
== 1)
289 int[] parent
= new int[components
.length
- 1];
290 System
.arraycopy(components
, 0, parent
, 0, parent
.length
);
291 return new OID(parent
);
294 public OID
getChild(int id
)
296 int[] child
= new int[components
.length
+ 1];
297 System
.arraycopy(components
, 0, child
, 0, components
.length
);
298 child
[child
.length
- 1] = id
;
299 return new OID(child
);
303 * Get the root OID of this OID. That is, the first two components.
305 * @return The root OID.
309 if (components
.length
<= 2)
311 int[] root
= new int[2];
312 root
[0] = components
[0];
313 root
[1] = components
[1];
314 return new OID(root
);
317 public boolean isRelative()
323 * Returns a copy of this OID.
327 public Object
clone()
330 oid
.components
= this.components
;
331 oid
.strRep
= this.strRep
;
335 /* Nice idea, but possibly too expensive for whatever benefit it
338 public String getShortName()
340 return OIDTable.getShortName(this);
343 public String getLongName()
345 return OIDTable.getLongName(this);
351 * Returns the value of this OID in dotted-decimal format.
353 * @return The string representation.
355 public String
toString()
361 StringBuffer buf
= new StringBuffer();
362 for (int i
= 0; i
< components
.length
; i
++)
364 buf
.append((long) components
[i
] & 0xFFFFFFFFL
);
365 if (i
< components
.length
- 1)
368 return (strRep
= buf
.toString());
373 * Computes a hash code for this OID.
375 * @return The hash code.
377 public int hashCode()
380 for (int i
= 0; i
< components
.length
; i
++)
381 ret
+= components
[i
] << (i
& 31);
386 * Tests whether or not this OID equals another.
388 * @return Whether or not this OID equals the other.
390 public boolean equals(Object o
)
392 if (!(o
instanceof OID
))
394 return java
.util
.Arrays
.equals(components
, ((OID
) o
).components
);
398 * Compares this OID to another. The comparison is essentially
399 * lexicographic, where the two OIDs are compared until their
400 * first difference, then that difference is returned. If one OID is
401 * shorter, but all elements equal between the two for the shorter
402 * length, then the shorter OID is lesser than the longer.
404 * @param o The object to compare.
405 * @return An integer less than, equal to, or greater than zero if
406 * this object is less than, equal to, or greater than the
408 * @throws ClassCastException If <i>o</i> is not an OID.
410 public int compareTo(Object o
)
414 int[] components2
= ((OID
) o
).components
;
415 int len
= Math
.min(components
.length
, components2
.length
);
416 for (int i
= 0; i
< len
; i
++)
418 if (components
[i
] != components2
[i
])
419 return (components
[i
] < components2
[i
]) ?
-1 : 1;
421 if (components
.length
== components2
.length
)
423 return (components
.length
< components2
.length
) ?
-1 : 1;
427 // ------------------------------------------------------------------------
429 private static int[] fromDER(byte[] der
, boolean relative
)
430 throws DEREncodingException
432 // cannot be longer than this.
433 int[] components
= new int[der
.length
+ 1];
436 if (!relative
&& i
< der
.length
)
438 // Non-relative OIDs have the first two arcs coded as:
440 // i = first_arc * 40 + second_arc;
442 int j
= (der
[i
] & 0xFF);
443 components
[count
++] = j
/ 40;
444 components
[count
++] = j
% 40;
447 while (i
< der
.length
)
453 components
[count
] <<= 7;
454 components
[count
] |= j
& 0x7F;
455 if (i
>= der
.length
&& (j
& 0x80) != 0)
456 throw new DEREncodingException("malformed OID");
458 while ((j
& 0x80) != 0);
461 if (count
== components
.length
)
463 int[] ret
= new int[count
];
464 System
.arraycopy(components
, 0, ret
, 0, count
);
468 private static int[] fromString(String strRep
) throws NumberFormatException
470 if (strRep
.startsWith("OID.") || strRep
.startsWith("oid."))
471 strRep
= strRep
.substring(4);
472 StringTokenizer tok
= new StringTokenizer(strRep
, ".");
473 if (tok
.countTokens() == 0)
474 throw new IllegalArgumentException();
475 int[] components
= new int[tok
.countTokens()];
477 while (tok
.hasMoreTokens())
479 components
[i
++] = Integer
.parseInt(tok
.nextToken());
484 private static void encodeSubID(ByteArrayOutputStream out
, int id
)
492 out
.write((id
>>> 7) | 0x80);
493 out
.write(id
& 0x7F);
495 else if (id
< 2097152)
497 out
.write((id
>>> 14) | 0x80);
498 out
.write(((id
>>> 7) | 0x80) & 0xFF);
499 out
.write(id
& 0x7F);
501 else if (id
< 268435456)
503 out
.write( (id
>>> 21) | 0x80);
504 out
.write(((id
>>> 14) | 0x80) & 0xFF);
505 out
.write(((id
>>> 7) | 0x80) & 0xFF);
506 out
.write(id
& 0x7F);