/protocols/asn/asn-impl/src/main/java/org/mobicents/protocols/asn/AsnInputStream.java
Java | 1046 lines | 632 code | 189 blank | 225 comment | 194 complexity | 6668d8abcdf33164a2f3e8fbe157cb55 MD5 | raw file
1/* 2 * JBoss, Home of Professional Open Source 3 * Copyright 2011, Red Hat, Inc. and individual contributors 4 * by the @authors tag. See the copyright.txt in the distribution for a 5 * full listing of individual contributors. 6 * 7 * This is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU Lesser General Public License as 9 * published by the Free Software Foundation; either version 2.1 of 10 * the License, or (at your option) any later version. 11 * 12 * This software is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this software; if not, write to the Free 19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 21 */ 22 23package org.mobicents.protocols.asn; 24 25import java.io.ByteArrayOutputStream; 26import java.io.EOFException; 27import java.io.IOException; 28import java.io.InputStream; 29import java.io.OutputStream; 30import java.nio.ByteBuffer; 31import java.util.BitSet; 32 33/** 34 * 35 * @author amit bhayani 36 * @author baranowb 37 * @author sergey vetyutnev 38 */ 39public class AsnInputStream extends InputStream { 40 41 private static final String _REAL_BASE10_CHARSET = "US-ASCII"; 42 private static final int DATA_BUCKET_SIZE = 1024; 43 44 private byte[] buffer; 45 46 private int start; 47 private int length; 48 private int pos; 49 50 private int tagClass = 0; 51 private int pCBit = 0; 52 private int tag; 53 54 55 public AsnInputStream( byte[] buf ) { 56 this.buffer = buf; 57 this.length = buf.length; 58 } 59 60 public AsnInputStream( byte[] buf, int tagClass, boolean isPrimitive, int tag ) { 61 this.buffer = buf; 62 this.length = buf.length; 63 64 this.tagClass = tagClass; 65 if (isPrimitive) 66 this.pCBit = 0; 67 else 68 this.pCBit = 1; 69 this.tag = tag; 70 } 71 72 protected AsnInputStream( AsnInputStream buf, int start, int length ) throws IOException { 73 this.buffer = buf.buffer; 74 this.start = buf.start + start; 75 this.length = length; 76 77 if (start < 0 || start > buf.length || this.start < 0 || this.start > this.buffer.length || this.length < 0 78 || this.start + this.length > this.buffer.length) 79 throw new IOException("Bad start or length values when creating AsnInputStream"); 80 81 this.tagClass = buf.tagClass; 82 this.pCBit = buf.pCBit; 83 this.tag = buf.tag; 84 } 85 86 @Deprecated 87 public AsnInputStream(InputStream in) { 88 try { 89 int av = in.available(); 90 byte[] buf = new byte[av]; 91 in.read(buf); 92 93 this.buffer = buf; 94 this.length = buf.length; 95 96 } catch (IOException e) { 97 e.printStackTrace(); 98 99 this.buffer = new byte[0]; 100 } 101 } 102 103 104 /** 105 * Return the current position in the stream 106 * 107 * @return 108 */ 109 public int position() { 110 return this.pos; 111 } 112 113 /** 114 * Set the new current position of the stream 115 * If the new position is bad - throws IOException 116 * 117 * @param newPosition 118 * @throws IOException 119 */ 120 public void position( int newPosition ) throws IOException { 121 if (newPosition < 0 || newPosition > this.length) 122 throw new IOException("Bad newPosition value when setting the new position in the AsnInputStream"); 123 124 this.pos = newPosition; 125 } 126 127 /** 128 * Return the count of available bytes to read 129 * 130 * @return 131 */ 132 @Override 133 public int available() { 134 return this.length - this.pos; 135 } 136 137 /** 138 * Advance the stream current position for byteCount bytes 139 * If the new position is bad - throws IOException 140 * 141 * @param byteCount 142 * @throws IOException 143 */ 144 public void advance(int byteCount) throws IOException { 145 this.position(this.pos + byteCount); 146 } 147 148 @Override 149 public long skip(long n) throws IOException { 150 if (n < 0) 151 n = 0; 152 int newPosition = this.pos + (int) n; 153 if (newPosition < 0 || newPosition > this.length) 154 newPosition = this.length; 155 156 long skipCnt = newPosition - this.pos; 157 this.pos = newPosition; 158 159 return skipCnt; 160 } 161 162 @Override 163 public boolean markSupported() { 164 return false; 165 } 166 167 /** 168 * Get a byte from stream and return it. 169 * If end of stream - throws IOException 170 * 171 * @return 172 */ 173 @Override 174 public int read() throws IOException { 175 if (this.available() == 0) 176 throw new EOFException("AsnInputStream has reached the end"); 177 178 return this.buffer[this.start + this.pos++]; 179 } 180 181 /** 182 * Fill the byte with bytes from the stream. If stream contains not enough 183 * data - only the part of array is filled 184 * 185 * @param b 186 * The byte array to be filled by data 187 * @param off 188 * Offset of byte array from which fill the array 189 * @param len 190 * Bytes count to fill 191 * @return Bytes count that have really read 192 * @throws IOException 193 */ 194 @Override 195 public int read(byte[] b, int off, int len) throws IOException { 196 197 if (len > b.length) 198 len = b.length; 199 200 int cnt = this.available(); 201 if (cnt > len) 202 cnt = len; 203 204 if (b == null || off < 0 || len < 0 || off + len > b.length) 205 throw new EOFException("Target byte array is null or bad off or len values"); 206 207 System.arraycopy(this.buffer, this.start + this.pos, b, off, cnt); 208 this.pos += cnt; 209 210 return cnt; 211 } 212 213 /** 214 * Fill the byte with bytes from the stream. If stream contains not enough 215 * data - only the part of array is filled 216 * 217 * @param b 218 * The byte array to be filled by data 219 * @return Bytes count that have really read 220 * @throws IOException 221 */ 222 @Override 223 public int read(byte[] b) throws IOException { 224 225 if (b == null ) 226 throw new EOFException("Target byte array is null"); 227 228 return this.read(b, 0, b.length); 229 } 230 231 /** 232 * Reads the tag field. Returns the tag value. 233 * Tag class and primitive / constructive mark can be get then by getTagClass() and isTagPrimitive() methods 234 * 235 * @return 236 * @throws IOException 237 */ 238 public int readTag() throws IOException { 239 byte b = (byte) this.read(); 240 241 this.tagClass = (b & Tag.CLASS_MASK) >> 6; 242 this.pCBit = (b & Tag.PC_MASK) >> 5; 243 244 this.tag = b & Tag.TAG_MASK; 245 246 // For larger tag values, the first octet has all ones in bits 5 to 1, 247 // and the tag value is then encoded in 248 // as many following octets as are needed, using only the least 249 // significant seven bits of each octet, 250 // and using the minimum number of octets for the encoding. The most 251 // significant bit (the "more" 252 // bit) is set to 1 in the first following octet, and to zero in the 253 // last. 254 if (tag == Tag.TAG_MASK) { 255 byte temp; 256 tag = 0; 257 do { 258 temp = (byte) this.read(); 259 tag = (tag << 7) | (0x7F & temp); 260 } while (0 != (0x80 & temp)); 261 } 262 263 return tag; 264 } 265 266 public int getTagClass() { 267 return tagClass; 268 } 269 270 public int getTag() { 271 return tag; 272 } 273 274 public boolean isTagPrimitive() { 275 return pCBit == Tag.PC_PRIMITIVITE; 276 } 277 278 /** 279 * Reads and returns the length field. 280 * In case of indefinite length returns Tag.Indefinite_Length value 281 * 282 * @return 283 * @throws IOException 284 */ 285 public int readLength() throws IOException { 286 int length = 0; 287 288 byte b = (byte) this.read(); 289 290 // This is short form. The short form can be used if the number of 291 // octets in the Value part is less than or 292 // equal to 127, and can be used whether the Value part is primitive or 293 // constructed. This form is identified by 294 // encoding bit 8 as zero, with the length count in bits 7 to 1 (as 295 // usual, with bit 7 the most significant bit 296 // of the length). 297 if ((b & 0x80) == 0) { 298 return b; 299 } 300 301 // This is indefinite form. The indefinite form of length can only be 302 // used (but does not have to be) if the V 303 // part is constructed, that 304 // is to say, consists of a series of TLVs. In the indefinite form of 305 // length the first bit of the first octet is 306 // set to 1, as for the long form, but the value N is set to zero. 307 b = (byte) (b & 0x7F); 308 if (b == 0) { 309 return Tag.Indefinite_Length; 310 } 311 312 // If bit 8 of the first length octet is set to 1, then we have the long 313 // form of length. In long form, the first 314 // octet encodes in its remaining seven bits a value N which is the 315 // length of a series of octets that themselves 316 // encode the length of the Value part. 317 byte temp; 318 for (int i = 0; i < b; i++) { 319 temp = (byte) this.read(); 320 length = (length << 8) | (0x00FF & temp); 321 } 322 323 return length; 324 } 325 326 /** 327 * This method can be invoked after the sequence tag has been read 328 * Returns the AsnInputStream that contains the sequence data 329 * The origin stream advances to the begin of the next record 330 * 331 * @return 332 * @throws AsnException 333 * @throws IOException 334 */ 335 public AsnInputStream readSequenceStream() throws AsnException, IOException { 336 337 int length = readLength(); 338 return this.readSequenceStreamData(length); 339 } 340 341 /** 342 * This method can be invoked after the sequence tag has been read 343 * Returns the byte array that contains the sequence data 344 * The origin stream advances to the begin of the next record 345 * 346 * @return 347 * @throws AsnException 348 * @throws IOException 349 */ 350 public byte[] readSequence() throws AsnException, IOException { 351 352 int length = readLength(); 353 return this.readSequenceData(length); 354 } 355 356 /** 357 * This method can be invoked after the sequence tag and length has been 358 * read. Returns the AsnInputStream that contains the sequence data. The origin 359 * stream advances to the begin of the next record 360 * 361 * @param length 362 * The sequence length 363 * @return 364 * @throws AsnException 365 * @throws IOException 366 */ 367 public AsnInputStream readSequenceStreamData(int length) throws AsnException, IOException { 368 369 if (length == Tag.Indefinite_Length) { 370 return this.readSequenceIndefinite(); 371 } else { 372 int startPos = this.pos; 373 this.advance(length); 374 return new AsnInputStream(this, startPos, length); 375 } 376 } 377 378 /** 379 * This method can be invoked after the sequence tag and length has been 380 * read. Returns the byte stream that contains the sequence data. The origin 381 * stream advances to the begin of the next record 382 * 383 * @param length 384 * The sequence length 385 * @return 386 * @throws AsnException 387 * @throws IOException 388 */ 389 public byte[] readSequenceData(int length) throws AsnException, IOException { 390 391 AsnInputStream ais = this.readSequenceStreamData(length); 392 byte[] res = new byte[ais.length]; 393 System.arraycopy(ais.buffer, ais.start + ais.pos, res, 0, ais.length); 394 return res; 395 } 396 397 public AsnInputStream readSequenceIndefinite() throws AsnException, IOException { 398 399 int startPos = this.pos; 400 this.advanceIndefiniteLength(); 401 return new AsnInputStream(this, startPos, this.pos - startPos - 2); 402 } 403 404 public byte[] readIndefinite() throws AsnException, IOException { 405 406 int startPos = this.pos; 407 this.advanceIndefiniteLength(); 408 409 byte[] res = new byte[this.pos - startPos - 2]; 410 System.arraycopy(this.buffer, this.start + startPos, res, 0, this.pos - startPos - 2); 411 return res; 412 413 } 414 415 private void advanceIndefiniteLength() throws AsnException, IOException { 416 417 while (this.available() > 0) { 418 419 // found End-of-contents tag 420 int tag = this.readTag(); 421 if (tag == 0 && this.tagClass == 0) { 422 if (this.read() == 0) 423 return; 424 else 425 throw new AsnException("End-of-contents tag must have the zero length"); 426 } 427 428 int length = this.readLength(); 429 if (length == Tag.Indefinite_Length) 430 this.advanceIndefiniteLength(); 431 else 432 this.advance(length); 433 } 434 } 435 436 /** 437 * Skip length and content fields of primitive and constructed element (definite and indefinite length supported) 438 * 439 * @throws IOException 440 * @throws AsnException 441 */ 442 public void advanceElement() throws IOException, AsnException { 443 int length = this.readLength(); 444 this.advanceElementData(length); 445 } 446 447 /** 448 * Skip content field of primitive and constructed element (definite and indefinite length supported) 449 * 450 * @param length 451 * @throws IOException 452 * @throws AsnException 453 */ 454 public void advanceElementData(int length) throws IOException, AsnException { 455 if( length==Tag.Indefinite_Length ) 456 this.advanceIndefiniteLength(); 457 else 458 this.advance(length); 459 } 460 461 public boolean readBoolean() throws AsnException, IOException { 462 463 int length = readLength(); 464 return this.readBooleanData(length); 465 } 466 467 public boolean readBooleanData(int length) throws AsnException, IOException { 468 469 if (this.pCBit != 0 || length != 1) 470 throw new AsnException("Failed when parsing the Boolean field: this field must be primitive and the length must be equal 1"); 471 472 byte temp = (byte) this.read(); 473 474 // If temp is not zero stands for true irrespective of actual Value 475 return (temp != 0); 476 } 477 478 public long readInteger() throws AsnException, IOException { 479 480 int length = this.readLength(); 481 return this.readIntegerData(length); 482 } 483 484 public long readIntegerData(int length) throws AsnException, IOException { 485 long value = 0; 486 byte temp; 487 488 if (this.pCBit != 0 || length == 0 || length == Tag.Indefinite_Length) 489 throw new AsnException("Failed when parsing the Interger field: this field must be primitive and have the length more then zero"); 490 491 temp = (byte) this.read(); 492 value = temp; 493 494 for (int i = 0; i < length - 1; i++) { 495 temp = (byte) this.read(); 496 value = (value << 8) | (0x00FF & temp); 497 } 498 499 return value; 500 } 501 502 public double readReal() throws AsnException, IOException { 503 504 int length = readLength(); 505 return readRealData(length); 506 } 507 508 public double readRealData(int length) throws AsnException, IOException { 509 510 if (this.pCBit != 0 || length == Tag.Indefinite_Length) 511 throw new AsnException("Failed when parsing the Real field: this field must be primitive"); 512 513 // universal part 514 if (length == 0) { 515 // yeah, nice 516 return 0.0; 517 } 518 519 if (length == 1) { 520 // +INF/-INF 521 int b = this.read() & 0xFF; 522 if (b == 0x40) { 523 return Double.POSITIVE_INFINITY; 524 } else if (b == 0x41) { 525 return Double.NEGATIVE_INFINITY; 526 } else { 527 throw new AsnException( 528 "Failed when parsing the Real field: Real length indicates positive/negative infinity, but value is wrong: " 529 + Integer.toBinaryString(b)); 530 } 531 } 532 int infoBits = this.read(); 533 // substract on for info bits 534 length--; 535 536 // only binary has first bit of info set to 1; 537 // now the tricky part, this takes into account base10 538 if ((infoBits & 0xC0) == 0) { 539 540 // FIXME: add check on boundry of simple length 541 // encoded as char string 542 // IA5 == ASCII...? 543 // ............................. 544 String nrRep = new String(this.buffer, this.start + this.pos, length, _REAL_BASE10_CHARSET); 545 // this will swallow NR(1-3) and give proper double :) 546 return Double.parseDouble(nrRep); 547 // ............................. 548 549 } else if((infoBits & 0x80) == 0x80) { 550 551 // encoded binary - mantisa and all that funny digits. 552 // the REAL type has been semantically equivalent to the 553 // type: 554 // [UNIVERSAL 9] IMPLICIT SEQUENCE { 555 // mantissa INTEGER (ALL EXCEPT 0), 556 // base INTEGER (2|10), 557 // exponent INTEGER } 558 // sign x N x (2 ^ scale) x (base ^ E); --> base ^ E == 2 ^(E+x) == 559 // where x 560 int tmp = 0; 561 562 int signBit = (infoBits & BERStatics.REAL_BB_SIGN_MASK) << 1; 563 // now lets determine length of e(exponent) and n(positive integer) 564 long e = 0; 565 int s = (infoBits & BERStatics.REAL_BB_SCALE_MASK) >> 2; 566 567 tmp = infoBits & BERStatics.REAL_BB_EE_MASK; 568 if (tmp == 0x0) { 569 e = this.read() & 0xFF; 570 length--; 571 // real representation 572 } else if (tmp == 0x01) { 573 e = (this.read() & 0xFF) << 8; 574 length--; 575 e |= this.read() & 0xFF; 576 length--; 577 if (e > 0x7FF) { 578 579 // to many bits... Double 580 throw new AsnException( 581 "Exponent part has to many bits lit, allowed are 11, present: " 582 + Long.toBinaryString(e)); 583 } 584 // prepare E to become bits - this may cause loose of data, 585 e &= 0x7FF; 586 } else { 587 // this is too big for java to handle.... we can have up to 11 588 // bits.. 589 throw new AsnException( 590 "Exponent part has to many bits lit, allowed are 11, but stream indicates 3 or more octets"); 591 } 592 // now we may read up to 52bits 593 // 7*8 == 56, we need up to 52 594 if (length > 7) { 595 throw new AsnException( 596 "Length exceeds JAVA double mantisa size"); 597 } 598 599 long n = 0; 600 while (length > 0) { 601 --length; 602 long readV = (((long) this.read() << 32) >>> 32) & 0xFF; 603 604 readV = readV << (length * 8); 605 606 n |= readV; 607 } 608 609 // check for possible overflow 610 if ((n & 0x0FFFFFFF) > 4503599627370495L) { // num is 11 bits lit to 611 // "1" 612 throw new AsnException("Overflow on mantisa"); 613 } 614 // we have real part, now lets add that scale; this is M x (2^F), 615 // which essentialy is bit shift :) 616 int shift = (int) Math.pow(2, s) - 1; // -1 for 2, where we dont 617 // shift 618 n = n << (shift); // this might be bad code. 619 620 // now lets take care of different base, we are base2: base8 == 621 // base2^3,base16== base2^4 622 int base = (infoBits & BERStatics.REAL_BB_BASE_MASK) >> 4; 623 // is this correct? 624 if (base == 0x01) { 625 e = e * 3; // (2^3)^e 626 } else if (base == 0x10) { 627 e = e * 4; // (2^4)^e 628 } 629 // do check again. 630 if (e > 0x7FF) { 631 // to many bits... Double 632 throw new AsnException( 633 "Exponent part has to many bits lit, allowed are 11, present: " 634 + Long.toBinaryString(e)); 635 } 636 637 // double is 8bytes 638 byte[] doubleRep = new byte[8]; 639 // set sign, no need to shift 640 doubleRep[0] = (byte) (signBit); 641 // now get first 7 bits of e; 642 doubleRep[0] |= ((e >> 4) & 0xFF); 643 doubleRep[1] = (byte) ((e & 0x0F) << 4); 644 // from back its easier 645 doubleRep[7] = (byte) n; 646 doubleRep[6] = (byte) (n >> 8); 647 doubleRep[5] = (byte) (n >> 16); 648 doubleRep[4] = (byte) (n >> 24); 649 doubleRep[3] = (byte) (n >> 32); 650 doubleRep[2] = (byte) (n >> 40); 651 doubleRep[1] |= (byte) ((n >> 48) & 0x0F); 652 ByteBuffer bb = ByteBuffer.wrap(doubleRep); 653 return bb.getDouble(); 654 } else { 655 throw new AsnException("Failed when parsing the Real field: Unknown infoBits: " + infoBits); 656 } 657 } 658 659 public BitSetStrictLength readBitString() throws AsnException, IOException { 660 661 int length = this.readLength(); 662 return this.readBitStringData(length); 663 } 664 665 public BitSetStrictLength readBitStringData(int length) throws AsnException, IOException { 666 667 BitSetStrictLength bitSet = new BitSetStrictLength(0); 668 int bitCount = this._readBitString(bitSet, length, 0); 669 bitSet.setStrictLength(bitCount); 670 671 return bitSet; 672 } 673 674 @Deprecated 675 public void readBitString(BitSet bitSet) throws AsnException, IOException { 676 677 int length = this.readLength(); 678 this.readBitStringData(bitSet, length); 679 } 680 681 @Deprecated 682 public void readBitStringData(BitSet bitSet, int length) throws AsnException, IOException { 683 this._readBitString(bitSet, length, 0); 684 } 685 686 @Deprecated 687 public void readBitStringData(BitSet bitSet, int length, boolean isTagPrimitive) throws AsnException, IOException { 688 if (isTagPrimitive) 689 this.pCBit = 0; 690 else 691 this.pCBit = 1; 692 693 this._readBitString(bitSet, length, 0); 694 } 695 696 private int _readBitString(BitSet bitSet, int length, int counter) throws AsnException, 697 IOException { 698 699 int bits = 0; 700 701 if (this.pCBit == 0) { 702 int pad = this.read(); 703 704 // TODO We are assuming that there is always pad, even if it is 00. 705 // This may not be true for some 706 // Constructed 707 // BitString where padding is only applied to last TLV. In which 708 // case this algo is incorrect 709 for (int count = 1; count < (length - 1); count++) { 710 byte dataByte = (byte) this.read(); 711 for (bits = 0; bits < 8; bits++) { 712 if (0 != (dataByte & (0x80 >> bits))) { 713 bitSet.set(counter); 714 } 715 ++counter; 716 } 717 } 718 719 byte lastByte = (byte) this.read(); 720 for (bits = 0; bits < (8 - pad); bits++) { 721 if (0 != (lastByte & (0x80 >> bits))) { 722 bitSet.set(counter); 723 } 724 ++counter; 725 } 726 727 return counter; 728 729 } else { 730 if (length == Tag.Indefinite_Length) { 731 while (true) { 732 int tag = this.readTag(); 733 if (tag == 0) { 734 length = this.read(); 735 if (length == 0) 736 break; 737 else 738 throw new AsnException("Error while decoding the bit-string: End-of-contents tag must have the zero length"); 739 } 740 741 if (tag != Tag.STRING_BIT || this.tagClass != Tag.CLASS_UNIVERSAL) 742 throw new AsnException("Error while decoding the bit-string: subsequent bit string tag must be CLASS_UNIVERSAL - STRING_BIT"); 743 744 int length2 = this.readLength(); 745 counter = _readBitString(bitSet, length2, counter); 746 } 747 } else { 748 int startPos = this.pos; 749 while (true) { 750 if (this.pos > startPos + length) 751 throw new AsnException("Error while decoding the bit-string: constructed bit-string content do not fit its length"); 752 if (this.pos == startPos + length) 753 break; 754 755 int tag = this.readTag(); 756 if (tag != Tag.STRING_BIT || this.tagClass != Tag.CLASS_UNIVERSAL) 757 throw new AsnException("Error while decoding the bit-string: subsequent bit string string tag must be CLASS_UNIVERSAL - STRING_BIT"); 758 759 int length2 = this.readLength(); 760 if (this.pos + length2 > startPos + length) 761 throw new AsnException("Error while decoding the bit-string: subsequent bit string is unconsistent"); 762 763 counter = _readBitString(bitSet, length2, counter); 764 } 765 } 766 return counter; 767 } 768 } 769 770 public byte[] readOctetString() throws AsnException, IOException { 771 772 int length = this.readLength(); 773 return this.readOctetStringData(length); 774 } 775 776 @Deprecated 777 public void readOctetString(OutputStream outputStream) throws AsnException, IOException { 778 779 int length = this.readLength(); 780 this.readOctetStringData(outputStream, length); 781 } 782 783 public byte[] readOctetStringData(int length) throws AsnException, IOException { 784 785 if (this.pCBit == 0) { 786 if (length == Tag.Indefinite_Length) 787 throw new AsnException("Error while decoding the octet-string: primitive with Indefinite_Length"); 788 byte[] buf = new byte[length]; 789 int cnt = this.read(buf); 790 if (cnt != length) 791 throw new AsnException("Error while decoding the octet-string: not enouph data for the octet string"); 792 return buf; 793 } else { 794 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 795 this._readOctetString(outputStream, length); 796 return outputStream.toByteArray(); 797 } 798 } 799 800 @Deprecated 801 public void readOctetStringData(OutputStream outputStream, int length) throws AsnException, IOException { 802 803 this._readOctetString(outputStream, length); 804 } 805 806 @Deprecated 807 public void readOctetStringData(OutputStream outputStream, int length, boolean isTagPrimitive) throws AsnException, IOException { 808 809 if (isTagPrimitive) 810 this.pCBit = 0; 811 else 812 this.pCBit = 1; 813 814 this._readOctetString(outputStream, length); 815 } 816 817 private void _readOctetString(OutputStream outputStream, int length) throws AsnException, IOException { 818 819 if (this.pCBit == 0) { 820 821 this.fillOutputStream(outputStream, length); 822 } else { 823 824 if (length == Tag.Indefinite_Length) { 825 while (true) { 826 int tag = this.readTag(); 827 if (tag == 0) { 828 length = this.read(); 829 if (length == 0) 830 break; 831 else 832 throw new AsnException("Error while decoding the octet-string: End-of-contents tag must have the zero length"); 833 } 834 835 if (tag != Tag.STRING_OCTET || this.tagClass != Tag.CLASS_UNIVERSAL) 836 throw new AsnException("Error while decoding the octet-string: subsequent octet string tag must be CLASS_UNIVERSAL - STRING_BIT"); 837 838 int length2 = this.readLength(); 839 this._readOctetString(outputStream, length2); 840 } 841 } else { 842 int startPos = this.pos; 843 while (true) { 844 if (this.pos == startPos + length) 845 break; 846 847 int tag = this.readTag(); 848 if (tag != Tag.STRING_OCTET || this.tagClass != Tag.CLASS_UNIVERSAL) 849 throw new AsnException("Error while decoding the octet-string: subsequent octet string string tag must be CLASS_UNIVERSAL - STRING_BIT"); 850 851 int length2 = this.readLength(); 852 if (this.pos + length2 > startPos + length) 853 throw new AsnException("Error while decoding the octet-string: subsequent octet string is unconsistent"); 854 855 this._readOctetString(outputStream, length2); 856 } 857 } 858 } 859 } 860 861 // private helper methods ------------------------------------------------- 862 private void fillOutputStream(OutputStream stream, int length) 863 throws AsnException, IOException { 864 byte[] dataBucket = new byte[DATA_BUCKET_SIZE]; 865 int readCount; 866 867 while (length != 0) { 868 int cnt = length < DATA_BUCKET_SIZE ? length : DATA_BUCKET_SIZE; 869 readCount = read(dataBucket, 0, cnt); 870 if (readCount < cnt) 871 throw new AsnException("input stream has reached the end"); 872 stream.write(dataBucket, 0, readCount); 873 length -= readCount; 874 } 875 } 876 877 public void readNull() throws AsnException, IOException { 878// int tagValue = this.readTag(); 879 880 int length = readLength(); 881 this.readNullData(length); 882 } 883 884 public void readNullData(int length) throws AsnException, IOException { 885 if (this.pCBit != 0 || length != 0) 886 throw new AsnException("Failed when parsing the NULL field: this field must be primitive and the length must be equal 0"); 887 } 888 889 public long[] readObjectIdentifier() throws AsnException, IOException { 890 891 int length = readLength(); 892 return this.readObjectIdentifierData(length); 893 } 894 895 public long[] readObjectIdentifierData(int length) throws AsnException, IOException { 896 897 if (this.pCBit != 0 || length == Tag.Indefinite_Length) 898 throw new AsnException("Failed when parsing the ObjectIdentifier field: this field must be primitive and the length must be defined"); 899 900 byte[] data = new byte[length]; 901 read(data); 902 903 length = 2; 904 for (int i = 1; i < data.length; ++i) { 905 if (data[i] >= 0) 906 ++length; 907 } 908 909 long[] oids = new long[length]; 910 int b = 0x00FF & data[0]; 911 912 // The first octet has value 40 * value1 + value2. 913 oids[0] = b / 40; 914 if (oids[0] == 0 || oids[0] == 1) 915 oids[1] = b % 40; 916 else { 917 oids[0] = 2; 918 oids[1] = b - 80; 919 } 920 921 int v = 0; 922 length = 2; 923 for (int i = 1; i < data.length; ++i) { 924 925 byte b1 = data[i]; 926 if ((b1 & 0x80) != 0x0) { 927 v = (v << 7) | ((b1 & 0x7F)); 928 } else { 929 v = (v << 7) | (b1 & 0x7F); 930 oids[length++] = v; 931 v = 0; 932 } 933 } 934 935 if (length == oids.length) 936 return oids; 937 else { 938 long[] oids2 = new long[length]; 939 System.arraycopy(oids, 0, oids2, 0, length); 940 return oids2; 941 } 942 } 943 944 public String readIA5String() throws AsnException, IOException { 945 946 int length = readLength(); 947 return readString(BERStatics.STRING_IA5_ENCODING, Tag.STRING_IA5, length); 948 } 949 950 public String readIA5StringData(int length) throws AsnException, IOException { 951 952 return readString(BERStatics.STRING_IA5_ENCODING, Tag.STRING_IA5, length); 953 } 954 955 public String readUTF8String() throws AsnException, IOException { 956 957 int length = readLength(); 958 return readString(BERStatics.STRING_UTF8_ENCODING, Tag.STRING_UTF8, length); 959 } 960 961 public String readUTF8StringData(int length) throws AsnException, IOException { 962 963 return readString(BERStatics.STRING_UTF8_ENCODING, Tag.STRING_UTF8, length); 964 } 965 966 public String readGraphicString() throws AsnException, IOException { 967 968 int length = readLength(); 969 return readString(BERStatics.STRING_IA5_ENCODING, Tag.STRING_GRAPHIC, length); 970 } 971 972 public String readGraphicStringData(int length) throws AsnException, IOException { 973 974 return readString(BERStatics.STRING_IA5_ENCODING, Tag.STRING_GRAPHIC, length); 975 } 976 977 private String readString(String charset, int tagValue, int length) throws IOException, 978 AsnException { 979 980 if (pCBit == 0) { 981 byte[] buf = new byte[length]; 982 int readCnt = this.read(buf); 983 if (readCnt < length) 984 throw new AsnException("Error decoding string fieald: not enough data in the stream"); 985 986 String s = new String(buf, charset); 987 return s; 988 } else { 989 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 990 this.readConstructedString(bos, tagValue, length); 991 992 String s = new String(bos.toByteArray(), charset); 993 return s; 994 } 995 } 996 997 private void readConstructedString(ByteArrayOutputStream bos, int parentTag, int length) 998 throws AsnException, IOException { 999 1000 AsnInputStream ais = this.readSequenceStreamData(length); 1001 1002 while(true) { 1003 if (ais.available() == 0) 1004 break; 1005 1006 int localTag = ais.readTag(); 1007 if (parentTag != localTag) 1008 throw new AsnException("Error decoding string fieald: Parent tag=" + parentTag + ", does not match member tag=" + localTag); 1009 1010 int localLength = ais.readLength(); 1011 if (ais.pCBit == 0) { 1012 byte[] buf = new byte[localLength]; 1013 int readCnt = ais.read(buf); 1014 if (readCnt < localLength) 1015 throw new AsnException("Error decoding string fieald: not enough data in the stream"); 1016 bos.write(buf); 1017 } else { 1018 ais.readConstructedString(bos, parentTag, localLength); 1019 } 1020 } 1021 } 1022 1023 @Override 1024 public String toString() { 1025 StringBuilder sb = new StringBuilder(); 1026 1027 sb.append("Size="); 1028 sb.append(this.length); 1029 sb.append(", Pos="); 1030 sb.append(this.pos); 1031 sb.append(", Tag="); 1032 sb.append(this.tag); 1033 sb.append(", TagClass="); 1034 sb.append(this.tagClass); 1035 sb.append(", pCBit="); 1036 sb.append(this.pCBit); 1037 sb.append("\n"); 1038 1039 byte[] bf = new byte[this.length]; 1040 System.arraycopy(this.buffer, this.start, bf, 0, this.length); 1041 sb.append(AsnOutputStream.arrayToString(bf)); 1042 1043 return sb.toString(); 1044 } 1045} 1046