diff options
Diffstat (limited to 'pki/base/util/src/netscape/security/util/ObjectIdentifier.java')
-rw-r--r-- | pki/base/util/src/netscape/security/util/ObjectIdentifier.java | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/pki/base/util/src/netscape/security/util/ObjectIdentifier.java b/pki/base/util/src/netscape/security/util/ObjectIdentifier.java new file mode 100644 index 000000000..fed9637af --- /dev/null +++ b/pki/base/util/src/netscape/security/util/ObjectIdentifier.java @@ -0,0 +1,447 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2007 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- +package netscape.security.util; + +import java.io.*; +import java.util.StringTokenizer; + + +/** + * Represent an ISO Object Identifier. + * + * <P>Object Identifiers are arbitrary length hierarchical identifiers. + * The individual components are numbers, and they define paths from the + * root of an ISO-managed identifier space. You will sometimes see a + * string name used instead of (or in addition to) the numerical id. + * These are synonyms for the numerical IDs, but are not widely used + * since most sites do not know all the requisite strings, while all + * sites can parse the numeric forms. + * + * <P>So for example, JavaSoft has the sole authority to assign the + * meaning to identifiers below the 1.3.6.1.4.42.2.17 node in the + * hierarchy, and other organizations can easily acquire the ability + * to assign such unique identifiers. + * + * @version 1.23 + * + * @author David Brownell + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +final public +class ObjectIdentifier implements Serializable +{ + /** use serialVersionUID from JDK 1.1. for interoperability */ + private static final long serialVersionUID = 8697030238860181294L; + + /** + * Constructs an object identifier from a string. This string + * should be of the form 1.23.34.45.56 etc. + */ + public ObjectIdentifier (String oid) + { + if (oid == null) + return; + + int ch = '.'; + int start = 0; + int end = 0; + + // Calculate length of oid + componentLen = 0; + while ((end = oid.indexOf(ch,start)) != -1) { + start = end + 1; + componentLen += 1; + } + componentLen += 1; + components = new int[componentLen]; + + start = 0; + int i = 0; + String comp = null; + while ((end = oid.indexOf(ch,start)) != -1) { + comp = oid.substring(start,end); + components[i++] = Integer.valueOf(comp).intValue(); + start = end + 1; + } + comp = oid.substring(start); + components[i] = Integer.valueOf(comp).intValue(); + } + + /** + * Constructs an object ID from an array of integers. This + * is used to construct constant object IDs. + */ + public ObjectIdentifier (int values []) + { + try { + components = (int []) values.clone (); + componentLen = values.length; + } catch (Throwable t) { + System.out.println ("X509.ObjectIdentifier(), no cloning!"); + } + } + + + /** + * Constructs an object ID from an ASN.1 encoded input stream. + * The encoding of the ID in the stream uses "DER", a BER/1 subset. + * In this case, that means a triple { typeId, length, data }. + * + * <P><STRONG>NOTE:</STRONG> When an exception is thrown, the + * input stream has not been returned to its "initial" state. + * + * @param in DER-encoded data holding an object ID + * @exception IOException indicates a decoding error + */ + public ObjectIdentifier (DerInputStream in) + throws IOException + { + byte type_id; + int bufferEnd; + + /* + * Object IDs are a "universal" type, and their tag needs only + * one byte of encoding. Verify that the tag of this datum + * is that of an object ID. + * + * Then get and check the length of the ID's encoding. We set + * up so that we can use in.available() to check for the end of + * this value in the data stream. + */ + type_id = (byte) in.getByte (); + if (type_id != DerValue.tag_ObjectId) + throw new IOException ( + "X509.ObjectIdentifier() -- data isn't an object ID" + + " (tag = " + type_id + ")" + ); + + bufferEnd = in.available () - in.getLength () - 1; + if (bufferEnd < 0) + throw new IOException ( + "X509.ObjectIdentifier() -- not enough data"); + + initFromEncoding (in, bufferEnd); + } + + /* + * Build the OID from the rest of a DER input buffer; the tag + * and length have been removed/verified + */ + ObjectIdentifier (DerInputBuffer buf) throws IOException + { + initFromEncoding (new DerInputStream (buf), 0); + } + + /* + * Helper function -- get the OID from a stream, after tag and + * length are verified. + */ + private void initFromEncoding (DerInputStream in, int bufferEnd) + throws IOException + { + + /* + * Now get the components ("sub IDs") one at a time. We fill a + * temporary buffer, resizing it as needed. + */ + int component; + boolean first_subid = true; + + for (components = new int [allocationQuantum], componentLen = 0; + in.available () > bufferEnd; + ) { + component = getComponent (in); + + if (first_subid) { + int X, Y; + + /* + * The ISO root has three children (0, 1, 2) and those nodes + * aren't allowed to assign IDs larger than 39. These rules + * are memorialized by some special casing in the BER encoding + * of object IDs ... or maybe it's vice versa. + * + * NOTE: the allocation quantum is large enough that we know + * we don't have to reallocate here! + */ + if (component < 40) + X = 0; + else if (component < 80) + X = 1; + else + X = 2; + Y = component - ( X * 40); + + components [0] = X; + components [1] = Y; + componentLen = 2; + + first_subid = false; + + } else { + + /* + * Other components are encoded less exotically. The only + * potential trouble is the need to grow the array. + */ + if (componentLen >= components.length) { + int tmp_components []; + + tmp_components = new int [components.length + + allocationQuantum]; + System.arraycopy (components, 0, tmp_components, 0, + components.length); + components = tmp_components; + } + components [componentLen++] = component; + } + } + + /* + * Final sanity check -- if we didn't use exactly the number of bytes + * specified, something's quite wrong. + */ + if (in.available () != bufferEnd) { + throw new IOException ( + "X509.ObjectIdentifier() -- malformed input data"); + } + } + + + /* + * n.b. the only public interface is DerOutputStream.putOID() + */ + void encode (DerOutputStream out) throws IOException + { + DerOutputStream bytes = new DerOutputStream (); + int i; + + bytes.write ((components [0] * 40) + components [1]); + for (i = 2; i < componentLen; i++) + putComponent (bytes, components [i]); + + /* + * Now that we've constructed the component, encode + * it in the stream we were given. + */ + out.write (DerValue.tag_ObjectId, bytes); + } + + /* + * Tricky OID component parsing technique ... note that one bit + * per octet is lost, this returns at most 28 bits of component. + * Also, notice this parses in big-endian format. + */ + private static int getComponent (DerInputStream in) + throws IOException + { + int retval, i, tmp; + + for (i = 0, retval = 0; i < 4; i++) { + retval <<= 7; + tmp = in.getByte (); + retval |= (tmp & 0x07f); + if ((tmp & 0x080) == 0) + return retval; + } + + throw new IOException ("X509.OID, component value too big"); + } + + /* + * Reverse of the above routine. Notice it needs to emit in + * big-endian form, so it buffers the output until it's ready. + * (Minimum length encoding is a DER requirement.) + */ + private static void putComponent (DerOutputStream out, int val) + throws IOException + { + int i; + byte buf [] = new byte [4] ; + + for (i = 0; i < 4; i++) { + buf [i] = (byte) (val & 0x07f); + val >>>= 7; + if (val == 0) + break; + } + for ( ; i > 0; --i) + out.write (buf [i] | 0x080); + out.write (buf [0]); + } + + // XXX this API should probably facilitate the JDK sort utility + + /** + * Compares this identifier with another, for sorting purposes. + * An identifier does not precede itself. + * + * @param other identifer that may precede this one. + * @return true iff <em>other</em> precedes this one + * in a particular sorting order. + */ + public boolean precedes (ObjectIdentifier other) + { + int i; + + // shorter IDs go first + if (other == this || componentLen < other.componentLen) + return false; + if (other.componentLen < componentLen) + return true; + + // for each component, the lesser component goes first + for (i = 0; i < componentLen; i++) { + if (other.components [i] < components [i]) + return true; + } + + // identical IDs don't precede each other + return false; + } + + public boolean equals (Object other) + { + if (other instanceof ObjectIdentifier) + return equals ((ObjectIdentifier) other); + else + return false; + } + + /** + * Compares this identifier with another, for equality. + * + * @return true iff the names are identical. + */ + public boolean equals (ObjectIdentifier other) + { + int i; + + if (other == this) + return true; + if (componentLen != other.componentLen) + return false; + for (i = 0; i < componentLen; i++) { + if (components [i] != other.components [i]) + return false; + } + return true; + } + + public int hashCode() { + int h=0; + int oflow=0; + + for (int i=0;i<componentLen;i++) { + oflow = (h & 0xff800000) >> 23; + h <<= 9; + h += components[i]; + h ^= oflow; + } + return h; + } + + /** + * Returns a string form of the object ID. The format is the + * conventional "dot" notation for such IDs, without any + * user-friendly descriptive strings, since those strings + * will not be understood everywhere. + */ + public String toString () + { + String retval; + int i; + + for (i = 0, retval = ""; i < componentLen; i++) { + if (i != 0) + retval += "."; + retval += components [i]; + } + return retval; + } + + /* + * To simplify, we assume no individual component of an object ID is + * larger than 32 bits. Then we represent the path from the root as + * an array that's (usually) only filled at the beginning. + */ + private int components []; // path from root + private int componentLen; // how much is used. + + private static final int allocationQuantum = 5; // >= 2 + + /** + * Netscape Enhancement: + * This function implements a object identifier factory. It + * should help reduces in-memory Object Identifier object. + * This function also provide additional checking on the OID. + * A valid OID should start with 0, 1, or 2. + * + * Notes: + * This function never returns null. IOException is raised + * in error conditions. + */ + public static java.util.Hashtable mOIDs = new java.util.Hashtable(); + public static ObjectIdentifier getObjectIdentifier(String oid) + throws IOException + { + int value; + + if (oid == null) + throw new IOException("empty object identifier"); + + oid = oid.trim(); + + ObjectIdentifier thisOID = (ObjectIdentifier)mOIDs.get(oid); + if (thisOID != null) + return thisOID; + + StringTokenizer token = new StringTokenizer(oid, "."); + value = new Integer(token.nextToken()).intValue(); + /* First token should be 0, 1, 2 */ + if (value >= 0 && value <= 2) { + value = new Integer(token.nextToken()).intValue(); + /* Second token should be 0 <= && >= 39 */ + if (value >= 0 && value <= 39) { + thisOID = new ObjectIdentifier(oid); + if (thisOID.toString().equals(oid)) { + mOIDs.put(oid, thisOID); + return thisOID; + } + throw new IOException("invalid oid " + oid); + } else + throw new IOException("invalid oid " + oid); + } else + throw new IOException("invalid oid " + oid); + } + + public static ObjectIdentifier getObjectIdentifier(int values[]) + throws IOException + { + String retval; + int i; + + for (i = 0, retval = ""; i < values.length; i++) { + if (i != 0) + retval += "."; + retval += values [i]; + } + return getObjectIdentifier(retval); + } +} |