1 /* OID.java -- numeric representation of an object identifier
2 Copyright (C) 2003, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, 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
.lang
.CPStringBuilder
;
43 import gnu
.java
.security
.der
.DEREncodingException
;
45 import java
.io
.ByteArrayOutputStream
;
46 import java
.io
.IOException
;
47 import java
.io
.InputStream
;
48 import java
.util
.StringTokenizer
;
51 * This immutable class represents an object identifier, or OID.
53 * <p>OIDs are represented as a series of hierarchical tokens, each of
54 * which is usually represented as a single, unsigned integer. The
55 * hierarchy works so that later tokens are considered within the group
56 * of earlier tokens. Thus, the OID for the Serpent block cipher,
57 * 1.3.6.1.4.1.11591.13.2, is maintained by the GNU project, whose OID
58 * is 1.3.6.1.4.1.11591 (which is, in turn, part of bigger, more general
59 * bodies; the topmost, 1, stands for the OIDs assigned by the
60 * International Standards Organization, ISO).
62 * <p>OIDs can be represented in a variety of ways, including the
63 * dotted-decimal form we use here.
65 * <p>OIDs may be relative, in which case the first two elements of the
68 * @author Casey Marshall (csm@gnu.org)
70 public class OID
implements Cloneable
, Comparable
, java
.io
.Serializable
74 // ------------------------------------------------------------------------
76 /* Serial version id for serialization. */
77 static final long serialVersionUID
= 5722492029044597779L;
80 * The numeric ID structure.
82 private int[] components
;
85 * The string representation of this OID, in dotted-decimal format.
87 private transient String strRep
;
90 * The DER encoding of this OID.
92 private transient byte[] der
;
95 * Whether or not this OID is relative.
97 private boolean relative
;
100 // ------------------------------------------------------------------------
103 * Create a new OID from the given byte array. The argument (which can
104 * neither be null nor zero-length) is copied to prevent subsequent
107 * @param components The numeric IDs.
108 * @throws IllegalArgumentException If <i>components</i> is null or empty.
110 public OID(int[] components
)
112 this(components
, false);
116 * Create a new OID from the given byte array. The argument (which can
117 * neither be null nor zero-length) is copied to prevent subsequent
120 * @param components The numeric IDs.
121 * @param relative The relative flag.
122 * @throws IllegalArgumentException If <i>components</i> is null or empty.
124 public OID(int[] components
, boolean relative
)
126 if (components
== null || components
.length
== 0)
127 throw new IllegalArgumentException();
128 this.components
= (int[]) components
.clone();
129 this.relative
= relative
;
133 * Create a new OID from the given dotted-decimal representation.
135 * @param strRep The string representation of the OID.
136 * @throws IllegalArgumentException If the string does not contain at
138 * @throws NumberFormatException If the string does not contain only
139 * numbers and periods ('.').
141 public OID(String strRep
)
147 * Create a new OID from the given dotted-decimal representation.
149 * @param strRep The string representation of the OID.
150 * @param relative The relative flag.
151 * @throws IllegalArgumentException If the string does not contain at
153 * @throws NumberFormatException If the string does not contain only
154 * numbers and periods ('.').
156 public OID(String strRep
, boolean relative
)
158 this.relative
= relative
;
159 this.strRep
= strRep
;
160 components
= fromString(strRep
);
164 * Construct a new OID from the DER bytes in an input stream. This method
165 * does not read the tag or the length field from the input stream, so
166 * the caller must supply the number of octets in this OID's encoded
169 * @param derIn The DER input stream.
170 * @param len The number of bytes in the encoded form.
171 * @throws IOException If an error occurs reading the OID.
173 public OID(InputStream derIn
, int len
) throws IOException
175 this(derIn
, len
, false);
179 * Construct a new OID from the DER bytes in an input stream. This method
180 * does not read the tag or the length field from the input stream, so
181 * the caller must supply the number of octets in this OID's encoded
184 * @param derIn The DER input stream.
185 * @param len The number of bytes in the encoded form.
186 * @param relative The relative flag.
187 * @throws IOException If an error occurs reading the OID.
189 public OID(InputStream derIn
, int len
, boolean relative
) throws IOException
193 this.relative
= relative
;
196 components
= fromDER(der
, relative
);
198 catch (ArrayIndexOutOfBoundsException aioobe
)
200 aioobe
.printStackTrace();
206 * Construct a new OID from the given DER bytes.
208 * @param encoded The DER encoded OID.
209 * @throws IOException If an error occurs reading the OID.
211 public OID(byte[] encoded
) throws IOException
213 this(encoded
, false);
217 * Construct a new OID from the given DER bytes.
219 * @param encoded The encoded relative OID.
220 * @param relative The relative flag.
222 public OID(byte[] encoded
, boolean relative
) throws IOException
224 der
= (byte[]) encoded
.clone();
225 this.relative
= relative
;
228 components
= fromDER(der
, relative
);
230 catch (ArrayIndexOutOfBoundsException aioobe
)
232 aioobe
.printStackTrace();
238 // ------------------------------------------------------------------------
241 * Return the numeric IDs of this OID. The value returned is copied to
242 * prevent modification.
244 * @return The IDs in a new integer array.
246 public int[] getIDs()
248 return (int[]) components
.clone();
252 * Get the DER encoding of this OID, minus the tag and length fields.
254 * @return The DER bytes.
256 public byte[] getDER()
260 ByteArrayOutputStream bout
= new ByteArrayOutputStream();
264 int b
= components
[i
++] * 40 + (components
.length
> 1
265 ? components
[i
++] : 0);
266 encodeSubID(bout
, b
);
268 for ( ; i
< components
.length
; i
++)
269 encodeSubID(bout
, components
[i
]);
270 der
= bout
.toByteArray();
272 return (byte[]) der
.clone();
276 * Get the parent OID of this OID. That is, if this OID is "1.2.3.4",
277 * then the parent OID will be "1.2.3". If this OID is a top-level
278 * OID, this method returns null.
280 * @return The parent OID, or null.
282 public OID
getParent()
284 if (components
.length
== 1)
286 int[] parent
= new int[components
.length
- 1];
287 System
.arraycopy(components
, 0, parent
, 0, parent
.length
);
288 return new OID(parent
);
291 public OID
getChild(int id
)
293 int[] child
= new int[components
.length
+ 1];
294 System
.arraycopy(components
, 0, child
, 0, components
.length
);
295 child
[child
.length
- 1] = id
;
296 return new OID(child
);
300 * Get the root OID of this OID. That is, the first two components.
302 * @return The root OID.
306 if (components
.length
<= 2)
308 int[] root
= new int[2];
309 root
[0] = components
[0];
310 root
[1] = components
[1];
311 return new OID(root
);
314 public boolean isRelative()
320 * Returns a copy of this OID.
324 public Object
clone()
328 return super.clone();
330 catch (CloneNotSupportedException cnse
)
332 InternalError ie
= new InternalError();
338 /* Nice idea, but possibly too expensive for whatever benefit it
341 public String getShortName()
343 return OIDTable.getShortName(this);
346 public String getLongName()
348 return OIDTable.getLongName(this);
354 * Returns the value of this OID in dotted-decimal format.
356 * @return The string representation.
358 public String
toString()
364 CPStringBuilder buf
= new CPStringBuilder();
365 for (int i
= 0; i
< components
.length
; i
++)
367 buf
.append((long) components
[i
] & 0xFFFFFFFFL
);
368 if (i
< components
.length
- 1)
371 return (strRep
= buf
.toString());
376 * Computes a hash code for this OID.
378 * @return The hash code.
380 public int hashCode()
383 for (int i
= 0; i
< components
.length
; i
++)
384 ret
+= components
[i
] << (i
& 31);
389 * Tests whether or not this OID equals another.
391 * @return Whether or not this OID equals the other.
393 public boolean equals(Object o
)
395 if (!(o
instanceof OID
))
397 return java
.util
.Arrays
.equals(components
, ((OID
) o
).components
);
401 * Compares this OID to another. The comparison is essentially
402 * lexicographic, where the two OIDs are compared until their
403 * first difference, then that difference is returned. If one OID is
404 * shorter, but all elements equal between the two for the shorter
405 * length, then the shorter OID is lesser than the longer.
407 * @param o The object to compare.
408 * @return An integer less than, equal to, or greater than zero if
409 * this object is less than, equal to, or greater than the
411 * @throws ClassCastException If <i>o</i> is not an OID.
413 public int compareTo(Object o
)
417 int[] components2
= ((OID
) o
).components
;
418 int len
= Math
.min(components
.length
, components2
.length
);
419 for (int i
= 0; i
< len
; i
++)
421 if (components
[i
] != components2
[i
])
422 return (components
[i
] < components2
[i
]) ?
-1 : 1;
424 if (components
.length
== components2
.length
)
426 return (components
.length
< components2
.length
) ?
-1 : 1;
430 // ------------------------------------------------------------------------
432 private static int[] fromDER(byte[] der
, boolean relative
)
433 throws DEREncodingException
435 // cannot be longer than this.
436 int[] components
= new int[der
.length
+ 1];
439 if (!relative
&& i
< der
.length
)
441 // Non-relative OIDs have the first two arcs coded as:
443 // i = first_arc * 40 + second_arc;
445 int j
= (der
[i
] & 0xFF);
446 components
[count
++] = j
/ 40;
447 components
[count
++] = j
% 40;
450 while (i
< der
.length
)
456 components
[count
] <<= 7;
457 components
[count
] |= j
& 0x7F;
458 if (i
>= der
.length
&& (j
& 0x80) != 0)
459 throw new DEREncodingException("malformed OID");
461 while ((j
& 0x80) != 0);
464 if (count
== components
.length
)
466 int[] ret
= new int[count
];
467 System
.arraycopy(components
, 0, ret
, 0, count
);
471 private static int[] fromString(String strRep
) throws NumberFormatException
473 if (strRep
.startsWith("OID.") || strRep
.startsWith("oid."))
474 strRep
= strRep
.substring(4);
475 StringTokenizer tok
= new StringTokenizer(strRep
, ".");
476 if (tok
.countTokens() == 0)
477 throw new IllegalArgumentException();
478 int[] components
= new int[tok
.countTokens()];
480 while (tok
.hasMoreTokens())
482 components
[i
++] = Integer
.parseInt(tok
.nextToken());
487 private static void encodeSubID(ByteArrayOutputStream out
, int id
)
495 out
.write((id
>>> 7) | 0x80);
496 out
.write(id
& 0x7F);
498 else if (id
< 2097152)
500 out
.write((id
>>> 14) | 0x80);
501 out
.write(((id
>>> 7) | 0x80) & 0xFF);
502 out
.write(id
& 0x7F);
504 else if (id
< 268435456)
506 out
.write( (id
>>> 21) | 0x80);
507 out
.write(((id
>>> 14) | 0x80) & 0xFF);
508 out
.write(((id
>>> 7) | 0x80) & 0xFF);
509 out
.write(id
& 0x7F);