001/* 002 * Copyright 2009-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2009-2020 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2009-2020 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.asn1; 037 038 039 040import java.io.BufferedInputStream; 041import java.io.ByteArrayInputStream; 042import java.io.Closeable; 043import java.io.InputStream; 044import java.io.IOException; 045import java.math.BigInteger; 046import java.net.SocketTimeoutException; 047import java.util.Date; 048import java.util.logging.Level; 049import javax.security.sasl.SaslClient; 050 051import com.unboundid.util.Debug; 052import com.unboundid.util.Mutable; 053import com.unboundid.util.NotNull; 054import com.unboundid.util.Nullable; 055import com.unboundid.util.StaticUtils; 056import com.unboundid.util.ThreadSafety; 057import com.unboundid.util.ThreadSafetyLevel; 058 059import static com.unboundid.asn1.ASN1Messages.*; 060 061 062 063/** 064 * This class provides a mechanism for ASN.1 elements (including sequences and 065 * sets) from an input stream in a manner that allows the data to be decoded on 066 * the fly without constructing {@link ASN1Element} objects if they are not 067 * needed. If any method in this class throws an {@code IOException}, then the 068 * caller must close this reader and must not attempt to use it any more. 069 * {@code ASN1StreamReader} instances are not threadsafe and must not be 070 * accessed concurrently by multiple threads. 071 */ 072@Mutable() 073@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 074public final class ASN1StreamReader 075 implements Closeable 076{ 077 // Indicates whether socket timeout exceptions should be ignored for the 078 // initial read of an element. 079 private boolean ignoreInitialSocketTimeout; 080 081 // Indicates whether socket timeout exceptions should be ignored for 082 // subsequent reads of an element. 083 private boolean ignoreSubsequentSocketTimeout; 084 085 // The input stream that will be used for reading data after it has been 086 // unwrapped by SASL processing. 087 @Nullable private volatile ByteArrayInputStream saslInputStream; 088 089 // The input stream from which data will be read. 090 @NotNull private final InputStream inputStream; 091 092 // The maximum element size that will be allowed. 093 private final int maxElementSize; 094 095 // The total number of bytes read from the underlying input stream. 096 private long totalBytesRead; 097 098 // The SASL client that will be used to unwrap any data read over this 099 // stream reader. 100 @Nullable private volatile SaslClient saslClient; 101 102 103 104 /** 105 * Creates a new ASN.1 stream reader that will read data from the provided 106 * input stream. It will use a maximum element size of 107 * {@code Integer.MAX_VALUE}. 108 * 109 * @param inputStream The input stream from which data should be read. If 110 * the provided input stream does not support the use of 111 * the {@code mark} and {@code reset} methods, then it 112 * will be wrapped with a {@code BufferedInputStream}. 113 */ 114 public ASN1StreamReader(@NotNull final InputStream inputStream) 115 { 116 this(inputStream, Integer.MAX_VALUE); 117 } 118 119 120 121 /** 122 * Creates a new ASN.1 stream reader that will read data from the provided 123 * input stream. It will use a maximum element size of 124 * {@code Integer.MAX_VALUE}. 125 * 126 * @param inputStream The input stream from which data should be read. 127 * If the provided input stream does not support the 128 * use of the {@code mark} and {@code reset} methods, 129 * then it will be wrapped with a 130 * {@code BufferedInputStream}. 131 * @param maxElementSize The maximum size in bytes of an ASN.1 element that 132 * may be read. A value less than or equal to zero 133 * will be interpreted as {@code Integer.MAX_VALUE}. 134 */ 135 public ASN1StreamReader(@NotNull final InputStream inputStream, 136 final int maxElementSize) 137 { 138 if (inputStream.markSupported()) 139 { 140 this.inputStream = inputStream; 141 } 142 else 143 { 144 this.inputStream = new BufferedInputStream(inputStream); 145 } 146 147 if (maxElementSize > 0) 148 { 149 this.maxElementSize = maxElementSize; 150 } 151 else 152 { 153 this.maxElementSize = Integer.MAX_VALUE; 154 } 155 156 totalBytesRead = 0L; 157 ignoreInitialSocketTimeout = false; 158 ignoreSubsequentSocketTimeout = false; 159 saslClient = null; 160 saslInputStream = null; 161 } 162 163 164 165 /** 166 * Closes this ASN.1 stream reader and the underlying input stream. This 167 * reader must not be used after it has been closed. 168 * 169 * @throws IOException If a problem occurs while closing the underlying 170 * input stream. 171 */ 172 @Override() 173 public void close() 174 throws IOException 175 { 176 inputStream.close(); 177 } 178 179 180 181 /** 182 * Retrieves the total number of bytes read so far from the underlying input 183 * stream. 184 * 185 * @return The total number of bytes read so far from the underlying input 186 * stream. 187 */ 188 long getTotalBytesRead() 189 { 190 return totalBytesRead; 191 } 192 193 194 195 /** 196 * Indicates whether to ignore {@code java.net.SocketTimeoutException} 197 * exceptions that may be caught during processing. 198 * 199 * @return {@code true} if {@code SocketTimeoutException} exceptions should 200 * be ignored, or {@code false} if they should not be ignored and 201 * should be propagated to the caller. 202 * 203 * @deprecated Use the {@link #ignoreInitialSocketTimeoutException()} and 204 * {@link #ignoreSubsequentSocketTimeoutException()} methods 205 * instead. 206 */ 207 @Deprecated() 208 public boolean ignoreSocketTimeoutException() 209 { 210 return ignoreInitialSocketTimeout; 211 } 212 213 214 215 /** 216 * Indicates whether to ignore {@code java.net.SocketTimeoutException} 217 * exceptions that may be caught while trying to read the first byte of an 218 * element. 219 * 220 * @return {@code true} if {@code SocketTimeoutException} exceptions should 221 * be ignored while trying to read the first byte of an element, or 222 * {@code false} if they should not be ignored and should be 223 * propagated to the caller. 224 */ 225 public boolean ignoreInitialSocketTimeoutException() 226 { 227 return ignoreInitialSocketTimeout; 228 } 229 230 231 232 /** 233 * Indicates whether to ignore {@code java.net.SocketTimeoutException} 234 * exceptions that may be caught while trying to read subsequent bytes of an 235 * element (after one or more bytes have already been read for that element). 236 * 237 * @return {@code true} if {@code SocketTimeoutException} exceptions should 238 * be ignored while trying to read subsequent bytes of an element, or 239 * {@code false} if they should not be ignored and should be 240 * propagated to the caller. 241 */ 242 public boolean ignoreSubsequentSocketTimeoutException() 243 { 244 return ignoreSubsequentSocketTimeout; 245 } 246 247 248 249 /** 250 * Indicates whether to ignore {@code java.net.SocketTimeoutException} 251 * exceptions that may be caught during processing. 252 * 253 * @param ignoreSocketTimeout Indicates whether to ignore 254 * {@code SocketTimeoutException} exceptions that 255 * may be caught during processing. 256 * 257 * @deprecated Use the {@link #setIgnoreSocketTimeout(boolean,boolean)} 258 * method instead. 259 */ 260 @Deprecated() 261 public void setIgnoreSocketTimeout(final boolean ignoreSocketTimeout) 262 { 263 ignoreInitialSocketTimeout = ignoreSocketTimeout; 264 ignoreSubsequentSocketTimeout = ignoreSocketTimeout; 265 } 266 267 268 269 /** 270 * Indicates whether to ignore {@code java.net.SocketTimeoutException} 271 * exceptions that may be caught during processing. 272 * 273 * @param ignoreInitialSocketTimeout Indicates whether to ignore 274 * {@code SocketTimeoutException} 275 * exceptions that may be caught while 276 * trying to read the first byte of an 277 * element. 278 * @param ignoreSubsequentSocketTimeout Indicates whether to ignore 279 * {@code SocketTimeoutException} 280 * exceptions that may be caught while 281 * reading beyond the first byte of an 282 * element. 283 */ 284 public void setIgnoreSocketTimeout(final boolean ignoreInitialSocketTimeout, 285 final boolean ignoreSubsequentSocketTimeout) 286 { 287 this.ignoreInitialSocketTimeout = ignoreInitialSocketTimeout; 288 this.ignoreSubsequentSocketTimeout = ignoreSubsequentSocketTimeout; 289 } 290 291 292 293 /** 294 * Peeks at the next byte to be read from the input stream without actually 295 * consuming it. 296 * 297 * @return An integer value encapsulating the BER type of the next element in 298 * the input stream, or -1 if the end of the input stream has been 299 * reached and there is no data to be read. If a value of -1 is 300 * returned, then the input stream will not have been closed since 301 * this method is not intended to have any impact on the underlying 302 * input stream. 303 * 304 * @throws IOException If a problem occurs while reading from the input 305 * stream. 306 */ 307 public int peek() 308 throws IOException 309 { 310 final InputStream is; 311 if (saslClient == null) 312 { 313 is = inputStream; 314 } 315 else 316 { 317 if ((saslInputStream == null) || (saslInputStream.available() <= 0)) 318 { 319 readAndDecodeSASLData(-1); 320 } 321 322 is = saslInputStream; 323 } 324 325 is.mark(1); 326 final int byteRead = read(true); 327 is.reset(); 328 329 return byteRead; 330 } 331 332 333 334 /** 335 * Reads the BER type of the next element from the input stream. This may not 336 * be called if a previous element has been started but not yet completed. 337 * 338 * @return An integer value encapsulating the BER type of the next element in 339 * the input stream, or -1 if the end of the input stream has been 340 * reached and there is no data to be read. If a value of -1 is 341 * returned, then the input stream will have been closed. 342 * 343 * @throws IOException If a problem occurs while reading from the input 344 * stream. 345 */ 346 private int readType() 347 throws IOException 348 { 349 final int typeInt = read(true); 350 if (typeInt < 0) 351 { 352 close(); 353 } 354 else 355 { 356 totalBytesRead++; 357 } 358 return typeInt; 359 } 360 361 362 363 /** 364 * Reads the length of the next element from the input stream. This may only 365 * be called after reading the BER type. 366 * 367 * @return The length of the next element from the input stream. 368 * 369 * @throws IOException If a problem occurs while reading from the input 370 * stream, if the end of the stream has been reached, or 371 * if the decoded length is greater than the maximum 372 * allowed length. 373 */ 374 private int readLength() 375 throws IOException 376 { 377 int length = read(false); 378 if (length < 0) 379 { 380 throw new IOException(ERR_READ_END_BEFORE_FIRST_LENGTH.get()); 381 } 382 383 totalBytesRead++; 384 if (length > 127) 385 { 386 final int numLengthBytes = length & 0x7F; 387 length = 0; 388 if ((numLengthBytes < 1) || (numLengthBytes > 4)) 389 { 390 throw new IOException(ERR_READ_LENGTH_TOO_LONG.get(numLengthBytes)); 391 } 392 393 for (int i=0; i < numLengthBytes; i++) 394 { 395 final int lengthInt = read(false); 396 if (lengthInt < 0) 397 { 398 throw new IOException(ERR_READ_END_BEFORE_LENGTH_END.get()); 399 } 400 401 length <<= 8; 402 length |= (lengthInt & 0xFF); 403 } 404 405 totalBytesRead += numLengthBytes; 406 } 407 408 if ((length < 0) || ((maxElementSize > 0) && (length > maxElementSize))) 409 { 410 throw new IOException(ERR_READ_LENGTH_EXCEEDS_MAX.get(length, 411 maxElementSize)); 412 } 413 414 return length; 415 } 416 417 418 419 /** 420 * Skips over the specified number of bytes. 421 * 422 * @param numBytes The number of bytes to skip. 423 * 424 * @throws IOException If a problem occurs while reading from the input 425 * stream, or if the end of the stream is reached before 426 * having skipped the specified number of bytes. 427 */ 428 private void skip(final int numBytes) 429 throws IOException 430 { 431 if (numBytes <= 0) 432 { 433 return; 434 } 435 436 if (saslClient != null) 437 { 438 int skippedSoFar = 0; 439 final byte[] skipBuffer = new byte[numBytes]; 440 while (true) 441 { 442 final int bytesRead = read(skipBuffer, skippedSoFar, 443 (numBytes - skippedSoFar)); 444 if (bytesRead < 0) 445 { 446 // We unexpectedly hit the end of the stream. We'll just return since 447 // we clearly can't skip any more, and subsequent read attempts will 448 // fail. 449 return; 450 } 451 452 skippedSoFar += bytesRead; 453 totalBytesRead += bytesRead; 454 if (skippedSoFar >= numBytes) 455 { 456 return; 457 } 458 } 459 } 460 461 long totalBytesSkipped = inputStream.skip(numBytes); 462 while (totalBytesSkipped < numBytes) 463 { 464 final long bytesSkipped = inputStream.skip(numBytes - totalBytesSkipped); 465 if (bytesSkipped <= 0) 466 { 467 while (totalBytesSkipped < numBytes) 468 { 469 final int byteRead = read(false); 470 if (byteRead < 0) 471 { 472 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get()); 473 } 474 totalBytesSkipped++; 475 } 476 } 477 else 478 { 479 totalBytesSkipped += bytesSkipped; 480 } 481 } 482 483 totalBytesRead += numBytes; 484 } 485 486 487 488 /** 489 * Reads a complete ASN.1 element from the input stream. 490 * 491 * @return The ASN.1 element read from the input stream, or {@code null} if 492 * the end of the input stream was reached before any data could be 493 * read. If {@code null} is returned, then the input stream will 494 * have been closed. 495 * 496 * @throws IOException If a problem occurs while reading from the input 497 * stream, if the end of the input stream is reached in 498 * the middle of the element, or or if an attempt is 499 * made to read an element larger than the maximum 500 * allowed size. 501 */ 502 @Nullable() 503 public ASN1Element readElement() 504 throws IOException 505 { 506 final int type = readType(); 507 if (type < 0) 508 { 509 return null; 510 } 511 512 final int length = readLength(); 513 514 int valueBytesRead = 0; 515 int bytesRemaining = length; 516 final byte[] value = new byte[length]; 517 while (valueBytesRead < length) 518 { 519 final int bytesRead = read(value, valueBytesRead, bytesRemaining); 520 if (bytesRead < 0) 521 { 522 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get()); 523 } 524 525 valueBytesRead += bytesRead; 526 bytesRemaining -= bytesRead; 527 } 528 529 totalBytesRead += length; 530 final ASN1Element e = new ASN1Element((byte) type, value); 531 Debug.debugASN1Read(e); 532 return e; 533 } 534 535 536 537 /** 538 * Reads an ASN.1 Boolean element from the input stream and returns the value 539 * as a {@code Boolean}. 540 * 541 * @return The {@code Boolean} value of the ASN.1 Boolean element read, or 542 * {@code null} if the end of the input stream was reached before any 543 * data could be read. If {@code null} is returned, then the input 544 * stream will have been closed. 545 * 546 * @throws IOException If a problem occurs while reading from the input 547 * stream, if the end of the input stream is reached in 548 * the middle of the element, or or if an attempt is 549 * made to read an element larger than the maximum 550 * allowed size. 551 * 552 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1 553 * Boolean element. 554 */ 555 @Nullable() 556 public Boolean readBoolean() 557 throws IOException, ASN1Exception 558 { 559 final int type = readType(); 560 if (type < 0) 561 { 562 return null; 563 } 564 565 final int length = readLength(); 566 567 if (length == 1) 568 { 569 final int value = read(false); 570 if (value < 0) 571 { 572 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get()); 573 } 574 575 totalBytesRead++; 576 577 final Boolean booleanValue = (value != 0x00); 578 Debug.debugASN1Read(Level.INFO, "Boolean", type, 1, booleanValue); 579 return booleanValue; 580 } 581 else 582 { 583 skip(length); 584 throw new ASN1Exception(ERR_BOOLEAN_INVALID_LENGTH.get()); 585 } 586 } 587 588 589 590 /** 591 * Reads an ASN.1 enumerated element from the input stream and returns the 592 * value as an {@code Integer}. 593 * 594 * @return The {@code Integer} value of the ASN.1 enumerated element read, or 595 * {@code null} if the end of the input stream was reached before any 596 * data could be read. If {@code null} is returned, then the input 597 * stream will have been closed. 598 * 599 * @throws IOException If a problem occurs while reading from the input 600 * stream, if the end of the input stream is reached in 601 * the middle of the element, or or if an attempt is 602 * made to read an element larger than the maximum 603 * allowed size. 604 * 605 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1 606 * enumerated element. 607 */ 608 @Nullable() 609 public Integer readEnumerated() 610 throws IOException, ASN1Exception 611 { 612 return readInteger(); 613 } 614 615 616 617 /** 618 * Reads an ASN.1 generalized time element from the input stream and returns 619 * the value as a {@code Date}. 620 * 621 * @return The {@code Date} value of the ASN.1 generalized time element read, 622 * or {@code null} if the end of the input stream was reached before 623 * any data could be read. If {@code null} is returned, then the 624 * input stream will have been closed. 625 * 626 * @throws IOException If a problem occurs while reading from the input 627 * stream, if the end of the input stream is reached in 628 * the middle of the element, or or if an attempt is 629 * made to read an element larger than the maximum 630 * allowed size. 631 * 632 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1 633 * generalized time element. 634 */ 635 @Nullable() 636 public Date readGeneralizedTime() 637 throws IOException, ASN1Exception 638 { 639 final int type = readType(); 640 if (type < 0) 641 { 642 return null; 643 } 644 645 final int length = readLength(); 646 647 int valueBytesRead = 0; 648 int bytesRemaining = length; 649 final byte[] value = new byte[length]; 650 while (valueBytesRead < length) 651 { 652 final int bytesRead = read(value, valueBytesRead, bytesRemaining); 653 if (bytesRead < 0) 654 { 655 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get()); 656 } 657 658 valueBytesRead += bytesRead; 659 bytesRemaining -= bytesRead; 660 } 661 662 totalBytesRead += length; 663 664 final String timestamp = StaticUtils.toUTF8String(value); 665 final Date date = 666 new Date(ASN1GeneralizedTime.decodeTimestamp(timestamp)); 667 Debug.debugASN1Read(Level.INFO, "GeneralizedTime", type, length, timestamp); 668 return date; 669 } 670 671 672 673 /** 674 * Reads an ASN.1 integer element from the input stream and returns the value 675 * as an {@code Integer}. 676 * 677 * @return The {@code Integer} value of the ASN.1 integer element read, or 678 * {@code null} if the end of the input stream was reached before any 679 * data could be read. If {@code null} is returned, then the input 680 * stream will have been closed. 681 * 682 * @throws IOException If a problem occurs while reading from the input 683 * stream, if the end of the input stream is reached in 684 * the middle of the element, or or if an attempt is 685 * made to read an element larger than the maximum 686 * allowed size. 687 * 688 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1 689 * integer element. 690 */ 691 @Nullable() 692 public Integer readInteger() 693 throws IOException, ASN1Exception 694 { 695 final int type = readType(); 696 if (type < 0) 697 { 698 return null; 699 } 700 701 final int length = readLength(); 702 if ((length == 0) || (length > 4)) 703 { 704 skip(length); 705 throw new ASN1Exception(ERR_INTEGER_INVALID_LENGTH.get(length)); 706 } 707 708 boolean negative = false; 709 int intValue = 0; 710 for (int i=0; i < length; i++) 711 { 712 final int byteRead = read(false); 713 if (byteRead < 0) 714 { 715 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get()); 716 } 717 718 if (i == 0) 719 { 720 negative = ((byteRead & 0x80) != 0x00); 721 } 722 723 intValue <<= 8; 724 intValue |= (byteRead & 0xFF); 725 } 726 727 if (negative) 728 { 729 switch (length) 730 { 731 case 1: 732 intValue |= 0xFFFF_FF00; 733 break; 734 case 2: 735 intValue |= 0xFFFF_0000; 736 break; 737 case 3: 738 intValue |= 0xFF00_0000; 739 break; 740 } 741 } 742 743 totalBytesRead += length; 744 Debug.debugASN1Read(Level.INFO, "Integer", type, length, intValue); 745 return intValue; 746 } 747 748 749 750 /** 751 * Reads an ASN.1 integer element from the input stream and returns the value 752 * as a {@code Long}. 753 * 754 * @return The {@code Long} value of the ASN.1 integer element read, or 755 * {@code null} if the end of the input stream was reached before any 756 * data could be read. If {@code null} is returned, then the input 757 * stream will have been closed. 758 * 759 * @throws IOException If a problem occurs while reading from the input 760 * stream, if the end of the input stream is reached in 761 * the middle of the element, or or if an attempt is 762 * made to read an element larger than the maximum 763 * allowed size. 764 * 765 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1 766 * integer element. 767 */ 768 @Nullable() 769 public Long readLong() 770 throws IOException, ASN1Exception 771 { 772 final int type = readType(); 773 if (type < 0) 774 { 775 return null; 776 } 777 778 final int length = readLength(); 779 if ((length == 0) || (length > 8)) 780 { 781 skip(length); 782 throw new ASN1Exception(ERR_LONG_INVALID_LENGTH.get(length)); 783 } 784 785 boolean negative = false; 786 long longValue = 0; 787 for (int i=0; i < length; i++) 788 { 789 final int byteRead = read(false); 790 if (byteRead < 0) 791 { 792 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get()); 793 } 794 795 if (i == 0) 796 { 797 negative = ((byteRead & 0x80) != 0x00); 798 } 799 800 longValue <<= 8; 801 longValue |= (byteRead & 0xFFL); 802 } 803 804 if (negative) 805 { 806 switch (length) 807 { 808 case 1: 809 longValue |= 0xFFFF_FFFF_FFFF_FF00L; 810 break; 811 case 2: 812 longValue |= 0xFFFF_FFFF_FFFF_0000L; 813 break; 814 case 3: 815 longValue |= 0xFFFF_FFFF_FF00_0000L; 816 break; 817 case 4: 818 longValue |= 0xFFFF_FFFF_0000_0000L; 819 break; 820 case 5: 821 longValue |= 0xFFFF_FF00_0000_0000L; 822 break; 823 case 6: 824 longValue |= 0xFFFF_0000_0000_0000L; 825 break; 826 case 7: 827 longValue |= 0xFF00_0000_0000_0000L; 828 break; 829 } 830 } 831 832 totalBytesRead += length; 833 Debug.debugASN1Read(Level.INFO, "Long", type, length, longValue); 834 return longValue; 835 } 836 837 838 839 /** 840 * Reads an ASN.1 integer element from the input stream and returns the value 841 * as a {@code BigInteger}. 842 * 843 * @return The {@code BigInteger} value of the ASN.1 integer element read, or 844 * {@code null} if the end of the input stream was reached before any 845 * data could be read. If {@code null} is returned, then the input 846 * stream will have been closed. 847 * 848 * @throws IOException If a problem occurs while reading from the input 849 * stream, if the end of the input stream is reached in 850 * the middle of the element, or or if an attempt is 851 * made to read an element larger than the maximum 852 * allowed size. 853 * 854 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1 855 * integer element. 856 */ 857 @Nullable() 858 public BigInteger readBigInteger() 859 throws IOException, ASN1Exception 860 { 861 final int type = readType(); 862 if (type < 0) 863 { 864 return null; 865 } 866 867 final int length = readLength(); 868 if (length == 0) 869 { 870 throw new ASN1Exception(ERR_BIG_INTEGER_DECODE_EMPTY_VALUE.get()); 871 } 872 873 final byte[] valueBytes = new byte[length]; 874 for (int i=0; i < length; i++) 875 { 876 final int byteRead = read(false); 877 if (byteRead < 0) 878 { 879 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get()); 880 } 881 882 valueBytes[i] = (byte) byteRead; 883 } 884 885 final BigInteger bigIntegerValue = new BigInteger(valueBytes); 886 887 totalBytesRead += length; 888 Debug.debugASN1Read(Level.INFO, "BigInteger", type, length, 889 bigIntegerValue); 890 return bigIntegerValue; 891 } 892 893 894 895 /** 896 * Reads an ASN.1 null element from the input stream. No value will be 897 * returned but the null element will be consumed. 898 * 899 * @throws IOException If a problem occurs while reading from the input 900 * stream, if the end of the input stream is reached in 901 * the middle of the element, or or if an attempt is 902 * made to read an element larger than the maximum 903 * allowed size. 904 * 905 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1 null 906 * element. 907 */ 908 public void readNull() 909 throws IOException, ASN1Exception 910 { 911 final int type = readType(); 912 if (type < 0) 913 { 914 return; 915 } 916 917 final int length = readLength(); 918 919 if (length != 0) 920 { 921 skip(length); 922 throw new ASN1Exception(ERR_NULL_HAS_VALUE.get()); 923 } 924 Debug.debugASN1Read(Level.INFO, "Null", type, 0, null); 925 } 926 927 928 929 /** 930 * Reads an ASN.1 octet string element from the input stream and returns the 931 * value as a byte array. 932 * 933 * @return The byte array value of the ASN.1 octet string element read, or 934 * {@code null} if the end of the input stream was reached before any 935 * data could be read. If {@code null} is returned, then the input 936 * stream will have been closed. 937 * 938 * @throws IOException If a problem occurs while reading from the input 939 * stream, if the end of the input stream is reached in 940 * the middle of the element, or or if an attempt is 941 * made to read an element larger than the maximum 942 * allowed size. 943 */ 944 @Nullable() 945 public byte[] readBytes() 946 throws IOException 947 { 948 final int type = readType(); 949 if (type < 0) 950 { 951 return null; 952 } 953 954 final int length = readLength(); 955 956 int valueBytesRead = 0; 957 int bytesRemaining = length; 958 final byte[] value = new byte[length]; 959 while (valueBytesRead < length) 960 { 961 final int bytesRead = read(value, valueBytesRead, bytesRemaining); 962 if (bytesRead < 0) 963 { 964 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get()); 965 } 966 967 valueBytesRead += bytesRead; 968 bytesRemaining -= bytesRead; 969 } 970 971 totalBytesRead += length; 972 Debug.debugASN1Read(Level.INFO, "byte[]", type, length, value); 973 return value; 974 } 975 976 977 978 /** 979 * Reads an ASN.1 octet string element from the input stream and returns the 980 * value as a {@code String} using the UTF-8 encoding. 981 * 982 * @return The {@code String} value of the ASN.1 octet string element read, 983 * or {@code null} if the end of the input stream was reached before 984 * any data could be read. If {@code null} is returned, then the 985 * input stream will have been closed. 986 * 987 * @throws IOException If a problem occurs while reading from the input 988 * stream, if the end of the input stream is reached in 989 * the middle of the element, or or if an attempt is 990 * made to read an element larger than the maximum 991 * allowed size. 992 */ 993 @Nullable() 994 public String readString() 995 throws IOException 996 { 997 final int type = readType(); 998 if (type < 0) 999 { 1000 return null; 1001 } 1002 1003 final int length = readLength(); 1004 1005 int valueBytesRead = 0; 1006 int bytesRemaining = length; 1007 final byte[] value = new byte[length]; 1008 while (valueBytesRead < length) 1009 { 1010 final int bytesRead = read(value, valueBytesRead, bytesRemaining); 1011 if (bytesRead < 0) 1012 { 1013 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get()); 1014 } 1015 1016 valueBytesRead += bytesRead; 1017 bytesRemaining -= bytesRead; 1018 } 1019 1020 totalBytesRead += length; 1021 1022 final String s = StaticUtils.toUTF8String(value); 1023 Debug.debugASN1Read(Level.INFO, "String", type, length, s); 1024 return s; 1025 } 1026 1027 1028 1029 /** 1030 * Reads an ASN.1 UTC time element from the input stream and returns the value 1031 * as a {@code Date}. 1032 * 1033 * @return The {@code Date} value of the ASN.1 UTC time element read, or 1034 * {@code null} if the end of the input stream was reached before any 1035 * data could be read. If {@code null} is returned, then the input 1036 * stream will have been closed. 1037 * 1038 * @throws IOException If a problem occurs while reading from the input 1039 * stream, if the end of the input stream is reached in 1040 * the middle of the element, or or if an attempt is 1041 * made to read an element larger than the maximum 1042 * allowed size. 1043 * 1044 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1 UTC 1045 * time element. 1046 */ 1047 @Nullable() 1048 public Date readUTCTime() 1049 throws IOException, ASN1Exception 1050 { 1051 final int type = readType(); 1052 if (type < 0) 1053 { 1054 return null; 1055 } 1056 1057 final int length = readLength(); 1058 1059 int valueBytesRead = 0; 1060 int bytesRemaining = length; 1061 final byte[] value = new byte[length]; 1062 while (valueBytesRead < length) 1063 { 1064 final int bytesRead = read(value, valueBytesRead, bytesRemaining); 1065 if (bytesRead < 0) 1066 { 1067 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get()); 1068 } 1069 1070 valueBytesRead += bytesRead; 1071 bytesRemaining -= bytesRead; 1072 } 1073 1074 totalBytesRead += length; 1075 1076 final String timestamp = StaticUtils.toUTF8String(value); 1077 final Date date = new Date(ASN1UTCTime.decodeTimestamp(timestamp)); 1078 Debug.debugASN1Read(Level.INFO, "UTCTime", type, length, timestamp); 1079 return date; 1080 } 1081 1082 1083 1084 /** 1085 * Reads the beginning of an ASN.1 sequence from the input stream and 1086 * returns a value that can be used to determine when the end of the sequence 1087 * has been reached. Elements which are part of the sequence may be read from 1088 * this ASN.1 stream reader until the 1089 * {@link ASN1StreamReaderSequence#hasMoreElements} method returns 1090 * {@code false}. 1091 * 1092 * @return An object which may be used to determine when the end of the 1093 * sequence has been reached, or {@code null} if the end of the input 1094 * stream was reached before any data could be read. If {@code null} 1095 * is returned, then the input stream will have been closed. 1096 * 1097 * @throws IOException If a problem occurs while reading from the input 1098 * stream, if the end of the input stream is reached in 1099 * the middle of the element, or or if an attempt is 1100 * made to read an element larger than the maximum 1101 * allowed size. 1102 */ 1103 @Nullable() 1104 public ASN1StreamReaderSequence beginSequence() 1105 throws IOException 1106 { 1107 final int type = readType(); 1108 if (type < 0) 1109 { 1110 return null; 1111 } 1112 1113 final int length = readLength(); 1114 1115 Debug.debugASN1Read(Level.INFO, "Sequence Header", type, length, null); 1116 return new ASN1StreamReaderSequence(this, (byte) type, length); 1117 } 1118 1119 1120 1121 /** 1122 * Reads the beginning of an ASN.1 set from the input stream and returns a 1123 * value that can be used to determine when the end of the set has been 1124 * reached. Elements which are part of the set may be read from this ASN.1 1125 * stream reader until the {@link ASN1StreamReaderSet#hasMoreElements} method 1126 * returns {@code false}. 1127 * 1128 * @return An object which may be used to determine when the end of the set 1129 * has been reached, or {@code null} if the end of the input stream 1130 * was reached before any data could be read. If {@code null} is 1131 * returned, then the input stream will have been closed. 1132 * 1133 * @throws IOException If a problem occurs while reading from the input 1134 * stream, if the end of the input stream is reached in 1135 * the middle of the element, or or if an attempt is 1136 * made to read an element larger than the maximum 1137 * allowed size. 1138 */ 1139 @Nullable() 1140 public ASN1StreamReaderSet beginSet() 1141 throws IOException 1142 { 1143 final int type = readType(); 1144 if (type < 0) 1145 { 1146 return null; 1147 } 1148 1149 final int length = readLength(); 1150 1151 Debug.debugASN1Read(Level.INFO, "Set Header", type, length, null); 1152 return new ASN1StreamReaderSet(this, (byte) type, length); 1153 } 1154 1155 1156 1157 /** 1158 * Reads a byte of data from the underlying input stream, optionally ignoring 1159 * socket timeout exceptions. 1160 * 1161 * @param initial Indicates whether this is the initial read for an element. 1162 * 1163 * @return The byte read from the input stream, or -1 if the end of the 1164 * input stream was reached. 1165 * 1166 * @throws IOException If a problem occurs while reading data. 1167 */ 1168 private int read(final boolean initial) 1169 throws IOException 1170 { 1171 if (saslClient != null) 1172 { 1173 if (saslInputStream != null) 1174 { 1175 final int b = saslInputStream.read(); 1176 if (b >= 0) 1177 { 1178 return b; 1179 } 1180 } 1181 1182 readAndDecodeSASLData(-1); 1183 return saslInputStream.read(); 1184 } 1185 1186 try 1187 { 1188 final int b = inputStream.read(); 1189 if ((saslClient == null) || (b < 0)) 1190 { 1191 return b; 1192 } 1193 else 1194 { 1195 // This should only happen the first time after the SASL client has been 1196 // installed. 1197 readAndDecodeSASLData(b); 1198 return saslInputStream.read(); 1199 } 1200 } 1201 catch (final SocketTimeoutException ste) 1202 { 1203 Debug.debugException(Level.FINEST, ste); 1204 1205 if ((initial && ignoreInitialSocketTimeout) || 1206 ((! initial) && ignoreSubsequentSocketTimeout)) 1207 { 1208 while (true) 1209 { 1210 try 1211 { 1212 return inputStream.read(); 1213 } 1214 catch (final SocketTimeoutException ste2) 1215 { 1216 Debug.debugException(Level.FINEST, ste2); 1217 } 1218 } 1219 } 1220 else 1221 { 1222 throw ste; 1223 } 1224 } 1225 } 1226 1227 1228 1229 /** 1230 * Reads data from the underlying input stream, optionally ignoring socket 1231 * timeout exceptions. 1232 * 1233 * @param buffer The buffer into which the data should be read. 1234 * @param offset The position at which to start placing the data that was 1235 * read. 1236 * @param length The maximum number of bytes to read. 1237 * 1238 * @return The number of bytes read, or -1 if the end of the input stream 1239 * was reached. 1240 * 1241 * @throws IOException If a problem occurs while reading data. 1242 */ 1243 private int read(@NotNull final byte[] buffer, final int offset, 1244 final int length) 1245 throws IOException 1246 { 1247 if (saslClient != null) 1248 { 1249 if (saslInputStream != null) 1250 { 1251 final int bytesRead = saslInputStream.read(buffer, offset, length); 1252 if (bytesRead > 0) 1253 { 1254 return bytesRead; 1255 } 1256 } 1257 1258 readAndDecodeSASLData(-1); 1259 return saslInputStream.read(buffer, offset, length); 1260 } 1261 1262 try 1263 { 1264 return inputStream.read(buffer, offset, length); 1265 } 1266 catch (final SocketTimeoutException ste) 1267 { 1268 Debug.debugException(Level.FINEST, ste); 1269 if (ignoreSubsequentSocketTimeout) 1270 { 1271 while (true) 1272 { 1273 try 1274 { 1275 return inputStream.read(buffer, offset, length); 1276 } 1277 catch (final SocketTimeoutException ste2) 1278 { 1279 Debug.debugException(Level.FINEST, ste2); 1280 } 1281 } 1282 } 1283 else 1284 { 1285 throw ste; 1286 } 1287 } 1288 } 1289 1290 1291 1292 /** 1293 * Sets the SASL client to use to unwrap any data read over this ASN.1 stream 1294 * reader. 1295 * 1296 * @param saslClient The SASL client to use to unwrap any data read over 1297 * this ASN.1 stream reader. 1298 */ 1299 void setSASLClient(@NotNull final SaslClient saslClient) 1300 { 1301 this.saslClient = saslClient; 1302 } 1303 1304 1305 1306 /** 1307 * Reads data from the underlying input stream, unwraps it using the 1308 * configured SASL client, and makes the result available in a byte array 1309 * input stream that will be used for subsequent reads. 1310 * 1311 * @param firstByte The first byte that has already been read. This should 1312 * only be used if the value is greater than or equal to 1313 * zero. 1314 * 1315 * @throws IOException If a problem is encountered while reading from the 1316 * underlying input stream or decoding the data that 1317 * has been read. 1318 */ 1319 private void readAndDecodeSASLData(final int firstByte) 1320 throws IOException 1321 { 1322 // The first four bytes must be the number of bytes of data to unwrap. 1323 int numWrappedBytes = 0; 1324 int numLengthBytes = 4; 1325 if (firstByte >= 0) 1326 { 1327 numLengthBytes = 3; 1328 numWrappedBytes = firstByte; 1329 } 1330 1331 for (int i=0; i < numLengthBytes; i++) 1332 { 1333 final int b = inputStream.read(); 1334 if (b < 0) 1335 { 1336 if ((i == 0) && (firstByte < 0)) 1337 { 1338 // This means that we hit the end of the input stream without 1339 // reading any data. This is fine and just means that the end of 1340 // the input stream has been reached. 1341 saslInputStream = new ByteArrayInputStream(StaticUtils.NO_BYTES); 1342 } 1343 else 1344 { 1345 // This means that we hit the end of the input stream after having 1346 // read a portion of the number of wrapped bytes. This is an error. 1347 throw new IOException( 1348 ERR_STREAM_READER_EOS_READING_SASL_LENGTH.get(i)); 1349 } 1350 } 1351 else 1352 { 1353 numWrappedBytes = (numWrappedBytes << 8) | (b & 0xFF); 1354 } 1355 } 1356 1357 if ((maxElementSize > 0) && (numWrappedBytes > maxElementSize)) 1358 { 1359 throw new IOException(ERR_READ_SASL_LENGTH_EXCEEDS_MAX.get( 1360 numWrappedBytes, maxElementSize)); 1361 } 1362 1363 int wrappedDataPos = 0; 1364 final byte[] wrappedData = new byte[numWrappedBytes]; 1365 while (true) 1366 { 1367 final int numBytesRead = inputStream.read(wrappedData, wrappedDataPos, 1368 (numWrappedBytes - wrappedDataPos)); 1369 if (numBytesRead < 0) 1370 { 1371 throw new IOException(ERR_STREAM_READER_EOS_READING_SASL_DATA.get( 1372 wrappedDataPos, numWrappedBytes)); 1373 } 1374 1375 wrappedDataPos += numBytesRead; 1376 if (wrappedDataPos >= numWrappedBytes) 1377 { 1378 break; 1379 } 1380 } 1381 1382 final byte[] unwrappedData = 1383 saslClient.unwrap(wrappedData, 0, numWrappedBytes); 1384 saslInputStream = new ByteArrayInputStream(unwrappedData, 0, 1385 unwrappedData.length); 1386 } 1387}