1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /** 19 * @author Vladimir N. Molotkov, Stepan M. Mishura 20 * @version $Revision$ 21 */ 22 23 package org.apache.harmony.security.asn1; 24 25 import java.io.IOException; 26 import java.io.InputStream; 27 import java.util.ArrayList; 28 29 30 /** 31 * Decodes ASN.1 types encoded with BER (X.690) 32 * 33 * @see <a href="http://asn1.elibel.tm.fr/en/standards/index.htm">ASN.1</a> 34 */ 35 36 public class BerInputStream { 37 38 private final InputStream in; 39 protected byte[] buffer; 40 41 /** 42 * The position in the buffer. 43 * Next read must place data into the buffer from this offset 44 */ 45 protected int offset = 0; 46 47 /** 48 * The buffer increment size. 49 * Must be reasonable big to reallocate memory not to often. 50 * Primary is used for decoding indefinite length encoding 51 */ 52 private static final int BUF_INCREASE_SIZE = 1024 * 16; 53 54 /** Indicates indefinite length of the current type */ 55 protected static final int INDEFINIT_LENGTH = -1; 56 57 /** Current decoded tag */ 58 public int tag; 59 60 /** Current decoded length */ 61 protected int length; 62 63 /** Current decoded content */ 64 public Object content; 65 66 /** Current decoded tag offset */ 67 protected int tagOffset; 68 69 /** Current decoded content offset */ 70 protected int contentOffset; 71 72 /** 73 * Creates stream for decoding. 74 */ 75 public BerInputStream(byte[] encoded) throws IOException { 76 this(encoded, 0, encoded.length); 77 } 78 79 /** 80 * Creates stream for decoding. 81 * 82 * @param encoded bytes array to be decoded 83 * @param offset the encoding offset 84 * @param expectedLength expected length of full encoding, this includes 85 * identifier, length an content octets 86 */ 87 public BerInputStream(byte[] encoded, int offset, int expectedLength) throws IOException { 88 this.in = null; 89 this.buffer = encoded; 90 this.offset = offset; 91 92 next(); 93 94 // compare expected and decoded length 95 if (length != INDEFINIT_LENGTH 96 && (offset + expectedLength) != (this.offset + this.length)) { 97 throw new ASN1Exception("Wrong content length"); 98 } 99 } 100 101 /** 102 * Creates stream for decoding. 103 * 104 * Allocates initial buffer of default size 105 */ 106 public BerInputStream(InputStream in) throws IOException { 107 this(in, BUF_INCREASE_SIZE); 108 } 109 110 /** 111 * Creates stream for decoding. 112 * 113 * @param initialSize the internal buffer initial size 114 */ 115 public BerInputStream(InputStream in, int initialSize) throws IOException { 116 this.in = in; 117 buffer = new byte[initialSize]; 118 119 next(); 120 121 if (length != INDEFINIT_LENGTH) { 122 // input stream has definite length encoding 123 // check allocated length to avoid further reallocations 124 if (buffer.length < (length + offset)) { 125 byte[] newBuffer = new byte[length + offset]; 126 System.arraycopy(buffer, 0, newBuffer, 0, offset); 127 buffer = newBuffer; 128 } 129 } else { 130 isIndefinedLength = true; 131 throw new ASN1Exception("Decoding indefinite length encoding is not supported"); 132 } 133 } 134 135 /** 136 * Resets this stream to initial state. 137 * 138 * @param encoded a new bytes array to be decoded 139 * @throws IOException if an error occurs 140 */ 141 public final void reset(byte[] encoded) throws IOException { 142 buffer = encoded; 143 next(); 144 } 145 146 /** 147 * Decodes next encoded type. 148 * Initializes tag, length, tagOffset and contentOffset variables 149 * 150 * @return next decoded tag 151 * @throws IOException if error occured 152 */ 153 public int next() throws IOException { 154 tagOffset = offset; 155 156 // read tag 157 tag = read(); 158 159 // read length 160 length = read(); 161 if (length != 0x80) { // definite form 162 // long or short length form 163 if ((length & 0x80) != 0) { // long form 164 int numOctets = length & 0x7F; 165 166 if (numOctets > 5) { 167 throw new ASN1Exception("Too long encoding at [" + tagOffset + "]"); //FIXME message 168 } 169 170 // collect this value length 171 length = read(); 172 for (int i = 1; i < numOctets; i++) { 173 int ch = read(); 174 length = (length << 8) + ch;//read(); 175 } 176 177 if (length > 0xFFFFFF) { 178 throw new ASN1Exception("Too long encoding at [" + tagOffset + "]"); //FIXME message 179 } 180 } 181 } else { //indefinite form 182 length = INDEFINIT_LENGTH; 183 } 184 contentOffset = offset; 185 186 return tag; 187 } 188 189 /** 190 * Returns the length of the encoding 191 */ 192 public static int getLength(byte[] encoding) { 193 int length = encoding[1] & 0xFF; 194 int numOctets = 0; 195 if ((length & 0x80) != 0) { // long form 196 numOctets = length & 0x7F; 197 198 // collect this value length 199 length = encoding[2] & 0xFF; 200 for (int i = 3; i < numOctets + 2; i++) { 201 length = (length << 8) + (encoding[i] & 0xFF); 202 } 203 } 204 // tag length long_form content 205 return 1 + 1 + numOctets + length; 206 } 207 208 /** 209 * Decodes ASN.1 bitstring type 210 */ 211 public void readBitString() throws IOException { 212 if (tag == ASN1Constants.TAG_BITSTRING) { 213 214 if (length == 0) { 215 throw new ASN1Exception("ASN.1 Bitstring: wrong length. Tag at [" + tagOffset + "]"); 216 } 217 218 readContent(); 219 220 // content: check unused bits 221 if (buffer[contentOffset] > 7) { 222 throw new ASN1Exception("ASN.1 Bitstring: wrong content at [" + contentOffset 223 + "]. A number of unused bits MUST be in range 0 to 7"); 224 } 225 226 if (length == 1 && buffer[contentOffset] != 0) { 227 throw new ASN1Exception("ASN.1 Bitstring: wrong content at [" + contentOffset 228 + "]. For empty string unused bits MUST be 0"); 229 } 230 231 } else if (tag == ASN1Constants.TAG_C_BITSTRING) { 232 throw new ASN1Exception("Decoding constructed ASN.1 bitstring type is not provided"); 233 } else { 234 throw expected("bitstring"); 235 } 236 } 237 238 /** 239 * Decodes ASN.1 Enumerated type 240 */ 241 public void readEnumerated() throws IOException { 242 if (tag != ASN1Constants.TAG_ENUM) { 243 throw expected("enumerated"); 244 } 245 246 // check encoded length 247 if (length == 0) { 248 throw new ASN1Exception("ASN.1 enumerated: wrong length for identifier at [" 249 + tagOffset + "]"); 250 } 251 252 readContent(); 253 254 // check encoded content 255 if (length > 1) { 256 int bits = buffer[contentOffset] & 0xFF; 257 if (buffer[contentOffset + 1] < 0) { 258 bits += 0x100; 259 } 260 261 if (bits == 0 || bits == 0x1FF) { 262 throw new ASN1Exception("ASN.1 enumerated: wrong content at [" + contentOffset 263 + "]. An integer MUST be encoded in minimum number of octets"); 264 } 265 } 266 } 267 268 /** 269 * Decodes ASN.1 boolean type 270 */ 271 public void readBoolean() throws IOException { 272 if (tag != ASN1Constants.TAG_BOOLEAN) { 273 throw expected("boolean"); 274 } 275 276 // check encoded length 277 if (length != 1) { 278 throw new ASN1Exception("Wrong length for ASN.1 boolean at [" + tagOffset + "]"); 279 } 280 281 readContent(); 282 } 283 284 /** The last choice index */ 285 public int choiceIndex; 286 287 /** Keeps last decoded: year, month, day, hour, minute, second, millisecond */ 288 public int[] times; 289 290 /** 291 * Decodes ASN.1 GeneralizedTime type 292 * 293 * @throws IOException if error occured 294 */ 295 public void readGeneralizedTime() throws IOException { 296 if (tag == ASN1Constants.TAG_GENERALIZEDTIME) { 297 // FIXME: any other optimizations? 298 readContent(); 299 // FIXME store string somewhere to allow a custom time type perform 300 // additional checks 301 302 // check syntax: the last char MUST be Z 303 if (buffer[offset - 1] != 'Z') { 304 // FIXME support only format that is acceptable for DER 305 throw new ASN1Exception("ASN.1 GeneralizedTime: encoded format is not implemented"); 306 } 307 308 // check syntax: MUST be YYYYMMDDHHMMSS[(./,)DDD]'Z' 309 if (length != 15 && (length < 17 || length > 19)) { 310 throw new ASN1Exception("ASN.1 GeneralizedTime wrongly encoded at [" 311 + contentOffset + "]"); 312 } 313 314 // check content: milliseconds 315 if (length > 16) { 316 byte char14 = buffer[contentOffset + 14]; 317 if (char14 != '.' && char14 != ',') { 318 throw new ASN1Exception("ASN.1 GeneralizedTime wrongly encoded at [" 319 + contentOffset + "]"); 320 } 321 } 322 323 if (times == null) { 324 times = new int[7]; 325 } 326 times[0] = strToInt(contentOffset, 4); // year 327 times[1] = strToInt(contentOffset + 4, 2); // month 328 times[2] = strToInt(contentOffset + 6, 2); // day 329 times[3] = strToInt(contentOffset + 8, 2); // hour 330 times[4] = strToInt(contentOffset + 10, 2); // minute 331 times[5] = strToInt(contentOffset + 12, 2); // second 332 333 if (length > 16) { 334 // FIXME optimize me 335 times[6] = strToInt(contentOffset + 15, length - 16); 336 337 if (length == 17) { 338 times[6] = times[6] * 100; 339 } else if (length == 18) { 340 times[6] = times[6] * 10; 341 } 342 } 343 344 // FIXME check all values for valid numbers!!! 345 } else if (tag == ASN1Constants.TAG_C_GENERALIZEDTIME) { 346 throw new ASN1Exception("Decoding constructed ASN.1 GeneralizedTime type is not supported"); 347 } else { 348 throw expected("GeneralizedTime"); 349 } 350 } 351 352 /** 353 * Decodes ASN.1 UTCTime type 354 * 355 * @throws IOException if an I/O error occurs or the end of the stream is reached 356 */ 357 public void readUTCTime() throws IOException { 358 if (tag == ASN1Constants.TAG_UTCTIME) { 359 switch (length) { 360 case ASN1UTCTime.UTC_HM: 361 case ASN1UTCTime.UTC_HMS: 362 break; 363 case ASN1UTCTime.UTC_LOCAL_HM: 364 case ASN1UTCTime.UTC_LOCAL_HMS: 365 // FIXME only coordinated universal time formats are supported 366 throw new ASN1Exception("ASN.1 UTCTime: local time format is not supported"); 367 default: 368 throw new ASN1Exception("ASN.1 UTCTime: wrong length, identifier at " + tagOffset); 369 } 370 371 // FIXME: any other optimizations? 372 readContent(); 373 374 // FIXME store string somewhere to allow a custom time type perform 375 // additional checks 376 377 // check syntax: the last char MUST be Z 378 if (buffer[offset - 1] != 'Z') { 379 throw new ASN1Exception("ASN.1 UTCTime wrongly encoded at [" 380 + contentOffset + ']'); 381 } 382 383 if (times == null) { 384 times = new int[7]; 385 } 386 387 times[0] = strToInt(contentOffset, 2); // year 388 if (times[0] > 49) { 389 times[0] += 1900; 390 } else { 391 times[0] += 2000; 392 } 393 394 times[1] = strToInt(contentOffset + 2, 2); // month 395 times[2] = strToInt(contentOffset + 4, 2); // day 396 times[3] = strToInt(contentOffset + 6, 2); // hour 397 times[4] = strToInt(contentOffset + 8, 2); // minute 398 399 if (length == ASN1UTCTime.UTC_HMS) { 400 times[5] = strToInt(contentOffset + 10, 2); // second 401 } 402 403 // FIXME check all time values for valid numbers!!! 404 } else if (tag == ASN1Constants.TAG_C_UTCTIME) { 405 throw new ASN1Exception("Decoding constructed ASN.1 UTCTime type is not supported"); 406 } else { 407 throw expected("UTCTime"); 408 } 409 } 410 411 private int strToInt(int off, int count) throws ASN1Exception { 412 int result = 0; 413 for (int i = off, end = off + count; i < end; i++) { 414 int c = buffer[i] - 48; 415 if (c < 0 || c > 9) { 416 throw new ASN1Exception("Time encoding has invalid char"); 417 } 418 result = result * 10 + c; 419 } 420 return result; 421 } 422 423 /** 424 * Decodes ASN.1 Integer type 425 */ 426 public void readInteger() throws IOException { 427 if (tag != ASN1Constants.TAG_INTEGER) { 428 throw expected("integer"); 429 } 430 431 // check encoded length 432 if (length < 1) { 433 throw new ASN1Exception("Wrong length for ASN.1 integer at [" + tagOffset + "]"); 434 } 435 436 readContent(); 437 438 // check encoded content 439 if (length > 1) { 440 byte firstByte = buffer[offset - length]; 441 byte secondByte = (byte) (buffer[offset - length + 1] & 0x80); 442 443 if (firstByte == 0 && secondByte == 0 || firstByte == (byte) 0xFF 444 && secondByte == (byte) 0x80) { 445 throw new ASN1Exception("Wrong content for ASN.1 integer at [" + (offset - length) + "]. An integer MUST be encoded in minimum number of octets"); 446 } 447 } 448 } 449 450 /** 451 * Decodes ASN.1 Octetstring type 452 */ 453 public void readOctetString() throws IOException { 454 if (tag == ASN1Constants.TAG_OCTETSTRING) { 455 readContent(); 456 } else if (tag == ASN1Constants.TAG_C_OCTETSTRING) { 457 throw new ASN1Exception("Decoding constructed ASN.1 octet string type is not supported"); 458 } else { 459 throw expected("octetstring"); 460 } 461 } 462 463 private ASN1Exception expected(String what) throws ASN1Exception { 464 throw new ASN1Exception("ASN.1 " + what + " identifier expected at [" + tagOffset + "], got " + Integer.toHexString(tag)); 465 } 466 467 public int oidElement; 468 469 /** 470 * Decodes ASN.1 ObjectIdentifier type 471 */ 472 public void readOID() throws IOException { 473 if (tag != ASN1Constants.TAG_OID) { 474 throw expected("OID"); 475 } 476 477 // check encoded length 478 if (length < 1) { 479 throw new ASN1Exception("Wrong length for ASN.1 object identifier at [" + tagOffset + "]"); 480 } 481 482 readContent(); 483 484 // check content: last encoded byte (8th bit MUST be zero) 485 if ((buffer[offset - 1] & 0x80) != 0) { 486 throw new ASN1Exception("Wrong encoding at [" + (offset - 1) + "]"); 487 } 488 489 oidElement = 1; 490 for (int i = 0; i < length; i++, ++oidElement) { 491 while ((buffer[contentOffset + i] & 0x80) == 0x80) { 492 i++; 493 } 494 } 495 } 496 497 /** 498 * Decodes ASN.1 Sequence type 499 */ 500 public void readSequence(ASN1Sequence sequence) throws IOException { 501 if (tag != ASN1Constants.TAG_C_SEQUENCE) { 502 throw expected("sequence"); 503 } 504 505 int begOffset = offset; 506 int endOffset = begOffset + length; 507 508 ASN1Type[] type = sequence.type; 509 510 int i = 0; 511 512 if (isVerify) { 513 514 for (; (offset < endOffset) && (i < type.length); i++) { 515 516 next(); 517 while (!type[i].checkTag(tag)) { 518 // check whether it is optional component or not 519 if (!sequence.OPTIONAL[i] || (i == type.length - 1)) { 520 throw new ASN1Exception("ASN.1 Sequence: mandatory value is missing at [" + tagOffset + "]"); 521 } 522 i++; 523 } 524 525 type[i].decode(this); 526 } 527 528 // check the rest of components 529 for (; i < type.length; i++) { 530 if (!sequence.OPTIONAL[i]) { 531 throw new ASN1Exception("ASN.1 Sequence: mandatory value is missing at [" + tagOffset + "]"); 532 } 533 } 534 535 } else { 536 int seqTagOffset = tagOffset; //store tag offset 537 538 Object[] values = new Object[type.length]; 539 for (; (offset < endOffset) && (i < type.length); i++) { 540 541 next(); 542 while (!type[i].checkTag(tag)) { 543 // check whether it is optional component or not 544 if (!sequence.OPTIONAL[i] || (i == type.length - 1)) { 545 throw new ASN1Exception("ASN.1 Sequence: mandatory value is missing at [" + tagOffset + "]"); 546 } 547 548 // sets default value 549 if (sequence.DEFAULT[i] != null) { 550 values[i] = sequence.DEFAULT[i]; 551 } 552 i++; 553 } 554 values[i] = type[i].decode(this); 555 } 556 557 // check the rest of components 558 for (; i < type.length; i++) { 559 if (!sequence.OPTIONAL[i]) { 560 throw new ASN1Exception("ASN.1 Sequence: mandatory value is missing at [" + tagOffset + "]"); 561 } 562 if (sequence.DEFAULT[i] != null) { 563 values[i] = sequence.DEFAULT[i]; 564 } 565 } 566 content = values; 567 568 tagOffset = seqTagOffset; //retrieve tag offset 569 } 570 571 if (offset != endOffset) { 572 throw new ASN1Exception("Wrong encoding at [" + begOffset + "]. Content's length and encoded length are not the same"); 573 } 574 } 575 576 /** 577 * Decodes ASN.1 SequenceOf type 578 */ 579 public void readSequenceOf(ASN1SequenceOf sequenceOf) throws IOException { 580 if (tag != ASN1Constants.TAG_C_SEQUENCEOF) { 581 throw expected("sequenceOf"); 582 } 583 584 decodeValueCollection(sequenceOf); 585 } 586 587 /** 588 * Decodes ASN.1 Set type 589 */ 590 public void readSet(ASN1Set set) throws IOException { 591 if (tag != ASN1Constants.TAG_C_SET) { 592 throw expected("set"); 593 } 594 595 throw new ASN1Exception("Decoding ASN.1 Set type is not supported"); 596 } 597 598 /** 599 * Decodes ASN.1 SetOf type 600 */ 601 public void readSetOf(ASN1SetOf setOf) throws IOException { 602 if (tag != ASN1Constants.TAG_C_SETOF) { 603 throw expected("setOf"); 604 } 605 606 decodeValueCollection(setOf); 607 } 608 609 private void decodeValueCollection(ASN1ValueCollection collection) throws IOException { 610 int begOffset = offset; 611 int endOffset = begOffset + length; 612 613 ASN1Type type = collection.type; 614 615 if (isVerify) { 616 while (endOffset > offset) { 617 next(); 618 type.decode(this); 619 } 620 } else { 621 int seqTagOffset = tagOffset; //store tag offset 622 623 ArrayList<Object> values = new ArrayList<Object>(); 624 while (endOffset > offset) { 625 next(); 626 values.add(type.decode(this)); 627 } 628 629 values.trimToSize(); 630 content = values; 631 632 tagOffset = seqTagOffset; //retrieve tag offset 633 } 634 635 if (offset != endOffset) { 636 throw new ASN1Exception("Wrong encoding at [" + begOffset + "]. Content's length and encoded length are not the same"); 637 } 638 } 639 640 /** 641 * Decodes ASN.1 String type 642 * 643 * @throws IOException if an I/O error occurs or the end of the stream is reached 644 */ 645 public void readString(ASN1StringType type) throws IOException { 646 if (tag == type.id) { 647 readContent(); 648 } else if (tag == type.constrId) { 649 throw new ASN1Exception("Decoding constructed ASN.1 string type is not provided"); 650 } else { 651 throw expected("string"); 652 } 653 } 654 655 /** 656 * Returns encoded array. 657 * 658 * MUST be invoked after decoding corresponding ASN.1 notation 659 */ 660 public byte[] getEncoded() { 661 byte[] encoded = new byte[offset - tagOffset]; 662 System.arraycopy(buffer, tagOffset, encoded, 0, encoded.length); 663 return encoded; 664 } 665 666 /** 667 * Returns internal buffer used for decoding 668 */ 669 public final byte[] getBuffer() { 670 return buffer; 671 } 672 673 /** 674 * Returns length of the current content for decoding 675 */ 676 public final int getLength() { 677 return length; 678 } 679 680 /** 681 * Returns the current offset 682 */ 683 public final int getOffset() { 684 return offset; 685 } 686 687 /** 688 * Returns end offset for the current encoded type 689 */ 690 public final int getEndOffset() { 691 return offset + length; 692 } 693 694 /** 695 * Returns start offset for the current encoded type 696 */ 697 public final int getTagOffset() { 698 return tagOffset; 699 } 700 701 /** 702 * Indicates verify or store mode. 703 * 704 * In store mode a decoded content is stored in a newly allocated 705 * appropriate object. The <code>content</code> variable holds 706 * a reference to the last created object. 707 * 708 * In verify mode a decoded content is not stored. 709 */ 710 // FIXME it is used only for one case 711 // decoding PCKS#8 Private Key Info notation 712 // remove this option because it does decoding more complex 713 protected boolean isVerify; 714 715 /** 716 * Sets verify mode. 717 */ 718 public final void setVerify() { 719 isVerify = true; 720 } 721 722 /** 723 * Indicates defined or indefined reading mode for associated InputStream. 724 * 725 * This mode is defined by reading a length 726 * for a first ASN.1 type from InputStream. 727 */ 728 protected boolean isIndefinedLength; 729 730 /** 731 * Reads the next encoded byte from the encoded input stream. 732 */ 733 protected int read() throws IOException { 734 if (offset == buffer.length) { 735 throw new ASN1Exception("Unexpected end of encoding"); 736 } 737 738 if (in == null) { 739 return buffer[offset++] & 0xFF; 740 } else { 741 int octet = in.read(); 742 if (octet == -1) { 743 throw new ASN1Exception("Unexpected end of encoding"); 744 } 745 746 buffer[offset++] = (byte) octet; 747 748 return octet; 749 } 750 } 751 752 /** 753 * Reads the next encoded content from the encoded input stream. 754 * The method MUST be used for reading a primitive encoded content. 755 */ 756 public void readContent() throws IOException { 757 if (offset + length > buffer.length) { 758 throw new ASN1Exception("Unexpected end of encoding"); 759 } 760 761 if (in == null) { 762 offset += length; 763 } else { 764 int bytesRead = in.read(buffer, offset, length); 765 766 if (bytesRead != length) { 767 // if input stream didn't return all data at once 768 // try to read it in several blocks 769 int c = bytesRead; 770 do { 771 if (c < 1 || bytesRead > length) { 772 throw new ASN1Exception("Failed to read encoded content"); 773 } 774 c = in.read(buffer, offset + bytesRead, length - bytesRead); 775 bytesRead += c; 776 } while (bytesRead != length); 777 } 778 779 offset += length; 780 } 781 } 782 783 /** 784 * Reallocates the buffer in order to make it 785 * exactly the size of data it contains 786 */ 787 public void compactBuffer() { 788 if (offset != buffer.length) { 789 byte[] newBuffer = new byte[offset]; 790 // restore buffer content 791 System.arraycopy(buffer, 0, newBuffer, 0, offset); 792 // set new buffer 793 buffer = newBuffer; 794 } 795 } 796 private Object[][] pool; 797 798 public void put(Object key, Object entry) { 799 if (pool == null) { 800 pool = new Object[2][10]; 801 } 802 803 int i = 0; 804 for (; i < pool[0].length && pool[0][i] != null; i++) { 805 if (pool[0][i] == key) { 806 pool[1][i] = entry; 807 return; 808 } 809 } 810 811 if (i == pool[0].length) { 812 Object[][] newPool = new Object[pool[0].length * 2][2]; 813 System.arraycopy(pool[0], 0, newPool[0], 0, pool[0].length); 814 System.arraycopy(pool[1], 0, newPool[1], 0, pool[0].length); 815 pool = newPool; 816 } else { 817 pool[0][i] = key; 818 pool[1][i] = entry; 819 } 820 } 821 822 public Object get(Object key) { 823 if (pool == null) { 824 return null; 825 } 826 827 for (int i = 0; i < pool[0].length; i++) { 828 if (pool[0][i] == key) { 829 return pool[1][i]; 830 } 831 } 832 return null; 833 } 834 } 835