1 /* 2 * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.util; 27 28 import java.io.*; 29 import java.math.BigInteger; 30 import java.util.Arrays; 31 32 /** 33 * Represent an ISO Object Identifier. 34 * 35 * <P>Object Identifiers are arbitrary length hierarchical identifiers. 36 * The individual components are numbers, and they define paths from the 37 * root of an ISO-managed identifier space. You will sometimes see a 38 * string name used instead of (or in addition to) the numerical id. 39 * These are synonyms for the numerical IDs, but are not widely used 40 * since most sites do not know all the requisite strings, while all 41 * sites can parse the numeric forms. 42 * 43 * <P>So for example, JavaSoft has the sole authority to assign the 44 * meaning to identifiers below the 1.3.6.1.4.1.42.2.17 node in the 45 * hierarchy, and other organizations can easily acquire the ability 46 * to assign such unique identifiers. 47 * 48 * @author David Brownell 49 * @author Amit Kapoor 50 * @author Hemma Prafullchandra 51 */ 52 final public 53 class ObjectIdentifier implements Serializable 54 { 55 /** 56 * We use the DER value (no tag, no length) as the internal format 57 * @serial 58 */ 59 private byte[] encoding = null; 60 61 private transient volatile String stringForm; 62 63 /* 64 * IMPORTANT NOTES FOR CODE CHANGES (bug 4811968) IN JDK 1.7.0 65 * =========================================================== 66 * 67 * (Almost) serialization compatibility with old versions: 68 * 69 * serialVersionUID is unchanged. Old field "component" is changed to 70 * type Object so that "poison" (unknown object type for old versions) 71 * can be put inside if there are huge components that cannot be saved 72 * as integers. 73 * 74 * New version use the new filed "encoding" only. 75 * 76 * Below are all 4 cases in a serialization/deserialization process: 77 * 78 * 1. old -> old: Not covered here 79 * 2. old -> new: There's no "encoding" field, new readObject() reads 80 * "components" and "componentLen" instead and inits correctly. 81 * 3. new -> new: "encoding" field exists, new readObject() uses it 82 * (ignoring the other 2 fields) and inits correctly. 83 * 4. new -> old: old readObject() only recognizes "components" and 84 * "componentLen" fields. If no huge components are involved, they 85 * are serialized as legal values and old object can init correctly. 86 * Otherwise, old object cannot recognize the form (component not int[]) 87 * and throw a ClassNotFoundException at deserialization time. 88 * 89 * Therfore, for the first 3 cases, exact compatibility is preserved. In 90 * the 4th case, non-huge OID is still supportable in old versions, while 91 * huge OID is not. 92 */ 93 private static final long serialVersionUID = 8697030238860181294L; 94 95 /** 96 * Changed to Object 97 * @serial 98 */ 99 private Object components = null; // path from root 100 /** 101 * @serial 102 */ 103 private int componentLen = -1; // how much is used. 104 105 // Is the components field calculated? 106 transient private boolean componentsCalculated = false; 107 readObject(ObjectInputStream is)108 private void readObject(ObjectInputStream is) 109 throws IOException, ClassNotFoundException { 110 is.defaultReadObject(); 111 112 if (encoding == null) { // from an old version 113 init((int[])components, componentLen); 114 } 115 } 116 writeObject(ObjectOutputStream os)117 private void writeObject(ObjectOutputStream os) 118 throws IOException { 119 if (!componentsCalculated) { 120 int[] comps = toIntArray(); 121 if (comps != null) { // every one understands this 122 components = comps; 123 componentLen = comps.length; 124 } else { 125 components = HugeOidNotSupportedByOldJDK.theOne; 126 } 127 componentsCalculated = true; 128 } 129 os.defaultWriteObject(); 130 } 131 132 static class HugeOidNotSupportedByOldJDK implements Serializable { 133 private static final long serialVersionUID = 1L; 134 static HugeOidNotSupportedByOldJDK theOne = new HugeOidNotSupportedByOldJDK(); 135 } 136 137 /** 138 * Constructs, from a string. This string should be of the form 1.23.56. 139 * Validity check included. 140 */ ObjectIdentifier(String oid)141 public ObjectIdentifier (String oid) throws IOException 142 { 143 int ch = '.'; 144 int start = 0; 145 int end = 0; 146 147 int pos = 0; 148 byte[] tmp = new byte[oid.length()]; 149 int first = 0, second; 150 int count = 0; 151 152 try { 153 String comp = null; 154 do { 155 int length = 0; // length of one section 156 end = oid.indexOf(ch,start); 157 if (end == -1) { 158 comp = oid.substring(start); 159 length = oid.length() - start; 160 } else { 161 comp = oid.substring(start,end); 162 length = end - start; 163 } 164 165 if (length > 9) { 166 BigInteger bignum = new BigInteger(comp); 167 if (count == 0) { 168 checkFirstComponent(bignum); 169 first = bignum.intValue(); 170 } else { 171 if (count == 1) { 172 checkSecondComponent(first, bignum); 173 bignum = bignum.add(BigInteger.valueOf(40*first)); 174 } else { 175 checkOtherComponent(count, bignum); 176 } 177 pos += pack7Oid(bignum, tmp, pos); 178 } 179 } else { 180 int num = Integer.parseInt(comp); 181 if (count == 0) { 182 checkFirstComponent(num); 183 first = num; 184 } else { 185 if (count == 1) { 186 checkSecondComponent(first, num); 187 num += 40 * first; 188 } else { 189 checkOtherComponent(count, num); 190 } 191 pos += pack7Oid(num, tmp, pos); 192 } 193 } 194 start = end + 1; 195 count++; 196 } while (end != -1); 197 198 checkCount(count); 199 encoding = new byte[pos]; 200 System.arraycopy(tmp, 0, encoding, 0, pos); 201 this.stringForm = oid; 202 } catch (IOException ioe) { // already detected by checkXXX 203 throw ioe; 204 } catch (Exception e) { 205 throw new IOException("ObjectIdentifier() -- Invalid format: " 206 + e.toString(), e); 207 } 208 } 209 210 /** 211 * Constructor, from an array of integers. 212 * Validity check included. 213 */ ObjectIdentifier(int values [])214 public ObjectIdentifier (int values []) throws IOException 215 { 216 checkCount(values.length); 217 checkFirstComponent(values[0]); 218 checkSecondComponent(values[0], values[1]); 219 for (int i=2; i<values.length; i++) 220 checkOtherComponent(i, values[i]); 221 init(values, values.length); 222 } 223 224 /** 225 * Constructor, from an ASN.1 encoded input stream. 226 * Validity check NOT included. 227 * The encoding of the ID in the stream uses "DER", a BER/1 subset. 228 * In this case, that means a triple { typeId, length, data }. 229 * 230 * <P><STRONG>NOTE:</STRONG> When an exception is thrown, the 231 * input stream has not been returned to its "initial" state. 232 * 233 * @param in DER-encoded data holding an object ID 234 * @exception IOException indicates a decoding error 235 */ ObjectIdentifier(DerInputStream in)236 public ObjectIdentifier (DerInputStream in) throws IOException 237 { 238 byte type_id; 239 int bufferEnd; 240 241 /* 242 * Object IDs are a "universal" type, and their tag needs only 243 * one byte of encoding. Verify that the tag of this datum 244 * is that of an object ID. 245 * 246 * Then get and check the length of the ID's encoding. We set 247 * up so that we can use in.available() to check for the end of 248 * this value in the data stream. 249 */ 250 type_id = (byte) in.getByte (); 251 if (type_id != DerValue.tag_ObjectId) 252 throw new IOException ( 253 "ObjectIdentifier() -- data isn't an object ID" 254 + " (tag = " + type_id + ")" 255 ); 256 257 int len = in.getLength(); 258 if (len > in.available()) { 259 throw new IOException("ObjectIdentifier() -- length exceeds" + 260 "data available. Length: " + len + ", Available: " + 261 in.available()); 262 } 263 encoding = new byte[len]; 264 in.getBytes(encoding); 265 check(encoding); 266 } 267 268 /* 269 * Constructor, from the rest of a DER input buffer; 270 * the tag and length have been removed/verified 271 * Validity check NOT included. 272 */ ObjectIdentifier(DerInputBuffer buf)273 ObjectIdentifier (DerInputBuffer buf) throws IOException 274 { 275 DerInputStream in = new DerInputStream(buf); 276 encoding = new byte[in.available()]; 277 in.getBytes(encoding); 278 check(encoding); 279 } 280 init(int[] components, int length)281 private void init(int[] components, int length) { 282 int pos = 0; 283 byte[] tmp = new byte[length*5+1]; // +1 for empty input 284 285 if (components[1] < Integer.MAX_VALUE - components[0]*40) 286 pos += pack7Oid(components[0]*40+components[1], tmp, pos); 287 else { 288 BigInteger big = BigInteger.valueOf(components[1]); 289 big = big.add(BigInteger.valueOf(components[0]*40)); 290 pos += pack7Oid(big, tmp, pos); 291 } 292 293 for (int i=2; i<length; i++) { 294 pos += pack7Oid(components[i], tmp, pos); 295 } 296 encoding = new byte[pos]; 297 System.arraycopy(tmp, 0, encoding, 0, pos); 298 } 299 300 /** 301 * This method is kept for compatibility reasons. The new implementation 302 * does the check and conversion. All around the JDK, the method is called 303 * in static blocks to initialize pre-defined ObjectIdentifieies. No 304 * obvious performance hurt will be made after this change. 305 * 306 * Old doc: Create a new ObjectIdentifier for internal use. The values are 307 * neither checked nor cloned. 308 */ newInternal(int[] values)309 public static ObjectIdentifier newInternal(int[] values) { 310 try { 311 return new ObjectIdentifier(values); 312 } catch (IOException ex) { 313 throw new RuntimeException(ex); 314 // Should not happen, internal calls always uses legal values. 315 } 316 } 317 318 /* 319 * n.b. the only public interface is DerOutputStream.putOID() 320 */ encode(DerOutputStream out)321 void encode (DerOutputStream out) throws IOException 322 { 323 out.write (DerValue.tag_ObjectId, encoding); 324 } 325 326 /** 327 * @deprecated Use equals((Object)oid) 328 */ 329 @Deprecated equals(ObjectIdentifier other)330 public boolean equals(ObjectIdentifier other) { 331 return equals((Object)other); 332 } 333 334 /** 335 * Compares this identifier with another, for equality. 336 * 337 * @return true iff the names are identical. 338 */ 339 @Override equals(Object obj)340 public boolean equals(Object obj) { 341 if (this == obj) { 342 return true; 343 } 344 if (obj instanceof ObjectIdentifier == false) { 345 return false; 346 } 347 ObjectIdentifier other = (ObjectIdentifier)obj; 348 return Arrays.equals(encoding, other.encoding); 349 } 350 351 @Override hashCode()352 public int hashCode() { 353 return Arrays.hashCode(encoding); 354 } 355 356 /** 357 * Private helper method for serialization. To be compatible with old 358 * versions of JDK. 359 * @return components in an int array, if all the components are less than 360 * Integer.MAX_VALUE. Otherwise, null. 361 */ 362 // Android-changed: s/private/public: Needed to keep sort order of RDN from prev impl toIntArray()363 public int[] toIntArray() { 364 int length = encoding.length; 365 int[] result = new int[20]; 366 int which = 0; 367 int fromPos = 0; 368 for (int i = 0; i < length; i++) { 369 if ((encoding[i] & 0x80) == 0) { 370 // one section [fromPos..i] 371 if (i - fromPos + 1 > 4) { 372 BigInteger big = new BigInteger(pack(encoding, fromPos, i-fromPos+1, 7, 8)); 373 if (fromPos == 0) { 374 result[which++] = 2; 375 BigInteger second = big.subtract(BigInteger.valueOf(80)); 376 if (second.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) == 1) { 377 return null; 378 } else { 379 result[which++] = second.intValue(); 380 } 381 } else { 382 if (big.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) == 1) { 383 return null; 384 } else { 385 result[which++] = big.intValue(); 386 } 387 } 388 } else { 389 int retval = 0; 390 for (int j = fromPos; j <= i; j++) { 391 retval <<= 7; 392 byte tmp = encoding[j]; 393 retval |= (tmp & 0x07f); 394 } 395 if (fromPos == 0) { 396 if (retval < 80) { 397 result[which++] = retval / 40; 398 result[which++] = retval % 40; 399 } else { 400 result[which++] = 2; 401 result[which++] = retval - 80; 402 } 403 } else { 404 result[which++] = retval; 405 } 406 } 407 fromPos = i+1; 408 } 409 if (which >= result.length) { 410 result = Arrays.copyOf(result, which + 10); 411 } 412 } 413 return Arrays.copyOf(result, which); 414 } 415 416 /** 417 * Returns a string form of the object ID. The format is the 418 * conventional "dot" notation for such IDs, without any 419 * user-friendly descriptive strings, since those strings 420 * will not be understood everywhere. 421 */ 422 @Override toString()423 public String toString() { 424 String s = stringForm; 425 if (s == null) { 426 int length = encoding.length; 427 StringBuffer sb = new StringBuffer(length * 4); 428 429 int fromPos = 0; 430 for (int i = 0; i < length; i++) { 431 if ((encoding[i] & 0x80) == 0) { 432 // one section [fromPos..i] 433 if (fromPos != 0) { // not the first segment 434 sb.append('.'); 435 } 436 if (i - fromPos + 1 > 4) { // maybe big integer 437 BigInteger big = new BigInteger(pack(encoding, fromPos, i-fromPos+1, 7, 8)); 438 if (fromPos == 0) { 439 // first section encoded with more than 4 bytes, 440 // must be 2.something 441 sb.append("2."); 442 sb.append(big.subtract(BigInteger.valueOf(80))); 443 } else { 444 sb.append(big); 445 } 446 } else { // small integer 447 int retval = 0; 448 for (int j = fromPos; j <= i; j++) { 449 retval <<= 7; 450 byte tmp = encoding[j]; 451 retval |= (tmp & 0x07f); 452 } 453 if (fromPos == 0) { 454 if (retval < 80) { 455 sb.append(retval/40); 456 sb.append('.'); 457 sb.append(retval%40); 458 } else { 459 sb.append("2."); 460 sb.append(retval - 80); 461 } 462 } else { 463 sb.append(retval); 464 } 465 } 466 fromPos = i+1; 467 } 468 } 469 s = sb.toString(); 470 stringForm = s; 471 } 472 return s; 473 } 474 475 /** 476 * Repack all bits from input to output. On the both sides, only a portion 477 * (from the least significant bit) of the 8 bits in a byte is used. This 478 * number is defined as the number of useful bits (NUB) for the array. All the 479 * used bits from the input byte array and repacked into the output in the 480 * exactly same order. The output bits are aligned so that the final bit of 481 * the input (the least significant bit in the last byte), when repacked as 482 * the final bit of the output, is still at the least significant position. 483 * Zeroes will be padded on the left side of the first output byte if 484 * necessary. All unused bits in the output are also zeroed. 485 * 486 * For example: if the input is 01001100 with NUB 8, the output which 487 * has a NUB 6 will look like: 488 * 00000001 00001100 489 * The first 2 bits of the output bytes are unused bits. The other bits 490 * turn out to be 000001 001100. While the 8 bits on the right are from 491 * the input, the left 4 zeroes are padded to fill the 6 bits space. 492 * 493 * @param in the input byte array 494 * @param ioffset start point inside <code>in</code> 495 * @param ilength number of bytes to repack 496 * @param iw NUB for input 497 * @param ow NUB for output 498 * @return the repacked bytes 499 */ pack(byte[] in, int ioffset, int ilength, int iw, int ow)500 private static byte[] pack(byte[] in, int ioffset, int ilength, int iw, int ow) { 501 assert (iw > 0 && iw <= 8): "input NUB must be between 1 and 8"; 502 assert (ow > 0 && ow <= 8): "output NUB must be between 1 and 8"; 503 504 if (iw == ow) { 505 return in.clone(); 506 } 507 508 int bits = ilength * iw; // number of all used bits 509 byte[] out = new byte[(bits+ow-1)/ow]; 510 511 // starting from the 0th bit in the input 512 int ipos = 0; 513 514 // the number of padding 0's needed in the output, skip them 515 int opos = (bits+ow-1)/ow*ow-bits; 516 517 while(ipos < bits) { 518 int count = iw - ipos%iw; // unpacked bits in current input byte 519 if (count > ow - opos%ow) { // free space available in output byte 520 count = ow - opos%ow; // choose the smaller number 521 } 522 // and move them! 523 out[opos/ow] |= // paste! 524 (((in[ioffset+ipos/iw]+256) // locate the byte (+256 so that it's never negative) 525 >> (iw-ipos%iw-count)) // move to the end of a byte 526 & ((1 << (count))-1)) // zero out all other bits 527 << (ow-opos%ow-count); // move to the output position 528 ipos += count; // advance 529 opos += count; // advance 530 } 531 return out; 532 } 533 534 /** 535 * Repack from NUB 8 to a NUB 7 OID sub-identifier, remove all 536 * unnecessary 0 headings, set the first bit of all non-tail 537 * output bytes to 1 (as ITU-T Rec. X.690 8.19.2 says), and 538 * paste it into an existing byte array. 539 * @param out the existing array to be pasted into 540 * @param ooffset the starting position to paste 541 * @return the number of bytes pasted 542 */ pack7Oid(byte[] in, int ioffset, int ilength, byte[] out, int ooffset)543 private static int pack7Oid(byte[] in, int ioffset, int ilength, byte[] out, int ooffset) { 544 byte[] pack = pack(in, ioffset, ilength, 8, 7); 545 int firstNonZero = pack.length-1; // paste at least one byte 546 for (int i=pack.length-2; i>=0; i--) { 547 if (pack[i] != 0) { 548 firstNonZero = i; 549 } 550 pack[i] |= 0x80; 551 } 552 System.arraycopy(pack, firstNonZero, out, ooffset, pack.length-firstNonZero); 553 return pack.length-firstNonZero; 554 } 555 556 /** 557 * Repack from NUB 7 to NUB 8, remove all unnecessary 0 558 * headings, and paste it into an existing byte array. 559 * @param out the existing array to be pasted into 560 * @param ooffset the starting position to paste 561 * @return the number of bytes pasted 562 */ pack8(byte[] in, int ioffset, int ilength, byte[] out, int ooffset)563 private static int pack8(byte[] in, int ioffset, int ilength, byte[] out, int ooffset) { 564 byte[] pack = pack(in, ioffset, ilength, 7, 8); 565 int firstNonZero = pack.length-1; // paste at least one byte 566 for (int i=pack.length-2; i>=0; i--) { 567 if (pack[i] != 0) { 568 firstNonZero = i; 569 } 570 } 571 System.arraycopy(pack, firstNonZero, out, ooffset, pack.length-firstNonZero); 572 return pack.length-firstNonZero; 573 } 574 575 /** 576 * Pack the int into a OID sub-identifier DER encoding 577 */ pack7Oid(int input, byte[] out, int ooffset)578 private static int pack7Oid(int input, byte[] out, int ooffset) { 579 byte[] b = new byte[4]; 580 b[0] = (byte)(input >> 24); 581 b[1] = (byte)(input >> 16); 582 b[2] = (byte)(input >> 8); 583 b[3] = (byte)(input); 584 return pack7Oid(b, 0, 4, out, ooffset); 585 } 586 587 /** 588 * Pack the BigInteger into a OID subidentifier DER encoding 589 */ pack7Oid(BigInteger input, byte[] out, int ooffset)590 private static int pack7Oid(BigInteger input, byte[] out, int ooffset) { 591 byte[] b = input.toByteArray(); 592 return pack7Oid(b, 0, b.length, out, ooffset); 593 } 594 595 /** 596 * Private methods to check validity of OID. They must be -- 597 * 1. at least 2 components 598 * 2. all components must be non-negative 599 * 3. the first must be 0, 1 or 2 600 * 4. if the first is 0 or 1, the second must be <40 601 */ 602 603 /** 604 * Check the DER encoding. Since DER encoding defines that the integer bits 605 * are unsigned, so there's no need to check the MSB. 606 */ check(byte[] encoding)607 private static void check(byte[] encoding) throws IOException { 608 int length = encoding.length; 609 if (length < 1 || // too short 610 (encoding[length - 1] & 0x80) != 0) { // not ended 611 throw new IOException("ObjectIdentifier() -- " + 612 "Invalid DER encoding, not ended"); 613 } 614 for (int i=0; i<length; i++) { 615 // 0x80 at the beginning of a subidentifier 616 if (encoding[i] == (byte)0x80 && 617 (i==0 || (encoding[i-1] & 0x80) == 0)) { 618 throw new IOException("ObjectIdentifier() -- " + 619 "Invalid DER encoding, useless extra octet detected"); 620 } 621 } 622 } checkCount(int count)623 private static void checkCount(int count) throws IOException { 624 if (count < 2) { 625 throw new IOException("ObjectIdentifier() -- " + 626 "Must be at least two oid components "); 627 } 628 } checkFirstComponent(int first)629 private static void checkFirstComponent(int first) throws IOException { 630 if (first < 0 || first > 2) { 631 throw new IOException("ObjectIdentifier() -- " + 632 "First oid component is invalid "); 633 } 634 } checkFirstComponent(BigInteger first)635 private static void checkFirstComponent(BigInteger first) throws IOException { 636 if (first.signum() == -1 || 637 first.compareTo(BigInteger.valueOf(2)) == 1) { 638 throw new IOException("ObjectIdentifier() -- " + 639 "First oid component is invalid "); 640 } 641 } checkSecondComponent(int first, int second)642 private static void checkSecondComponent(int first, int second) throws IOException { 643 if (second < 0 || first != 2 && second > 39) { 644 throw new IOException("ObjectIdentifier() -- " + 645 "Second oid component is invalid "); 646 } 647 } checkSecondComponent(int first, BigInteger second)648 private static void checkSecondComponent(int first, BigInteger second) throws IOException { 649 if (second.signum() == -1 || 650 first != 2 && 651 second.compareTo(BigInteger.valueOf(39)) == 1) { 652 throw new IOException("ObjectIdentifier() -- " + 653 "Second oid component is invalid "); 654 } 655 } checkOtherComponent(int i, int num)656 private static void checkOtherComponent(int i, int num) throws IOException { 657 if (num < 0) { 658 throw new IOException("ObjectIdentifier() -- " + 659 "oid component #" + (i+1) + " must be non-negative "); 660 } 661 } checkOtherComponent(int i, BigInteger num)662 private static void checkOtherComponent(int i, BigInteger num) throws IOException { 663 if (num.signum() == -1) { 664 throw new IOException("ObjectIdentifier() -- " + 665 "oid component #" + (i+1) + " must be non-negative "); 666 } 667 } 668 } 669