4811968: ASN.1 (X509Certificate) implementations don't handle large OID components
Reviewed-by: xuelei
This commit is contained in:
parent
a2a7ded4d4
commit
67bb035b94
@ -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,22 +45,99 @@ import java.io.*;
|
||||
* 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 value (no tag, no length) 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
|
||||
/**
|
||||
* @serial
|
||||
*/
|
||||
private int componentLen = -1; // how much is used.
|
||||
|
||||
// Is the components field calculated?
|
||||
transient private boolean componentsCalculated = false;
|
||||
|
||||
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 (!componentsCalculated) {
|
||||
int[] comps = toIntArray();
|
||||
if (comps != null) { // every one understands this
|
||||
components = comps;
|
||||
componentLen = comps.length;
|
||||
} else {
|
||||
components = HugeOidNotSupportedByOldJDK.theOne;
|
||||
}
|
||||
componentsCalculated = true;
|
||||
}
|
||||
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
|
||||
{
|
||||
@ -67,82 +145,86 @@ class ObjectIdentifier implements Serializable
|
||||
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];
|
||||
int pos = 0;
|
||||
byte[] tmp = new byte[oid.length()];
|
||||
int first = 0, second;
|
||||
int count = 0;
|
||||
|
||||
start = 0;
|
||||
int i = 0;
|
||||
String comp = null;
|
||||
try {
|
||||
while ((end = oid.indexOf(ch,start)) != -1) {
|
||||
comp = oid.substring(start,end);
|
||||
components[i++] = Integer.valueOf(comp).intValue();
|
||||
start = end + 1;
|
||||
}
|
||||
String comp = null;
|
||||
do {
|
||||
int length = 0; // length of one section
|
||||
end = oid.indexOf(ch,start);
|
||||
if (end == -1) {
|
||||
comp = oid.substring(start);
|
||||
components[i] = Integer.valueOf(comp).intValue();
|
||||
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;
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an object ID from an ASN.1 encoded input stream.
|
||||
* 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 @@ class ObjectIdentifier implements Serializable
|
||||
* @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,215 +255,67 @@ class ObjectIdentifier implements Serializable
|
||||
+ " (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);
|
||||
DerInputStream in = new DerInputStream(buf);
|
||||
encoding = new byte[in.available()];
|
||||
in.getBytes(encoding);
|
||||
check(encoding);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ObjectIdentifier for internal use. The values are
|
||||
* This method is kept for compatibility reasons. The new implementation
|
||||
* does the check and conversion. All around the JDK, the method is called
|
||||
* in static blocks to initialize pre-defined ObjectIdentifieies. No
|
||||
* obvious performance hurt will be made after this change.
|
||||
*
|
||||
* Old doc: Create a new ObjectIdentifier for internal use. The values are
|
||||
* 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;
|
||||
try {
|
||||
return new ObjectIdentifier(values);
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
// Should not happen, internal calls always uses legal values.
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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 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 ("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;
|
||||
// TODO: val must be <128*128*128*128 here, otherwise, 4 bytes is not
|
||||
// enough to hold it. Will address this later.
|
||||
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;
|
||||
out.write (DerValue.tag_ObjectId, encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -398,6 +331,7 @@ class ObjectIdentifier implements Serializable
|
||||
*
|
||||
* @return true iff the names are identical.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
@ -406,23 +340,71 @@ class ObjectIdentifier implements Serializable
|
||||
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 Arrays.hashCode(encoding);
|
||||
}
|
||||
return h;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -431,15 +413,52 @@ class ObjectIdentifier implements Serializable
|
||||
* 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) {
|
||||
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('.');
|
||||
}
|
||||
sb.append(components[i]);
|
||||
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;
|
||||
}
|
||||
}
|
||||
s = sb.toString();
|
||||
stringForm = s;
|
||||
@ -447,15 +466,197 @@ class ObjectIdentifier implements Serializable
|
||||
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.
|
||||
/**
|
||||
* 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 int components []; // path from root
|
||||
private int componentLen; // how much is used.
|
||||
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";
|
||||
|
||||
private transient volatile String stringForm;
|
||||
if (iw == ow) {
|
||||
return in.clone();
|
||||
}
|
||||
|
||||
private static final int allocationQuantum = 5; // >= 2
|
||||
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 sub-identifier, 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 sub-identifier 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 components
|
||||
* 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 ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,9 +63,22 @@ public class OidFormat {
|
||||
"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",
|
||||
//"1.2.2147483647.4",
|
||||
//"1.2.268435456.4",
|
||||
"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",
|
||||
};
|
||||
|
||||
for (String s: goodOids) {
|
||||
|
164
jdk/test/sun/security/util/Oid/S11N.sh
Normal file
164
jdk/test/sun/security/util/Oid/S11N.sh
Normal file
@ -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
|
66
jdk/test/sun/security/util/Oid/SerialTest.java
Normal file
66
jdk/test/sun/security/util/Oid/SerialTest.java
Normal file
@ -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));
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user