662 lines · 24.8 KB · Java
RawDownload
/* * Copyright (c) 1996, 2006, Oracle and/or its affiliates. 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.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.util; import java.io.*;import java.math.BigInteger;import java.util.Arrays; /** * 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.1.42.2.17 node in the * hierarchy, and other organizations can easily acquire the ability * to assign such unique identifiers. * * @author David Brownell * @author Amit Kapoor * @author Hemma Prafullchandra */ final publicclass ObjectIdentifier implements Serializable{    /**     * 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;     /**     * 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    {        int ch = '.';        int start = 0;        int end = 0;         int pos = 0;        byte[] tmp = new byte[oid.length()];        int first = 0, second;        int count = 0;         try {            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;                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);        }    }     /**     * Constructor, from an array of integers.     * Validity check included.     */    public ObjectIdentifier (int values []) throws IOException    {        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 }.     *     * <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 (                "ObjectIdentifier() -- data isn't an object ID"                + " (tag = " +  type_id + ")"                );         encoding = new byte[in.getLength()];        in.getBytes(encoding);        check(encoding);    }     /*     * 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    {        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);    }     /**     * 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) {        try {            return new ObjectIdentifier(values);        } catch (IOException ex) {            throw new RuntimeException(ex);            // Should not happen, internal calls always uses legal values.        }    }     /*     * n.b. the only public interface is DerOutputStream.putOID()     */    void encode (DerOutputStream out) throws IOException    {        out.write (DerValue.tag_ObjectId, encoding);    }     /**     * @deprecated Use equals((Object)oid)     */    @Deprecated    public boolean equals(ObjectIdentifier other) {        return equals((Object)other);    }     /**     * Compares this identifier with another, for equality.     *     * @return true iff the names are identical.     */    @Override    public boolean equals(Object obj) {        if (this == obj) {            return true;        }        if (obj instanceof ObjectIdentifier == false) {            return false;        }        ObjectIdentifier other = (ObjectIdentifier)obj;        return Arrays.equals(encoding, other.encoding);    }     @Override    public int hashCode() {        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);    }     /**     * 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.     */    @Override    public String toString() {        String s = stringForm;        if (s == null) {            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;                }            }            s = sb.toString();            stringForm = s;        }        return s;    }     /**     * 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();        }

Lines 1–500 of 662.