diff options
Diffstat (limited to 'base/util/src/netscape/security/util/ObjectIdentifier.java')
-rw-r--r-- | base/util/src/netscape/security/util/ObjectIdentifier.java | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/base/util/src/netscape/security/util/ObjectIdentifier.java b/base/util/src/netscape/security/util/ObjectIdentifier.java new file mode 100644 index 000000000..6ff02d1b0 --- /dev/null +++ b/base/util/src/netscape/security/util/ObjectIdentifier.java @@ -0,0 +1,426 @@ +// --- 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.IOException; +import java.io.Serializable; +import java.util.Hashtable; +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 Hashtable<String, ObjectIdentifier> mOIDs = new Hashtable<String, ObjectIdentifier>(); + + public static ObjectIdentifier getObjectIdentifier(String oid) + throws IOException { + int value; + + if (oid == null) + throw new IOException("empty object identifier"); + + oid = oid.trim(); + + ObjectIdentifier thisOID = 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); + } +} |