Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/util/ObjectIdentifier.java
38830 views
/*1* Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.util;2627import java.io.*;28import java.math.BigInteger;29import java.util.Arrays;3031/**32* Represent an ISO Object Identifier.33*34* <P>Object Identifiers are arbitrary length hierarchical identifiers.35* The individual components are numbers, and they define paths from the36* root of an ISO-managed identifier space. You will sometimes see a37* string name used instead of (or in addition to) the numerical id.38* These are synonyms for the numerical IDs, but are not widely used39* since most sites do not know all the requisite strings, while all40* sites can parse the numeric forms.41*42* <P>So for example, JavaSoft has the sole authority to assign the43* meaning to identifiers below the 1.3.6.1.4.1.42.2.17 node in the44* hierarchy, and other organizations can easily acquire the ability45* to assign such unique identifiers.46*47* @author David Brownell48* @author Amit Kapoor49* @author Hemma Prafullchandra50*/5152final public53class ObjectIdentifier implements Serializable54{55/*56* The maximum encoded OID length, excluding the ASN.1 encoding tag and57* length.58*59* In theory, there is no maximum size for OIDs. However, there are some60* limitation in practice.61*62* RFC 5280 mandates support for OIDs that have arc elements with values63* that are less than 2^28 (that is, they MUST be between 0 and64* 268,435,455, inclusive), and implementations MUST be able to handle65* OIDs with up to 20 elements (inclusive). Per RFC 5280, an encoded66* OID should be less than 80 bytes for safe interoperability.67*68* This class could be used for protocols other than X.509 certificates.69* To be safe, a relatively large but still reasonable value is chosen70* as the restriction in JDK.71*/72private static final int MAXIMUM_OID_SIZE = 4096; // 2^12737475/**76* We use the DER value (no tag, no length) as the internal format77* @serial78*/79private byte[] encoding = null;8081private transient volatile String stringForm;8283/*84* IMPORTANT NOTES FOR CODE CHANGES (bug 4811968) IN JDK 1.7.085* ===========================================================86*87* (Almost) serialization compatibility with old versions:88*89* serialVersionUID is unchanged. Old field "component" is changed to90* type Object so that "poison" (unknown object type for old versions)91* can be put inside if there are huge components that cannot be saved92* as integers.93*94* New version use the new filed "encoding" only.95*96* Below are all 4 cases in a serialization/deserialization process:97*98* 1. old -> old: Not covered here99* 2. old -> new: There's no "encoding" field, new readObject() reads100* "components" and "componentLen" instead and inits correctly.101* 3. new -> new: "encoding" field exists, new readObject() uses it102* (ignoring the other 2 fields) and inits correctly.103* 4. new -> old: old readObject() only recognizes "components" and104* "componentLen" fields. If no huge components are involved, they105* are serialized as legal values and old object can init correctly.106* Otherwise, old object cannot recognize the form (component not int[])107* and throw a ClassNotFoundException at deserialization time.108*109* Therfore, for the first 3 cases, exact compatibility is preserved. In110* the 4th case, non-huge OID is still supportable in old versions, while111* huge OID is not.112*/113private static final long serialVersionUID = 8697030238860181294L;114115/**116* Changed to Object117* @serial118*/119private Object components = null; // path from root120/**121* @serial122*/123private int componentLen = -1; // how much is used.124125// Is the components field calculated?126transient private boolean componentsCalculated = false;127128private void readObject(ObjectInputStream is)129throws IOException, ClassNotFoundException {130is.defaultReadObject();131132if (encoding == null) { // from an old version133int[] comp = (int[])components;134if (componentLen > comp.length) {135componentLen = comp.length;136}137138// Check the estimated size before it is too later.139checkOidSize(componentLen);140141init(comp, componentLen);142} else {143checkOidSize(encoding.length);144}145}146147private void writeObject(ObjectOutputStream os)148throws IOException {149if (!componentsCalculated) {150int[] comps = toIntArray();151if (comps != null) { // every one understands this152components = comps;153componentLen = comps.length;154} else {155components = HugeOidNotSupportedByOldJDK.theOne;156}157componentsCalculated = true;158}159os.defaultWriteObject();160}161162static class HugeOidNotSupportedByOldJDK implements Serializable {163private static final long serialVersionUID = 1L;164static HugeOidNotSupportedByOldJDK theOne = new HugeOidNotSupportedByOldJDK();165}166167/**168* Constructs, from a string. This string should be of the form 1.23.56.169* Validity check included.170*/171public ObjectIdentifier (String oid) throws IOException172{173int ch = '.';174int start = 0;175int end = 0;176177int pos = 0;178byte[] tmp = new byte[oid.length()];179int first = 0, second;180int count = 0;181182try {183String comp = null;184do {185int length = 0; // length of one section186end = oid.indexOf(ch,start);187if (end == -1) {188comp = oid.substring(start);189length = oid.length() - start;190} else {191comp = oid.substring(start,end);192length = end - start;193}194195if (length > 9) {196BigInteger bignum = new BigInteger(comp);197if (count == 0) {198checkFirstComponent(bignum);199first = bignum.intValue();200} else {201if (count == 1) {202checkSecondComponent(first, bignum);203bignum = bignum.add(BigInteger.valueOf(40*first));204} else {205checkOtherComponent(count, bignum);206}207pos += pack7Oid(bignum, tmp, pos);208}209} else {210int num = Integer.parseInt(comp);211if (count == 0) {212checkFirstComponent(num);213first = num;214} else {215if (count == 1) {216checkSecondComponent(first, num);217num += 40 * first;218} else {219checkOtherComponent(count, num);220}221pos += pack7Oid(num, tmp, pos);222}223}224start = end + 1;225count++;226227checkOidSize(pos);228} while (end != -1);229230checkCount(count);231encoding = new byte[pos];232System.arraycopy(tmp, 0, encoding, 0, pos);233this.stringForm = oid;234} catch (IOException ioe) { // already detected by checkXXX235throw ioe;236} catch (Exception e) {237throw new IOException("ObjectIdentifier() -- Invalid format: "238+ e.toString(), e);239}240}241242/**243* Constructor, from an array of integers.244* Validity check included.245*/246public ObjectIdentifier (int values []) throws IOException247{248checkCount(values.length);249checkFirstComponent(values[0]);250checkSecondComponent(values[0], values[1]);251for (int i=2; i<values.length; i++)252checkOtherComponent(i, values[i]);253init(values, values.length);254}255256/**257* Constructor, from an ASN.1 encoded input stream.258* Validity check NOT included.259* The encoding of the ID in the stream uses "DER", a BER/1 subset.260* In this case, that means a triple { typeId, length, data }.261*262* <P><STRONG>NOTE:</STRONG> When an exception is thrown, the263* input stream has not been returned to its "initial" state.264*265* @param in DER-encoded data holding an object ID266* @exception IOException indicates a decoding error267*/268public ObjectIdentifier (DerInputStream in) throws IOException269{270byte type_id;271int bufferEnd;272273/*274* Object IDs are a "universal" type, and their tag needs only275* one byte of encoding. Verify that the tag of this datum276* is that of an object ID.277*278* Then get and check the length of the ID's encoding. We set279* up so that we can use in.available() to check for the end of280* this value in the data stream.281*/282type_id = (byte) in.getByte ();283if (type_id != DerValue.tag_ObjectId)284throw new IOException (285"ObjectIdentifier() -- data isn't an object ID"286+ " (tag = " + type_id + ")"287);288289int len = in.getDefiniteLength();290checkOidSize(len);291if (len > in.available()) {292throw new IOException("ObjectIdentifier length exceeds " +293"data available. Length: " + len + ", Available: " +294in.available());295}296297encoding = new byte[len];298in.getBytes(encoding);299check(encoding);300}301302/*303* Constructor, from the rest of a DER input buffer;304* the tag and length have been removed/verified305* Validity check NOT included.306*/307ObjectIdentifier (DerInputBuffer buf) throws IOException308{309DerInputStream in = new DerInputStream(buf);310int len = in.available();311checkOidSize(len);312313encoding = new byte[len];314in.getBytes(encoding);315check(encoding);316}317318private void init(int[] components, int length) throws IOException {319int pos = 0;320byte[] tmp = new byte[length * 5 + 1]; // +1 for empty input321322if (components[1] < Integer.MAX_VALUE - components[0] * 40) {323pos += pack7Oid(components[0] * 40 + components[1], tmp, pos);324} else {325BigInteger big = BigInteger.valueOf(components[1]);326big = big.add(BigInteger.valueOf(components[0] * 40));327pos += pack7Oid(big, tmp, pos);328}329330for (int i = 2; i < length; i++) {331pos += pack7Oid(components[i], tmp, pos);332333checkOidSize(pos);334}335336encoding = new byte[pos];337System.arraycopy(tmp, 0, encoding, 0, pos);338}339340/**341* This method is kept for compatibility reasons. The new implementation342* does the check and conversion. All around the JDK, the method is called343* in static blocks to initialize pre-defined ObjectIdentifieies. No344* obvious performance hurt will be made after this change.345*346* Old doc: Create a new ObjectIdentifier for internal use. The values are347* neither checked nor cloned.348*/349public static ObjectIdentifier newInternal(int[] values) {350try {351return new ObjectIdentifier(values);352} catch (IOException ex) {353throw new RuntimeException(ex);354// Should not happen, internal calls always uses legal values.355}356}357358/*359* n.b. the only public interface is DerOutputStream.putOID()360*/361void encode (DerOutputStream out) throws IOException362{363out.write (DerValue.tag_ObjectId, encoding);364}365366/**367* @deprecated Use equals((Object)oid)368*/369@Deprecated370public boolean equals(ObjectIdentifier other) {371return equals((Object)other);372}373374/**375* Compares this identifier with another, for equality.376*377* @return true iff the names are identical.378*/379@Override380public boolean equals(Object obj) {381if (this == obj) {382return true;383}384if (obj instanceof ObjectIdentifier == false) {385return false;386}387ObjectIdentifier other = (ObjectIdentifier)obj;388return Arrays.equals(encoding, other.encoding);389}390391@Override392public int hashCode() {393return Arrays.hashCode(encoding);394}395396/**397* Private helper method for serialization. To be compatible with old398* versions of JDK.399* @return components in an int array, if all the components are less than400* Integer.MAX_VALUE. Otherwise, null.401*/402private int[] toIntArray() {403int length = encoding.length;404int[] result = new int[20];405int which = 0;406int fromPos = 0;407for (int i = 0; i < length; i++) {408if ((encoding[i] & 0x80) == 0) {409// one section [fromPos..i]410if (i - fromPos + 1 > 4) {411BigInteger big = new BigInteger(pack(encoding, fromPos, i-fromPos+1, 7, 8));412if (fromPos == 0) {413result[which++] = 2;414BigInteger second = big.subtract(BigInteger.valueOf(80));415if (second.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) == 1) {416return null;417} else {418result[which++] = second.intValue();419}420} else {421if (big.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) == 1) {422return null;423} else {424result[which++] = big.intValue();425}426}427} else {428int retval = 0;429for (int j = fromPos; j <= i; j++) {430retval <<= 7;431byte tmp = encoding[j];432retval |= (tmp & 0x07f);433}434if (fromPos == 0) {435if (retval < 80) {436result[which++] = retval / 40;437result[which++] = retval % 40;438} else {439result[which++] = 2;440result[which++] = retval - 80;441}442} else {443result[which++] = retval;444}445}446fromPos = i+1;447}448if (which >= result.length) {449result = Arrays.copyOf(result, which + 10);450}451}452return Arrays.copyOf(result, which);453}454455/**456* Returns a string form of the object ID. The format is the457* conventional "dot" notation for such IDs, without any458* user-friendly descriptive strings, since those strings459* will not be understood everywhere.460*/461@Override462public String toString() {463String s = stringForm;464if (s == null) {465int length = encoding.length;466StringBuffer sb = new StringBuffer(length * 4);467468int fromPos = 0;469for (int i = 0; i < length; i++) {470if ((encoding[i] & 0x80) == 0) {471// one section [fromPos..i]472if (fromPos != 0) { // not the first segment473sb.append('.');474}475if (i - fromPos + 1 > 4) { // maybe big integer476BigInteger big = new BigInteger(pack(encoding, fromPos, i-fromPos+1, 7, 8));477if (fromPos == 0) {478// first section encoded with more than 4 bytes,479// must be 2.something480sb.append("2.");481sb.append(big.subtract(BigInteger.valueOf(80)));482} else {483sb.append(big);484}485} else { // small integer486int retval = 0;487for (int j = fromPos; j <= i; j++) {488retval <<= 7;489byte tmp = encoding[j];490retval |= (tmp & 0x07f);491}492if (fromPos == 0) {493if (retval < 80) {494sb.append(retval/40);495sb.append('.');496sb.append(retval%40);497} else {498sb.append("2.");499sb.append(retval - 80);500}501} else {502sb.append(retval);503}504}505fromPos = i+1;506}507}508s = sb.toString();509stringForm = s;510}511return s;512}513514/**515* Repack all bits from input to output. On the both sides, only a portion516* (from the least significant bit) of the 8 bits in a byte is used. This517* number is defined as the number of useful bits (NUB) for the array. All the518* used bits from the input byte array and repacked into the output in the519* exactly same order. The output bits are aligned so that the final bit of520* the input (the least significant bit in the last byte), when repacked as521* the final bit of the output, is still at the least significant position.522* Zeroes will be padded on the left side of the first output byte if523* necessary. All unused bits in the output are also zeroed.524*525* For example: if the input is 01001100 with NUB 8, the output which526* has a NUB 6 will look like:527* 00000001 00001100528* The first 2 bits of the output bytes are unused bits. The other bits529* turn out to be 000001 001100. While the 8 bits on the right are from530* the input, the left 4 zeroes are padded to fill the 6 bits space.531*532* @param in the input byte array533* @param ioffset start point inside <code>in</code>534* @param ilength number of bytes to repack535* @param iw NUB for input536* @param ow NUB for output537* @return the repacked bytes538*/539private static byte[] pack(byte[] in, int ioffset, int ilength, int iw, int ow) {540assert (iw > 0 && iw <= 8): "input NUB must be between 1 and 8";541assert (ow > 0 && ow <= 8): "output NUB must be between 1 and 8";542543if (iw == ow) {544return in.clone();545}546547int bits = ilength * iw; // number of all used bits548byte[] out = new byte[(bits+ow-1)/ow];549550// starting from the 0th bit in the input551int ipos = 0;552553// the number of padding 0's needed in the output, skip them554int opos = (bits+ow-1)/ow*ow-bits;555556while(ipos < bits) {557int count = iw - ipos%iw; // unpacked bits in current input byte558if (count > ow - opos%ow) { // free space available in output byte559count = ow - opos%ow; // choose the smaller number560}561// and move them!562out[opos/ow] |= // paste!563(((in[ioffset+ipos/iw]+256) // locate the byte (+256 so that it's never negative)564>> (iw-ipos%iw-count)) // move to the end of a byte565& ((1 << (count))-1)) // zero out all other bits566<< (ow-opos%ow-count); // move to the output position567ipos += count; // advance568opos += count; // advance569}570return out;571}572573/**574* Repack from NUB 8 to a NUB 7 OID sub-identifier, remove all575* unnecessary 0 headings, set the first bit of all non-tail576* output bytes to 1 (as ITU-T Rec. X.690 8.19.2 says), and577* paste it into an existing byte array.578* @param out the existing array to be pasted into579* @param ooffset the starting position to paste580* @return the number of bytes pasted581*/582private static int pack7Oid(byte[] in, int ioffset, int ilength, byte[] out, int ooffset) {583byte[] pack = pack(in, ioffset, ilength, 8, 7);584int firstNonZero = pack.length-1; // paste at least one byte585for (int i=pack.length-2; i>=0; i--) {586if (pack[i] != 0) {587firstNonZero = i;588}589pack[i] |= 0x80;590}591System.arraycopy(pack, firstNonZero, out, ooffset, pack.length-firstNonZero);592return pack.length-firstNonZero;593}594595/**596* Repack from NUB 7 to NUB 8, remove all unnecessary 0597* headings, and paste it into an existing byte array.598* @param out the existing array to be pasted into599* @param ooffset the starting position to paste600* @return the number of bytes pasted601*/602private static int pack8(byte[] in, int ioffset, int ilength, byte[] out, int ooffset) {603byte[] pack = pack(in, ioffset, ilength, 7, 8);604int firstNonZero = pack.length-1; // paste at least one byte605for (int i=pack.length-2; i>=0; i--) {606if (pack[i] != 0) {607firstNonZero = i;608}609}610System.arraycopy(pack, firstNonZero, out, ooffset, pack.length-firstNonZero);611return pack.length-firstNonZero;612}613614/**615* Pack the int into a OID sub-identifier DER encoding616*/617private static int pack7Oid(int input, byte[] out, int ooffset) {618byte[] b = new byte[4];619b[0] = (byte)(input >> 24);620b[1] = (byte)(input >> 16);621b[2] = (byte)(input >> 8);622b[3] = (byte)(input);623return pack7Oid(b, 0, 4, out, ooffset);624}625626/**627* Pack the BigInteger into a OID subidentifier DER encoding628*/629private static int pack7Oid(BigInteger input, byte[] out, int ooffset) {630byte[] b = input.toByteArray();631return pack7Oid(b, 0, b.length, out, ooffset);632}633634/**635* Private methods to check validity of OID. They must be --636* 1. at least 2 components637* 2. all components must be non-negative638* 3. the first must be 0, 1 or 2639* 4. if the first is 0 or 1, the second must be <40640*/641642/**643* Check the DER encoding. Since DER encoding defines that the integer bits644* are unsigned, so there's no need to check the MSB.645*/646private static void check(byte[] encoding) throws IOException {647int length = encoding.length;648if (length < 1 || // too short649(encoding[length - 1] & 0x80) != 0) { // not ended650throw new IOException("ObjectIdentifier() -- " +651"Invalid DER encoding, not ended");652}653for (int i=0; i<length; i++) {654// 0x80 at the beginning of a subidentifier655if (encoding[i] == (byte)0x80 &&656(i==0 || (encoding[i-1] & 0x80) == 0)) {657throw new IOException("ObjectIdentifier() -- " +658"Invalid DER encoding, useless extra octet detected");659}660}661}662private static void checkCount(int count) throws IOException {663if (count < 2) {664throw new IOException("ObjectIdentifier() -- " +665"Must be at least two oid components ");666}667}668private static void checkFirstComponent(int first) throws IOException {669if (first < 0 || first > 2) {670throw new IOException("ObjectIdentifier() -- " +671"First oid component is invalid ");672}673}674private static void checkFirstComponent(BigInteger first) throws IOException {675if (first.signum() == -1 ||676first.compareTo(BigInteger.valueOf(2)) == 1) {677throw new IOException("ObjectIdentifier() -- " +678"First oid component is invalid ");679}680}681private static void checkSecondComponent(int first, int second) throws IOException {682if (second < 0 || first != 2 && second > 39) {683throw new IOException("ObjectIdentifier() -- " +684"Second oid component is invalid ");685}686}687private static void checkSecondComponent(int first, BigInteger second) throws IOException {688if (second.signum() == -1 ||689first != 2 &&690second.compareTo(BigInteger.valueOf(39)) == 1) {691throw new IOException("ObjectIdentifier() -- " +692"Second oid component is invalid ");693}694}695private static void checkOtherComponent(int i, int num) throws IOException {696if (num < 0) {697throw new IOException("ObjectIdentifier() -- " +698"oid component #" + (i+1) + " must be non-negative ");699}700}701private static void checkOtherComponent(int i, BigInteger num) throws IOException {702if (num.signum() == -1) {703throw new IOException("ObjectIdentifier() -- " +704"oid component #" + (i+1) + " must be non-negative ");705}706}707708private static void checkOidSize(int oidLength) throws IOException {709if (oidLength > MAXIMUM_OID_SIZE) {710throw new IOException(711"ObjectIdentifier encoded length exceeds " +712"the restriction in JDK (OId length(>=): " + oidLength +713", Restriction: " + MAXIMUM_OID_SIZE + ")");714}715}716}717718719