OID-huge
# HG changeset patch
# User weijun
# Date 1237523027 -28800
# Node ID 66cb55a0454f197cd456945fea293c1877d699db
# Parent 4bd791a3b5e05f812479a222ffad1cf80af78321
9999999: Really huge OIDs
Reviewed-by: nobody
--- a/src/share/classes/sun/security/util/ObjectIdentifier.java Fri Mar 20 12:23:45 2009 +0800
+++ b/src/share/classes/sun/security/util/ObjectIdentifier.java Fri Mar 20 12:23:47 2009 +0800
@@ -26,7 +26,8 @@
package sun.security.util;
import java.io.*;
-
+import java.math.BigInteger;
+import java.util.Arrays;
/**
* Represent an ISO Object Identifier.
@@ -44,105 +45,186 @@
* hierarchy, and other organizations can easily acquire the ability
* to assign such unique identifiers.
*
- *
* @author David Brownell
* @author Amit Kapoor
* @author Hemma Prafullchandra
*/
+
final public
class ObjectIdentifier implements Serializable
{
- /** use serialVersionUID from JDK 1.1. for interoperability */
+ /**
+ * We use the DER encoding as the internal format
+ * @serial
+ */
+ private byte[] encoding = null;
+
+ private transient volatile String stringForm;
+
+ /*
+ * IMPORTANT NOTES FOR CODE CHANGES (bug 4811968) IN JDK 1.7.0
+ * ===========================================================
+ *
+ * (Almost) serialization compatibility with old versions:
+ *
+ * serialVersionUID is unchanged. Old field "component" is changed to
+ * type Object so that "poison" (unknown object type for old versions)
+ * can be put inside if there are huge components that cannot be saved
+ * as integers.
+ *
+ * New version use the new filed "encoding" only.
+ *
+ * Below are all 4 cases in a serialization/deserialization process:
+ *
+ * 1. old -> old: Not covered here
+ * 2. old -> new: There's no "encoding" field, new readObject() reads
+ * "components" and "componentLen" instead and inits correctly.
+ * 3. new -> new: "encoding" field exists, new readObject() uses it
+ * (ignoring the other 2 fields) and inits correctly.
+ * 4. new -> old: old readObject() only recognizes "components" and
+ * "componentLen" fields. If no huge components are involved, they
+ * are serialized as legal values and old object can init correctly.
+ * Otherwise, old object cannot recognize the form (component not int[])
+ * and throw a ClassNotFoundException at deserialization time.
+ *
+ * Therfore, for the first 3 cases, exact compatibility is preserved. In
+ * the 4th case, non-huge OID is still supportable in old versions, while
+ * huge OID is not.
+ */
private static final long serialVersionUID = 8697030238860181294L;
- private static final int maxFirstComponent = 2;
- private static final int maxSecondComponent = 39;
-
- /**
- * Constructs an object identifier from a string. This string
- * should be of the form 1.23.34.45.56 etc.
+
+ /**
+ * Changed to Object
+ * @serial
+ */
+ private Object components = null; // path from root
+ /**
+ * componentLen:
+ * -1 means uninitilized,
+ * -2 means huge OID
+ * @serial
+ */
+ private int componentLen = -1; // how much is used.
+
+ private void readObject(ObjectInputStream is)
+ throws IOException, ClassNotFoundException {
+ is.defaultReadObject();
+
+ if (encoding == null) { // from an old version
+ init((int[])components, componentLen);
+ }
+ }
+
+ private void writeObject(ObjectOutputStream os)
+ throws IOException {
+ if (componentLen == -1) {
+ int[] comps = toIntArray();
+ if (comps != null) { // every one understands this
+ components = comps;
+ componentLen = comps.length;
+ } else {
+ components = HugeOidNotSupportedByOldJDK.theOne;
+ componentLen = -2;
+ }
+ }
+ os.defaultWriteObject();
+ }
+
+ static class HugeOidNotSupportedByOldJDK implements Serializable {
+ private static final long serialVersionUID = 1L;
+ static HugeOidNotSupportedByOldJDK theOne = new HugeOidNotSupportedByOldJDK();
+ }
+
+ /**
+ * Constructs, from a string. This string should be of the form 1.23.56.
+ * Validity check included.
*/
public ObjectIdentifier (String oid) throws IOException
{
int ch = '.';
- int start = 0;
+ 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;
+ int pos = 0;
+ byte[] tmp = new byte[oid.length()];
+ int first = 0, second;
+ int count = 0;
+
try {
- while ((end = oid.indexOf(ch,start)) != -1) {
- comp = oid.substring(start,end);
- components[i++] = Integer.valueOf(comp).intValue();
+ String comp = null;
+ do {
+ int length = 0; // length of one section
+ end = oid.indexOf(ch,start);
+ if (end == -1) {
+ comp = oid.substring(start);
+ length = oid.length() - start;
+ } else {
+ comp = oid.substring(start,end);
+ length = end - start;
+ }
+
+ if (length > 9) {
+ BigInteger bignum = new BigInteger(comp);
+ if (count == 0) {
+ checkFirstComponent(bignum);
+ first = bignum.intValue();
+ } else {
+ if (count == 1) {
+ checkSecondComponent(first, bignum);
+ bignum = bignum.add(BigInteger.valueOf(40*first));
+ } else {
+ checkOtherComponent(count, bignum);
+ }
+ pos += pack7Oid(bignum, tmp, pos);
+ }
+ } else {
+ int num = Integer.parseInt(comp);
+ if (count == 0) {
+ checkFirstComponent(num);
+ first = num;
+ } else {
+ if (count == 1) {
+ checkSecondComponent(first, num);
+ num += 40 * first;
+ } else {
+ checkOtherComponent(count, num);
+ }
+ pos += pack7Oid(num, tmp, pos);
+ }
+ }
start = end + 1;
- }
- comp = oid.substring(start);
- components[i] = Integer.valueOf(comp).intValue();
+ count++;
+ } while (end != -1);
+
+ checkCount(count);
+ encoding = new byte[pos];
+ System.arraycopy(tmp, 0, encoding, 0, pos);
+ this.stringForm = oid;
+ } catch (IOException ioe) { // already detected by checkXXX
+ throw ioe;
} catch (Exception e) {
throw new IOException("ObjectIdentifier() -- Invalid format: "
+ e.toString(), e);
}
- checkValidOid(components, componentLen);
- this.stringForm = oid;
- }
-
- /**
- * Check if the values make a legal OID. There must be at least 2
- * components and they must be all non-negative. The first component
- * should be 0,1 or 2. When the first component is 0 or 1, the
- * second component should be less than or equal to 39
- *
- * @param values the components that will make the OID
- * @param len the number of components to check. Note that the allocation
- * size of <code>values</code> may be longer than <code>len</code>.
- * In this case, only the first <code>len</code> items are checked.
- * @exception IOException if this is not a legal OID
- */
- private void checkValidOid(int[] values, int len) throws IOException {
- if (values == null || len < 2) {
- throw new IOException("ObjectIdentifier() -- " +
- "Must be at least two oid components ");
- }
-
- for (int i=0; i<len; i++) {
- if (values[i] < 0) {
- throw new IOException("ObjectIdentifier() -- " +
- "oid component #" + (i+1) + " must be non-negative ");
- }
- }
-
- if (values[0] > maxFirstComponent) {
- throw new IOException("ObjectIdentifier() -- " +
- "First oid component is invalid ");
- }
-
- if (values[0] < 2 && values[1] > maxSecondComponent) {
- throw new IOException("ObjectIdentifier() -- " +
- "Second oid component is invalid ");
- }
- }
- /**
- * Constructs an object ID from an array of integers. This
- * is used to construct constant object IDs.
+ }
+
+ /**
+ * Constructor, from an array of integers.
+ * Validity check included.
*/
public ObjectIdentifier (int values []) throws IOException
{
- checkValidOid(values, values.length);
- components = values.clone();
- componentLen = values.length;
- }
-
- /**
- * Constructs an object ID from an ASN.1 encoded input stream.
+ checkCount(values.length);
+ checkFirstComponent(values[0]);
+ checkSecondComponent(values[0], values[1]);
+ for (int i=2; i<values.length; i++)
+ checkOtherComponent(i, values[i]);
+ init(values, values.length);
+ }
+
+ /**
+ * Constructor, from an ASN.1 encoded input stream.
+ * Validity check NOT included.
* The encoding of the ID in the stream uses "DER", a BER/1 subset.
* In this case, that means a triple { typeId, length, data }.
*
@@ -152,8 +234,7 @@
* @param in DER-encoded data holding an object ID
* @exception IOException indicates a decoding error
*/
- public ObjectIdentifier (DerInputStream in)
- throws IOException
+ public ObjectIdentifier (DerInputStream in) throws IOException
{
byte type_id;
int bufferEnd;
@@ -174,30 +255,49 @@
+ " (tag = " + type_id + ")"
);
- bufferEnd = in.available () - in.getLength () - 1;
- if (bufferEnd < 0)
- throw new IOException (
- "ObjectIdentifier() -- not enough data");
-
- initFromEncoding (in, bufferEnd);
+ encoding = new byte[in.getLength()];
+ in.getBytes(encoding);
+ check(encoding);
}
/*
- * Build the OID from the rest of a DER input buffer; the tag
- * and length have been removed/verified
+ * Constructor, from the rest of a DER input buffer;
+ * the tag and length have been removed/verified
+ * Validity check NOT included.
*/
ObjectIdentifier (DerInputBuffer buf) throws IOException
{
- initFromEncoding (new DerInputStream (buf), 0);
- }
-
- /**
- * Private constructor for use by newInternal(). Dummy argument
- * to avoid clash with the public constructor.
- */
- private ObjectIdentifier(int[] components, boolean dummy) {
- this.components = components;
- this.componentLen = components.length;
+ DerInputStream in = new DerInputStream(buf);
+ encoding = new byte[in.available()];
+ in.getBytes(encoding);
+ check(encoding);
+ }
+
+ /**
+ * Private constructor.
+ * Validity check NOT included.
+ */
+ private ObjectIdentifier(int[] components, int length) {
+ init(components, length);
+ }
+
+ private void init(int[] components, int length) {
+ int pos = 0;
+ byte[] tmp = new byte[length*5+1]; // +1 for empty input
+
+ if (components[1] < Integer.MAX_VALUE - components[0]*40)
+ pos += pack7Oid(components[0]*40+components[1], tmp, pos);
+ else {
+ BigInteger big = BigInteger.valueOf(components[1]);
+ big = big.add(BigInteger.valueOf(components[0]*40));
+ pos += pack7Oid(big, tmp, pos);
+ }
+
+ for (int i=2; i<length; i++) {
+ pos += pack7Oid(components[i], tmp, pos);
+ }
+ encoding = new byte[pos];
+ System.arraycopy(tmp, 0, encoding, 0, pos);
}
/**
@@ -205,187 +305,15 @@
* neither checked nor cloned.
*/
public static ObjectIdentifier newInternal(int[] values) {
- return new ObjectIdentifier(values, true);
- }
-
- /*
- * 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 (component < 0) {
- throw new IOException(
- "ObjectIdentifier() -- " +
- "component values must be nonnegative");
- }
- if (first_subid) {
- int X, Y;
-
- /*
- * 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;
- }
- }
-
- checkValidOid(components, componentLen);
-
- /*
- * 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 (
- "ObjectIdentifier() -- malformed input data");
- }
- }
-
+ return new ObjectIdentifier(values, values.length);
+ }
/*
* n.b. the only public interface is DerOutputStream.putOID()
*/
void encode (DerOutputStream out) throws IOException
{
- DerOutputStream bytes = new DerOutputStream ();
- int i;
-
- // According to ISO X.660, when the 1st component is 0 or 1, the 2nd
- // component is restricted to be less than or equal to 39, thus make
- // it small enough to be encoded into one single byte.
- if (components[0] < 2) {
- bytes.write ((components [0] * 40) + components [1]);
- } else {
- putComponent(bytes, (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 31 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 < 5; i++) {
- // Since we can only deal with 31 bits of component now, make sure
- // it's still within 24 bits before <<= 7.
- if ((retval & 0xff000000) != 0) {
- throw new IOException ("ObjectIdentifier() -- component value too big");
- }
- retval <<= 7;
- tmp = in.getByte ();
- retval |= (tmp & 0x07f);
- if ((tmp & 0x080) == 0)
- return retval;
- }
-
- throw new IOException ("ObjectIdentifier() -- 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 [5] ;
-
- for (i = 0; i < 5; 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;
+ out.write (DerValue.tag_ObjectId, encoding);
}
/**
@@ -401,6 +329,7 @@
*
* @return true iff the names are identical.
*/
+ @Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
@@ -409,23 +338,71 @@
return false;
}
ObjectIdentifier other = (ObjectIdentifier)obj;
- if (componentLen != other.componentLen) {
- return false;
- }
- for (int i = 0; i < componentLen; i++) {
- if (components[i] != other.components[i]) {
- return false;
- }
- }
- return true;
- }
-
+ return Arrays.equals(encoding, other.encoding);
+ }
+
+ @Override
public int hashCode() {
- int h = componentLen;
- for (int i = 0; i < componentLen; i++) {
- h += components[i] * 37;
- }
- return h;
+ return Arrays.hashCode(encoding);
+ }
+
+ /**
+ * Private helper method for serialization. To be compatible with old
+ * versions of JDK.
+ * @return components in an int array, if all the components are less than
+ * Integer.MAX_VALUE. Otherwise, null.
+ */
+ private int[] toIntArray() {
+ int length = encoding.length;
+ int[] result = new int[20];
+ int which = 0;
+ int fromPos = 0;
+ for (int i = 0; i < length; i++) {
+ if ((encoding[i] & 0x80) == 0) {
+ // one section [fromPos..i]
+ if (i - fromPos + 1 > 4) {
+ BigInteger big = new BigInteger(pack(encoding, fromPos, i-fromPos+1, 7, 8));
+ if (fromPos == 0) {
+ result[which++] = 2;
+ BigInteger second = big.subtract(BigInteger.valueOf(80));
+ if (second.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) == 1) {
+ return null;
+ } else {
+ result[which++] = second.intValue();
+ }
+ } else {
+ if (big.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) == 1) {
+ return null;
+ } else {
+ result[which++] = big.intValue();
+ }
+ }
+ } else {
+ int retval = 0;
+ for (int j = fromPos; j <= i; j++) {
+ retval <<= 7;
+ byte tmp = encoding[j];
+ retval |= (tmp & 0x07f);
+ }
+ if (fromPos == 0) {
+ if (retval < 80) {
+ result[which++] = retval / 40;
+ result[which++] = retval % 40;
+ } else {
+ result[which++] = 2;
+ result[which++] = retval - 80;
+ }
+ } else {
+ result[which++] = retval;
+ }
+ }
+ fromPos = i+1;
+ }
+ if (which >= result.length) {
+ result = Arrays.copyOf(result, which + 10);
+ }
+ }
+ return Arrays.copyOf(result, which);
}
/**
@@ -434,15 +411,52 @@
* user-friendly descriptive strings, since those strings
* will not be understood everywhere.
*/
+ @Override
public String toString() {
String s = stringForm;
if (s == null) {
- StringBuffer sb = new StringBuffer(componentLen * 4);
- for (int i = 0; i < componentLen; i++) {
- if (i != 0) {
- sb.append('.');
+ int length = encoding.length;
+ StringBuffer sb = new StringBuffer(length * 4);
+
+ int fromPos = 0;
+ for (int i = 0; i < length; i++) {
+ if ((encoding[i] & 0x80) == 0) {
+ // one section [fromPos..i]
+ if (fromPos != 0) { // not the first segment
+ sb.append('.');
+ }
+ if (i - fromPos + 1 > 4) { // maybe big integer
+ BigInteger big = new BigInteger(pack(encoding, fromPos, i-fromPos+1, 7, 8));
+ if (fromPos == 0) {
+ // first section encoded with more than 4 bytes,
+ // must be 2.something
+ sb.append("2.");
+ sb.append(big.subtract(BigInteger.valueOf(80)));
+ } else {
+ sb.append(big);
+ }
+ } else { // small integer
+ int retval = 0;
+ for (int j = fromPos; j <= i; j++) {
+ retval <<= 7;
+ byte tmp = encoding[j];
+ retval |= (tmp & 0x07f);
+ }
+ if (fromPos == 0) {
+ if (retval < 80) {
+ sb.append(retval/40);
+ sb.append('.');
+ sb.append(retval%40);
+ } else {
+ sb.append("2.");
+ sb.append(retval - 80);
+ }
+ } else {
+ sb.append(retval);
+ }
+ }
+ fromPos = i+1;
}
- sb.append(components[i]);
}
s = sb.toString();
stringForm = s;
@@ -450,15 +464,197 @@
return s;
}
- /*
- * 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 transient volatile String stringForm;
-
- private static final int allocationQuantum = 5; // >= 2
+ /**
+ * Repack all bits from input to output. On the both sides, only a portion
+ * (from the least significant bit) of the 8 bits in a byte is used. This
+ * number is defined as the number of useful bits (NUB) for the array. All the
+ * used bits from the input byte array and repacked into the output in the
+ * exactly same order. The output bits are aligned so that the final bit of
+ * the input (the least significant bit in the last byte), when repacked as
+ * the final bit of the output, is still at the least significant position.
+ * Zeroes will be padded on the left side of the first output byte if
+ * necessary. All unused bits in the output are also zeroed.
+ *
+ * For example: if the input is 01001100 with NUB 8, the output which
+ * has a NUB 6 will look like:
+ * 00000001 00001100
+ * The first 2 bits of the output bytes are unused bits. The other bits
+ * turn out to be 000001 001100. While the 8 bits on the right are from
+ * the input, the left 4 zeroes are padded to fill the 6 bits space.
+ *
+ * @param in the input byte array
+ * @param ioffset start point inside <code>in</code>
+ * @param ilength number of bytes to repack
+ * @param iw NUB for input
+ * @param ow NUB for output
+ * @return the repacked bytes
+ */
+ private static byte[] pack(byte[] in, int ioffset, int ilength, int iw, int ow) {
+ assert (iw > 0 && iw <= 8): "input NUB must be between 1 and 8";
+ assert (ow > 0 && ow <= 8): "output NUB must be between 1 and 8";
+
+ if (iw == ow) {
+ return in.clone();
+ }
+
+ int bits = ilength * iw; // number of all used bits
+ byte[] out = new byte[(bits+ow-1)/ow];
+
+ // starting from the 0th bit in the input
+ int ipos = 0;
+
+ // the number of padding 0's needed in the output, skip them
+ int opos = (bits+ow-1)/ow*ow-bits;
+
+ while(ipos < bits) {
+ int count = iw - ipos%iw; // unpacked bits in current input byte
+ if (count > ow - opos%ow) { // free space available in output byte
+ count = ow - opos%ow; // choose the smaller number
+ }
+ // and move them!
+ out[opos/ow] |= // paste!
+ (((in[ioffset+ipos/iw]+256) // locate the byte (+256 so that it's never negative)
+ >> (iw-ipos%iw-count)) // move to the end of a byte
+ & ((1 << (count))-1)) // zero out all other bits
+ << (ow-opos%ow-count); // move to the output position
+ ipos += count; // advance
+ opos += count; // advance
+ }
+ return out;
+ }
+
+ /**
+ * Repack from NUB 8 to a NUB 7 OID subidentifier, remove all
+ * unnecessary 0 headings, set the first bit of all non-tail
+ * output bytes to 1 (as ITU-T Rec. X.690 8.19.2 says), and
+ * paste it into an existing byte array.
+ * @param out the existing array to be pasted into
+ * @param ooffset the starting position to paste
+ * @return the number of bytes pasted
+ */
+ private static int pack7Oid(byte[] in, int ioffset, int ilength, byte[] out, int ooffset) {
+ byte[] pack = pack(in, ioffset, ilength, 8, 7);
+ int firstNonZero = pack.length-1; // paste at least one byte
+ for (int i=pack.length-2; i>=0; i--) {
+ if (pack[i] != 0) {
+ firstNonZero = i;
+ }
+ pack[i] |= 0x80;
+ }
+ System.arraycopy(pack, firstNonZero, out, ooffset, pack.length-firstNonZero);
+ return pack.length-firstNonZero;
+ }
+
+ /**
+ * Repack from NUB 7 to NUB 8, remove all unnecessary 0
+ * headings, and paste it into an existing byte array.
+ * @param out the existing array to be pasted into
+ * @param ooffset the starting position to paste
+ * @return the number of bytes pasted
+ */
+ private static int pack8(byte[] in, int ioffset, int ilength, byte[] out, int ooffset) {
+ byte[] pack = pack(in, ioffset, ilength, 7, 8);
+ int firstNonZero = pack.length-1; // paste at least one byte
+ for (int i=pack.length-2; i>=0; i--) {
+ if (pack[i] != 0) {
+ firstNonZero = i;
+ }
+ }
+ System.arraycopy(pack, firstNonZero, out, ooffset, pack.length-firstNonZero);
+ return pack.length-firstNonZero;
+ }
+
+ /**
+ * Pack the int into a OID subidentifier DER encoding
+ */
+ private static int pack7Oid(int input, byte[] out, int ooffset) {
+ byte[] b = new byte[4];
+ b[0] = (byte)(input >> 24);
+ b[1] = (byte)(input >> 16);
+ b[2] = (byte)(input >> 8);
+ b[3] = (byte)(input);
+ return pack7Oid(b, 0, 4, out, ooffset);
+ }
+
+ /**
+ * Pack the BigInteger into a OID subidentifier DER encoding
+ */
+ private static int pack7Oid(BigInteger input, byte[] out, int ooffset) {
+ byte[] b = input.toByteArray();
+ return pack7Oid(b, 0, b.length, out, ooffset);
+ }
+
+ /**
+ * Private methods to check validity of OID. They must be --
+ * 1. at least 2 componets
+ * 2. all components must be non-negative
+ * 3. the first must be 0, 1 or 2
+ * 4. if the first is 0 or 1, the second must be <40
+ */
+
+ /**
+ * Check the DER encoding. Since DER encoding defines that the integer bits
+ * are unsigned, so there's no need to check the MSB.
+ */
+ private static void check(byte[] encoding) throws IOException {
+ int length = encoding.length;
+ if (length < 1 || // too short
+ (encoding[length - 1] & 0x80) != 0) { // not ended
+ throw new IOException("ObjectIdentifier() -- " +
+ "Invalid DER encoding, not ended");
+ }
+ for (int i=0; i<length; i++) {
+ // 0x80 at the beginning of a subidentifier
+ if (encoding[i] == (byte)0x80 &&
+ (i==0 || (encoding[i-1] & 0x80) == 0)) {
+ throw new IOException("ObjectIdentifier() -- " +
+ "Invalid DER encoding, useless extra octet detected");
+ }
+ }
+ }
+ private static void checkCount(int count) throws IOException {
+ if (count < 2) {
+ throw new IOException("ObjectIdentifier() -- " +
+ "Must be at least two oid components ");
+ }
+ }
+ private static void checkFirstComponent(int first) throws IOException {
+ if (first < 0 || first > 2) {
+ throw new IOException("ObjectIdentifier() -- " +
+ "First oid component is invalid ");
+ }
+ }
+ private static void checkFirstComponent(BigInteger first) throws IOException {
+ if (first.signum() == -1 ||
+ first.compareTo(BigInteger.valueOf(2)) == 1) {
+ throw new IOException("ObjectIdentifier() -- " +
+ "First oid component is invalid ");
+ }
+ }
+ private static void checkSecondComponent(int first, int second) throws IOException {
+ if (second < 0 || first != 2 && second > 39) {
+ throw new IOException("ObjectIdentifier() -- " +
+ "Second oid component is invalid ");
+ }
+ }
+ private static void checkSecondComponent(int first, BigInteger second) throws IOException {
+ if (second.signum() == -1 ||
+ first != 2 &&
+ second.compareTo(BigInteger.valueOf(39)) == 1) {
+ throw new IOException("ObjectIdentifier() -- " +
+ "Second oid component is invalid ");
+ }
+ }
+ private static void checkOtherComponent(int i, int num) throws IOException {
+ if (num < 0) {
+ throw new IOException("ObjectIdentifier() -- " +
+ "oid component #" + (i+1) + " must be non-negative ");
+ }
+ }
+ private static void checkOtherComponent(int i, BigInteger num) throws IOException {
+ if (num.signum() == -1) {
+ throw new IOException("ObjectIdentifier() -- " +
+ "oid component #" + (i+1) + " must be non-negative ");
+ }
+ }
}
--- a/test/sun/security/util/Oid/HugeOid.java Fri Mar 20 12:23:45 2009 +0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/*
- * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code 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
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- */
-
-/*
- * @test
- * @author Weijun Wang
- * @bug 4811968
- * @summary ASN.1 cannot handle huge OID components
- */
-
-import sun.security.util.*;
-
-// ATTENTION: This is a temporary test to make sure the partial fix to 4811968
-// will not bring error. After the bug is fully fixed, this test should be
-// removed.
-
-public class HugeOid {
- public static void main(String[] args) throws Exception {
- int failure = 0;
-
- // OID 1.2.2^31-1, we support this now
- byte[] bb = new byte[] {0x06, 0x06, 0x2A, (byte)0x87, (byte)0xff, (byte)0xff, (byte)0xff, 0x7f};
- new ObjectIdentifier(new DerInputStream(bb));
-
- // OID 1.2.2^31, still not supported
- bb = new byte[] {0x06, 0x06, 0x2A, (byte)0x88, (byte)0x80, (byte)0x80, (byte)0x80, 0x00};
- try {
- new ObjectIdentifier(new DerInputStream(bb));
- } catch (Exception e) {
- failure++;
- }
-
- // In fact, the above OID should have already been rejected by the old
- // implementation since 2^31 is negative. Here's another one:
- // OID 1.2.2^32, this one is not negative (it's 0 in 32 bit)
- bb = new byte[] {0x06, 0x06, 0x2A, (byte)0x90, (byte)0x80, (byte)0x80, (byte)0x80, 0x00};
- try {
- new ObjectIdentifier(new DerInputStream(bb));
- } catch (Exception e) {
- failure++;;
- }
-
- if (failure != 2) {
- throw new Exception("Test failed");
- }
- }
-}
--- a/test/sun/security/util/Oid/OidFormat.java Fri Mar 20 12:23:45 2009 +0800
+++ b/test/sun/security/util/Oid/OidFormat.java Fri Mar 20 12:23:47 2009 +0800
@@ -63,7 +63,20 @@
"1.2.3", "1.2.3445",
"1.3.6.1.4.1.42.2.17",
// 4811968: ASN.1 cannot handle huge OID components
- //"2.16.764.1.3101555394.1.0.100.2.1",
+ "2.16.764.1.3101555394.1.0.100.2.1",
+ "2.2726957624935694386592435", // as huge as possible
+ "1.2.777777777777777777",
+ "1.2.888888888888888888.111111111111111.2222222222222.33333333333333333.44444444444444",
+ "1.2." +
+ "1111111111111111111111111111111111111111111111111111111111111." +
+ "2222222222222222222222222222222222222222222222222222222222222222." +
+ "333333333333333333333333333333333333333333333333333333333333333." +
+ "4444444444444444444444444444444444444444444444444444444." +
+ "55555555555555555555555555555555555555555555555555555555555555555555555." +
+ "666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666." +
+ "77777777777777777777777777777777777777777777777777777777777777777777777777." +
+ "8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888." +
+ "9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999",
"1.2.2147483647.4",
"1.2.268435456.4",
};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/util/Oid/S11N.sh Fri Mar 20 12:23:47 2009 +0800
@@ -0,0 +1,164 @@
+#
+# Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code 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
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+# CA 95054 USA or visit www.sun.com if you need additional information or
+# have any questions.
+#
+# @test
+# @bug 4811968
+# @summary Serialization compatibility with old versions
+# @author Weijun Wang
+#
+# set a few environment variables so that the shell-script can run stand-alone
+# in the source directory
+
+if [ "${TESTSRC}" = "" ] ; then
+ TESTSRC="."
+fi
+if [ "${TESTCLASSES}" = "" ] ; then
+ TESTCLASSES="."
+fi
+if [ "${TESTJAVA}" = "" ] ; then
+ echo "TESTJAVA not set. Test cannot execute."
+ echo "FAILED!!!"
+ exit 1
+fi
+
+# set platform-dependent variables
+PF=""
+
+OS=`uname -s`
+case "$OS" in
+ SunOS )
+ FS="/"
+ ARCH=`isainfo`
+ case "$ARCH" in
+ sparc* )
+ PF="solaris-sparc"
+ ;;
+ i[3-6]86 )
+ PF="solaris-i586"
+ ;;
+ amd64* )
+ PF="solaris-amd64"
+ ;;
+ * )
+ echo "Unsupported System: Solaris ${ARCH}"
+ exit 0;
+ ;;
+ esac
+ ;;
+ Linux )
+ ARCH=`uname -m`
+ FS="/"
+ case "$ARCH" in
+ i[3-6]86 )
+ PF="linux-i586"
+ ;;
+ amd64* )
+ PF="linux-amd64"
+ ;;
+ * )
+ echo "Unsupported System: Linux ${ARCH}"
+ exit 0;
+ ;;
+ esac
+ ;;
+ Windows* )
+ FS="\\"
+ PF="windows-i586"
+
+ # 'uname -m' does not give us enough information -
+ # should rely on $PROCESSOR_IDENTIFIER (as is done in Defs-windows.gmk),
+ # but JTREG does not pass this env variable when executing a shell script.
+ #
+ # execute test program - rely on it to exit if platform unsupported
+
+ ;;
+ * )
+ echo "Unsupported System: ${OS}"
+ exit 0;
+ ;;
+esac
+
+# the test code
+
+${TESTJAVA}${FS}bin${FS}javac -source 1.3 -target 1.3 -d . ${TESTSRC}${FS}SerialTest.java || exit 10
+
+OLDJAVA="
+ /java/re/j2se/1.6.0/latest/binaries/${PF}
+ /java/re/j2se/1.5.0/latest/binaries/${PF}
+ /java/re/j2se/1.4.2/latest/binaries/${PF}
+"
+
+SMALL="
+ 0.0
+ 1.1
+ 2.2
+ 1.2.3456
+ 1.2.2147483647.4
+ 1.2.268435456.4
+"
+
+HUGE="
+ 2.16.764.1.3101555394.1.0.100.2.1
+ 1.2.2147483648.4
+ 2.3.4444444444444444444444
+ 1.2.888888888888888888.111111111111111.2222222222222.33333333333333333.44444444444444
+"
+
+for oid in ${SMALL}; do
+ echo ${oid}
+ # new ->
+ ${TESTJAVA}${FS}bin${FS}java SerialTest out ${oid} > tmp.oid.serial || exit 1
+ # -> new
+ ${TESTJAVA}${FS}bin${FS}java SerialTest in ${oid} < tmp.oid.serial || exit 2
+ for oldj in ${OLDJAVA}; do
+ if [ -d ${oldj} ]; then
+ echo ${oldj}
+ # -> old
+ ${oldj}${FS}bin${FS}java SerialTest in ${oid} < tmp.oid.serial || exit 3
+ # old ->
+ ${oldj}${FS}bin${FS}java SerialTest out ${oid} > tmp.oid.serial.old || exit 4
+ # -> new
+ ${TESTJAVA}${FS}bin${FS}java SerialTest in ${oid} < tmp.oid.serial.old || exit 5
+ fi
+ done
+done
+
+for oid in ${HUGE}; do
+ echo ${oid}
+ # new ->
+ ${TESTJAVA}${FS}bin${FS}java SerialTest out ${oid} > tmp.oid.serial || exit 1
+ # -> new
+ ${TESTJAVA}${FS}bin${FS}java SerialTest in ${oid} < tmp.oid.serial || exit 2
+ for oldj in ${OLDJAVA}; do
+ if [ -d ${oldj} ]; then
+ echo ${oldj}
+ # -> old
+ ${oldj}${FS}bin${FS}java SerialTest badin < tmp.oid.serial || exit 3
+ fi
+ done
+done
+
+rm -f tmp.oid.serial
+rm -f tmp.oid.serial.old
+rm -f SerialTest.class
+
+exit 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/util/Oid/SerialTest.java Fri Mar 20 12:23:47 2009 +0800
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * read S11.sh
+ */
+import java.io.*;
+import sun.security.util.*;
+
+/**
+ * Test OID serialization between versions
+ *
+ * java SerialTest out oid // write a OID into System.out
+ * java SerialTest in oid // read from System.in and compare it with oid
+ * java SerialTest badin // make sure *cannot* read from System.in
+ */
+class SerialTest {
+ public static void main(String[] args) throws Exception {
+ if (args[0].equals("out"))
+ out(args[1]);
+ else if (args[0].equals("in"))
+ in(args[1]);
+ else
+ badin();
+ }
+
+ static void in(String oid) throws Exception {
+ ObjectIdentifier o = (ObjectIdentifier) (new ObjectInputStream(System.in).readObject());
+ if (!o.toString().equals(oid))
+ throw new Exception("Read Fail " + o + ", not " + oid);
+ }
+
+ static void badin() throws Exception {
+ boolean pass = true;
+ try {
+ new ObjectInputStream(System.in).readObject();
+ } catch (Exception e) {
+ pass = false;
+ }
+ if (pass) throw new Exception("Should fail but not");
+ }
+
+ static void out(String oid) throws Exception {
+ new ObjectOutputStream(System.out).writeObject(new ObjectIdentifier(oid));
+ }
+}