libjava/ChangeLog:
[official-gcc.git] / libjava / classpath / gnu / java / security / OID.java
bloba3d70e220199c0363b57549dd190e0e2a9e7b6d9
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)
9 any later version.
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
19 02110-1301 USA.
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
24 combination.
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;
50 /**
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
66 * OID are omitted.
68 * @author Casey Marshall (csm@gnu.org)
70 public class OID implements Cloneable, Comparable, java.io.Serializable
73 // Fields.
74 // ------------------------------------------------------------------------
76 /* Serial version id for serialization. */
77 static final long serialVersionUID = 5722492029044597779L;
79 /**
80 * The numeric ID structure.
82 private int[] components;
84 /**
85 * The string representation of this OID, in dotted-decimal format.
87 private transient String strRep;
89 /**
90 * The DER encoding of this OID.
92 private transient byte[] der;
94 /**
95 * Whether or not this OID is relative.
97 private boolean relative;
99 // Constructors.
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
105 * modification.
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
118 * modification.
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
137 * least one integer.
138 * @throws NumberFormatException If the string does not contain only
139 * numbers and periods ('.').
141 public OID(String strRep)
143 this(strRep, false);
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
152 * least one integer.
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
167 * form.
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
182 * form.
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
191 der = new byte[len];
192 derIn.read(der);
193 this.relative = relative;
196 components = fromDER(der, relative);
198 catch (ArrayIndexOutOfBoundsException aioobe)
200 aioobe.printStackTrace();
201 throw aioobe;
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();
233 throw aioobe;
237 // Instance methods.
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()
258 if (der == null)
260 ByteArrayOutputStream bout = new ByteArrayOutputStream();
261 int i = 0;
262 if (!relative)
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)
285 return null;
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.
304 public OID getRoot()
306 if (components.length <= 2)
307 return this;
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()
316 return relative;
320 * Returns a copy of this OID.
322 * @return The copy.
324 public Object clone()
328 return super.clone();
330 catch (CloneNotSupportedException cnse)
332 InternalError ie = new InternalError();
333 ie.initCause(cnse);
334 throw ie;
338 /* Nice idea, but possibly too expensive for whatever benefit it
339 * provides.
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()
360 if (strRep != null)
361 return strRep;
362 else
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)
369 buf.append('.');
371 return (strRep = buf.toString());
376 * Computes a hash code for this OID.
378 * @return The hash code.
380 public int hashCode()
382 int ret = 0;
383 for (int i = 0; i < components.length; i++)
384 ret += components[i] << (i & 31);
385 return ret;
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))
396 return false;
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
410 * argument.
411 * @throws ClassCastException If <i>o</i> is not an OID.
413 public int compareTo(Object o)
415 if (equals(o))
416 return 0;
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)
425 return 0;
426 return (components.length < components2.length) ? -1 : 1;
429 // Own methods.
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];
437 int count = 0;
438 int i = 0;
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;
448 i++;
450 while (i < der.length)
452 int j = 0;
455 j = der[i++] & 0xFF;
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);
462 count++;
464 if (count == components.length)
465 return components;
466 int[] ret = new int[count];
467 System.arraycopy(components, 0, ret, 0, count);
468 return ret;
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()];
479 int i = 0;
480 while (tok.hasMoreTokens())
482 components[i++] = Integer.parseInt(tok.nextToken());
484 return components;
487 private static void encodeSubID(ByteArrayOutputStream out, int id)
489 if (id < 128)
491 out.write(id);
493 else if (id < 16384)
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);