1 /*
   2  * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.util;
  27 
  28 import java.io.*;
  29 import java.math.BigInteger;
  30 import java.util.Date;

  31 
  32 /**
  33  * Represents a single DER-encoded value.  DER encoding rules are a subset
  34  * of the "Basic" Encoding Rules (BER), but they only support a single way
  35  * ("Definite" encoding) to encode any given value.
  36  *
  37  * <P>All DER-encoded data are triples <em>{type, length, data}</em>.  This
  38  * class represents such tagged values as they have been read (or constructed),
  39  * and provides structured access to the encoded data.
  40  *
  41  * <P>At this time, this class supports only a subset of the types of DER
  42  * data encodings which are defined.  That subset is sufficient for parsing
  43  * most X.509 certificates, and working with selected additional formats
  44  * (such as PKCS #10 certificate requests, and some kinds of PKCS #7 data).
  45  *
  46  * A note with respect to T61/Teletex strings: From RFC 1617, section 4.1.3
  47  * and RFC 5280, section 8, we assume that this kind of string will contain
  48  * ISO-8859-1 characters only.
  49  *
  50  *
  51  * @author David Brownell
  52  * @author Amit Kapoor
  53  * @author Hemma Prafullchandra
  54  */
  55 public class DerValue {
  56     /** The tag class types */
  57     public static final byte TAG_UNIVERSAL = (byte)0x000;
  58     public static final byte TAG_APPLICATION = (byte)0x040;
  59     public static final byte TAG_CONTEXT = (byte)0x080;
  60     public static final byte TAG_PRIVATE = (byte)0x0c0;
  61 
  62     /** The DER tag of the value; one of the tag_ constants. */
  63     public byte                 tag;
  64 
  65     protected DerInputBuffer    buffer;
  66 
  67     /**
  68      * The DER-encoded data of the value, never null
  69      */
  70     public final DerInputStream data;
  71 
  72     private int                 length;
  73 
  74     /*
  75      * The type starts at the first byte of the encoding, and
  76      * is one of these tag_* values.  That may be all the type
  77      * data that is needed.
  78      */
  79 
  80     /*
  81      * These tags are the "universal" tags ... they mean the same
  82      * in all contexts.  (Mask with 0x1f -- five bits.)
  83      */
  84 
  85     /** Tag value indicating an ASN.1 "BOOLEAN" value. */
  86     public static final byte    tag_Boolean = 0x01;
  87 
  88     /** Tag value indicating an ASN.1 "INTEGER" value. */
  89     public static final byte    tag_Integer = 0x02;
  90 
  91     /** Tag value indicating an ASN.1 "BIT STRING" value. */
  92     public static final byte    tag_BitString = 0x03;
  93 
  94     /** Tag value indicating an ASN.1 "OCTET STRING" value. */
  95     public static final byte    tag_OctetString = 0x04;
  96 
  97     /** Tag value indicating an ASN.1 "NULL" value. */
  98     public static final byte    tag_Null = 0x05;
  99 
 100     /** Tag value indicating an ASN.1 "OBJECT IDENTIFIER" value. */
 101     public static final byte    tag_ObjectId = 0x06;
 102 
 103     /** Tag value including an ASN.1 "ENUMERATED" value */
 104     public static final byte    tag_Enumerated = 0x0A;
 105 
 106     /** Tag value indicating an ASN.1 "UTF8String" value. */
 107     public static final byte    tag_UTF8String = 0x0C;
 108 
 109     /** Tag value including a "printable" string */
 110     public static final byte    tag_PrintableString = 0x13;
 111 
 112     /** Tag value including a "teletype" string */
 113     public static final byte    tag_T61String = 0x14;
 114 
 115     /** Tag value including an ASCII string */
 116     public static final byte    tag_IA5String = 0x16;
 117 
 118     /** Tag value indicating an ASN.1 "UTCTime" value. */
 119     public static final byte    tag_UtcTime = 0x17;
 120 
 121     /** Tag value indicating an ASN.1 "GeneralizedTime" value. */
 122     public static final byte    tag_GeneralizedTime = 0x18;
 123 
 124     /** Tag value indicating an ASN.1 "GenerallString" value. */
 125     public static final byte    tag_GeneralString = 0x1B;
 126 
 127     /** Tag value indicating an ASN.1 "UniversalString" value. */
 128     public static final byte    tag_UniversalString = 0x1C;
 129 
 130     /** Tag value indicating an ASN.1 "BMPString" value. */
 131     public static final byte    tag_BMPString = 0x1E;
 132 
 133     // CONSTRUCTED seq/set
 134 
 135     /**
 136      * Tag value indicating an ASN.1
 137      * "SEQUENCE" (zero to N elements, order is significant).
 138      */
 139     public static final byte    tag_Sequence = 0x30;
 140 
 141     /**
 142      * Tag value indicating an ASN.1
 143      * "SEQUENCE OF" (one to N elements, order is significant).
 144      */
 145     public static final byte    tag_SequenceOf = 0x30;
 146 
 147     /**
 148      * Tag value indicating an ASN.1
 149      * "SET" (zero to N members, order does not matter).
 150      */
 151     public static final byte    tag_Set = 0x31;
 152 
 153     /**
 154      * Tag value indicating an ASN.1
 155      * "SET OF" (one to N members, order does not matter).
 156      */
 157     public static final byte    tag_SetOf = 0x31;
 158 
 159     /*
 160      * These values are the high order bits for the other kinds of tags.
 161      */
 162 
 163     /**
 164      * Returns true if the tag class is UNIVERSAL.
 165      */
 166     public boolean isUniversal()      { return ((tag & 0x0c0) == 0x000); }
 167 
 168     /**
 169      * Returns true if the tag class is APPLICATION.
 170      */
 171     public boolean isApplication()    { return ((tag & 0x0c0) == 0x040); }
 172 
 173     /**
 174      * Returns true iff the CONTEXT SPECIFIC bit is set in the type tag.
 175      * This is associated with the ASN.1 "DEFINED BY" syntax.
 176      */
 177     public boolean isContextSpecific() { return ((tag & 0x0c0) == 0x080); }
 178 
 179     /**
 180      * Returns true iff the CONTEXT SPECIFIC TAG matches the passed tag.
 181      */
 182     public boolean isContextSpecific(byte cntxtTag) {
 183         if (!isContextSpecific()) {
 184             return false;
 185         }
 186         return ((tag & 0x01f) == cntxtTag);
 187     }
 188 
 189     boolean isPrivate()        { return ((tag & 0x0c0) == 0x0c0); }
 190 
 191     /** Returns true iff the CONSTRUCTED bit is set in the type tag. */
 192     public boolean isConstructed()    { return ((tag & 0x020) == 0x020); }
 193 
 194     /**
 195      * Returns true iff the CONSTRUCTED TAG matches the passed tag.
 196      */
 197     public boolean isConstructed(byte constructedTag) {
 198         if (!isConstructed()) {
 199             return false;
 200         }
 201         return ((tag & 0x01f) == constructedTag);
 202     }
 203 
 204     /**
 205      * Creates a PrintableString or UTF8string DER value from a string
 206      */
 207     public DerValue(String value) throws IOException {
 208         boolean isPrintableString = true;
 209         for (int i = 0; i < value.length(); i++) {
 210             if (!isPrintableStringChar(value.charAt(i))) {
 211                 isPrintableString = false;
 212                 break;
 213             }
 214         }
 215 
 216         data = init(isPrintableString ? tag_PrintableString : tag_UTF8String, value);
 217     }
 218 
 219     /**
 220      * Creates a string type DER value from a String object
 221      * @param stringTag the tag for the DER value to create
 222      * @param value the String object to use for the DER value
 223      */
 224     public DerValue(byte stringTag, String value) throws IOException {
 225         data = init(stringTag, value);
 226     }
 227 
 228     /**
 229      * Creates a DerValue from a tag and some DER-encoded data.
 230      *
 231      * @param tag the DER type tag
 232      * @param data the DER-encoded data
 233      */
 234     public DerValue(byte tag, byte[] data) {
 235         this.tag = tag;
 236         buffer = new DerInputBuffer(data.clone());
 237         length = data.length;
 238         this.data = new DerInputStream(buffer);
 239         this.data.mark(Integer.MAX_VALUE);
 240     }
 241 
 242     /*
 243      * package private
 244      */
 245     DerValue(DerInputBuffer in) throws IOException {
 246         // XXX must also parse BER-encoded constructed
 247         // values such as sequences, sets...
 248 
 249         tag = (byte)in.read();
 250         byte lenByte = (byte)in.read();
 251         length = DerInputStream.getLength((lenByte & 0xff), in);
 252         if (length == -1) {  // indefinite length encoding found
 253             DerInputBuffer inbuf = in.dup();
 254             int readLen = inbuf.available();
 255             int offset = 2;     // for tag and length bytes
 256             byte[] indefData = new byte[readLen + offset];
 257             indefData[0] = tag;
 258             indefData[1] = lenByte;
 259             DataInputStream dis = new DataInputStream(inbuf);
 260             dis.readFully(indefData, offset, readLen);
 261             dis.close();
 262             DerIndefLenConverter derIn = new DerIndefLenConverter();
 263             inbuf = new DerInputBuffer(derIn.convert(indefData));
 264             if (tag != inbuf.read())
 265                 throw new IOException
 266                         ("Indefinite length encoding not supported");
 267             length = DerInputStream.getDefiniteLength(inbuf);
 268             buffer = inbuf.dup();
 269             buffer.truncate(length);
 270             data = new DerInputStream(buffer);
 271             // indefinite form is encoded by sending a length field with a
 272             // length of 0. - i.e. [1000|0000].
 273             // the object is ended by sending two zero bytes.
 274             in.skip(length + offset);
 275         } else {
 276 
 277             buffer = in.dup();
 278             buffer.truncate(length);
 279             data = new DerInputStream(buffer);
 280 
 281             in.skip(length);
 282         }
 283     }
 284 
 285     /**
 286      * Get an ASN.1/DER encoded datum from a buffer.  The
 287      * entire buffer must hold exactly one datum, including
 288      * its tag and length.
 289      *
 290      * @param buf buffer holding a single DER-encoded datum.
 291      */
 292     public DerValue(byte[] buf) throws IOException {
 293         data = init(true, new ByteArrayInputStream(buf));
 294     }
 295 
 296     /**
 297      * Get an ASN.1/DER encoded datum from part of a buffer.
 298      * That part of the buffer must hold exactly one datum, including
 299      * its tag and length.
 300      *
 301      * @param buf the buffer
 302      * @param offset start point of the single DER-encoded dataum
 303      * @param len how many bytes are in the encoded datum
 304      */
 305     public DerValue(byte[] buf, int offset, int len) throws IOException {
 306         data = init(true, new ByteArrayInputStream(buf, offset, len));
 307     }
 308 
 309     /**
 310      * Get an ASN1/DER encoded datum from an input stream.  The
 311      * stream may have additional data following the encoded datum.
 312      * In case of indefinite length encoded datum, the input stream
 313      * must hold only one datum.
 314      *
 315      * @param in the input stream holding a single DER datum,
 316      *  which may be followed by additional data
 317      */
 318     public DerValue(InputStream in) throws IOException {
 319         data = init(false, in);
 320     }
 321 
 322     private DerInputStream init(byte stringTag, String value) throws IOException {
 323         String enc = null;
 324 
 325         tag = stringTag;
 326 
 327         switch (stringTag) {
 328         case tag_PrintableString:
 329         case tag_IA5String:
 330         case tag_GeneralString:
 331             enc = "ASCII";
 332             break;
 333         case tag_T61String:
 334             enc = "ISO-8859-1";
 335             break;
 336         case tag_BMPString:
 337             enc = "UnicodeBigUnmarked";
 338             break;
 339         case tag_UTF8String:
 340             enc = "UTF8";
 341             break;
 342             // TBD: Need encoder for UniversalString before it can
 343             // be handled.
 344         default:
 345             throw new IllegalArgumentException("Unsupported DER string type");
 346         }
 347 
 348         byte[] buf = value.getBytes(enc);
 349         length = buf.length;
 350         buffer = new DerInputBuffer(buf);
 351         DerInputStream result = new DerInputStream(buffer);
 352         result.mark(Integer.MAX_VALUE);
 353         return result;
 354     }
 355 
 356     /*
 357      * helper routine
 358      */
 359     private DerInputStream init(boolean fullyBuffered, InputStream in)
 360             throws IOException {
 361 
 362         tag = (byte)in.read();
 363         byte lenByte = (byte)in.read();
 364         length = DerInputStream.getLength((lenByte & 0xff), in);
 365         if (length == -1) { // indefinite length encoding found
 366             int readLen = in.available();
 367             int offset = 2;     // for tag and length bytes
 368             byte[] indefData = new byte[readLen + offset];
 369             indefData[0] = tag;
 370             indefData[1] = lenByte;
 371             DataInputStream dis = new DataInputStream(in);
 372             dis.readFully(indefData, offset, readLen);
 373             dis.close();
 374             DerIndefLenConverter derIn = new DerIndefLenConverter();
 375             in = new ByteArrayInputStream(derIn.convert(indefData));
 376             if (tag != in.read())
 377                 throw new IOException
 378                         ("Indefinite length encoding not supported");
 379             length = DerInputStream.getDefiniteLength(in);
 380         }
 381 
 382         if (fullyBuffered && in.available() != length)
 383             throw new IOException("extra data given to DerValue constructor");
 384 
 385         byte[] bytes = IOUtils.readFully(in, length, true);
 386 
 387         buffer = new DerInputBuffer(bytes);
 388         return new DerInputStream(buffer);
 389     }
 390 
 391     /**
 392      * Encode an ASN1/DER encoded datum onto a DER output stream.
 393      */
 394     public void encode(DerOutputStream out)
 395     throws IOException {
 396         out.write(tag);
 397         out.putLength(length);
 398         // XXX yeech, excess copies ... DerInputBuffer.write(OutStream)
 399         if (length > 0) {
 400             byte[] value = new byte[length];
 401             // always synchronized on data
 402             synchronized (data) {
 403                 buffer.reset();
 404                 if (buffer.read(value) != length) {
 405                     throw new IOException("short DER value read (encode)");
 406                 }
 407                 out.write(value);
 408             }
 409         }
 410     }
 411 
 412     public final DerInputStream getData() {
 413         return data;
 414     }
 415 
 416     public final byte getTag() {
 417         return tag;
 418     }
 419 
 420     /**
 421      * Returns an ASN.1 BOOLEAN
 422      *
 423      * @return the boolean held in this DER value
 424      */
 425     public boolean getBoolean() throws IOException {
 426         if (tag != tag_Boolean) {
 427             throw new IOException("DerValue.getBoolean, not a BOOLEAN " + tag);
 428         }
 429         if (length != 1) {
 430             throw new IOException("DerValue.getBoolean, invalid length "
 431                                         + length);
 432         }
 433         if (buffer.read() != 0) {
 434             return true;
 435         }
 436         return false;
 437     }
 438 
 439     /**
 440      * Returns an ASN.1 OBJECT IDENTIFIER.
 441      *
 442      * @return the OID held in this DER value
 443      */
 444     public ObjectIdentifier getOID() throws IOException {
 445         if (tag != tag_ObjectId)
 446             throw new IOException("DerValue.getOID, not an OID " + tag);
 447         return new ObjectIdentifier(buffer);
 448     }
 449 
 450     private byte[] append(byte[] a, byte[] b) {
 451         if (a == null)
 452             return b;
 453 
 454         byte[] ret = new byte[a.length + b.length];
 455         System.arraycopy(a, 0, ret, 0, a.length);
 456         System.arraycopy(b, 0, ret, a.length, b.length);
 457 
 458         return ret;
 459     }
 460 
 461     /**
 462      * Returns an ASN.1 OCTET STRING
 463      *
 464      * @return the octet string held in this DER value
 465      */
 466     public byte[] getOctetString() throws IOException {
 467         byte[] bytes;
 468 
 469         if (tag != tag_OctetString && !isConstructed(tag_OctetString)) {
 470             throw new IOException(
 471                 "DerValue.getOctetString, not an Octet String: " + tag);
 472         }
 473         bytes = new byte[length];
 474         // Note: do not tempt to call buffer.read(bytes) at all. There's a
 475         // known bug that it returns -1 instead of 0.
 476         if (length == 0) {
 477             return bytes;
 478         }
 479         if (buffer.read(bytes) != length)
 480             throw new IOException("short read on DerValue buffer");
 481         if (isConstructed()) {
 482             DerInputStream in = new DerInputStream(bytes);
 483             bytes = null;
 484             while (in.available() != 0) {
 485                 bytes = append(bytes, in.getOctetString());
 486             }
 487         }
 488         return bytes;
 489     }
 490 
 491     /**
 492      * Returns an ASN.1 INTEGER value as an integer.
 493      *
 494      * @return the integer held in this DER value.
 495      */
 496     public int getInteger() throws IOException {
 497         if (tag != tag_Integer) {
 498             throw new IOException("DerValue.getInteger, not an int " + tag);
 499         }
 500         return buffer.getInteger(data.available());
 501     }
 502 
 503     /**
 504      * Returns an ASN.1 INTEGER value as a BigInteger.
 505      *
 506      * @return the integer held in this DER value as a BigInteger.
 507      */
 508     public BigInteger getBigInteger() throws IOException {
 509         if (tag != tag_Integer)
 510             throw new IOException("DerValue.getBigInteger, not an int " + tag);
 511         return buffer.getBigInteger(data.available(), false);
 512     }
 513 
 514     /**
 515      * Returns an ASN.1 INTEGER value as a positive BigInteger.
 516      * This is just to deal with implementations that incorrectly encode
 517      * some values as negative.
 518      *
 519      * @return the integer held in this DER value as a BigInteger.
 520      */
 521     public BigInteger getPositiveBigInteger() throws IOException {
 522         if (tag != tag_Integer)
 523             throw new IOException("DerValue.getBigInteger, not an int " + tag);
 524         return buffer.getBigInteger(data.available(), true);
 525     }
 526 
 527     /**
 528      * Returns an ASN.1 ENUMERATED value.
 529      *
 530      * @return the integer held in this DER value.
 531      */
 532     public int getEnumerated() throws IOException {
 533         if (tag != tag_Enumerated) {
 534             throw new IOException("DerValue.getEnumerated, incorrect tag: "
 535                                   + tag);
 536         }
 537         return buffer.getInteger(data.available());
 538     }
 539 
 540     /**
 541      * Returns an ASN.1 BIT STRING value.  The bit string must be byte-aligned.
 542      *
 543      * @return the bit string held in this value
 544      */
 545     public byte[] getBitString() throws IOException {
 546         if (tag != tag_BitString)
 547             throw new IOException(
 548                 "DerValue.getBitString, not a bit string " + tag);
 549 
 550         return buffer.getBitString();
 551     }
 552 
 553     /**
 554      * Returns an ASN.1 BIT STRING value that need not be byte-aligned.
 555      *
 556      * @return a BitArray representing the bit string held in this value
 557      */
 558     public BitArray getUnalignedBitString() throws IOException {
 559         if (tag != tag_BitString)
 560             throw new IOException(
 561                 "DerValue.getBitString, not a bit string " + tag);
 562 
 563         return buffer.getUnalignedBitString();
 564     }
 565 
 566     /**
 567      * Returns the name component as a Java string, regardless of its
 568      * encoding restrictions (ASCII, T61, Printable, IA5, BMP, UTF8).
 569      */
 570     // TBD: Need encoder for UniversalString before it can be handled.
 571     public String getAsString() throws IOException {
 572         if (tag == tag_UTF8String)
 573             return getUTF8String();
 574         else if (tag == tag_PrintableString)
 575             return getPrintableString();
 576         else if (tag == tag_T61String)
 577             return getT61String();
 578         else if (tag == tag_IA5String)
 579             return getIA5String();
 580         /*
 581           else if (tag == tag_UniversalString)
 582           return getUniversalString();
 583         */
 584         else if (tag == tag_BMPString)
 585             return getBMPString();
 586         else if (tag == tag_GeneralString)
 587             return getGeneralString();
 588         else
 589             return null;
 590     }
 591 
 592     /**
 593      * Returns an ASN.1 BIT STRING value, with the tag assumed implicit
 594      * based on the parameter.  The bit string must be byte-aligned.
 595      *
 596      * @param tagImplicit if true, the tag is assumed implicit.
 597      * @return the bit string held in this value
 598      */
 599     public byte[] getBitString(boolean tagImplicit) throws IOException {
 600         if (!tagImplicit) {
 601             if (tag != tag_BitString)
 602                 throw new IOException("DerValue.getBitString, not a bit string "
 603                                        + tag);
 604             }
 605         return buffer.getBitString();
 606     }
 607 
 608     /**
 609      * Returns an ASN.1 BIT STRING value, with the tag assumed implicit
 610      * based on the parameter.  The bit string need not be byte-aligned.
 611      *
 612      * @param tagImplicit if true, the tag is assumed implicit.
 613      * @return the bit string held in this value
 614      */
 615     public BitArray getUnalignedBitString(boolean tagImplicit)
 616     throws IOException {
 617         if (!tagImplicit) {
 618             if (tag != tag_BitString)
 619                 throw new IOException("DerValue.getBitString, not a bit string "
 620                                        + tag);
 621             }
 622         return buffer.getUnalignedBitString();
 623     }
 624 
 625     /**
 626      * Helper routine to return all the bytes contained in the
 627      * DerInputStream associated with this object.
 628      */
 629     public byte[] getDataBytes() throws IOException {
 630         byte[] retVal = new byte[length];
 631         synchronized (data) {
 632             data.reset();
 633             data.getBytes(retVal);
 634         }
 635         return retVal;
 636     }
 637 
 638     /**
 639      * Returns an ASN.1 STRING value
 640      *
 641      * @return the printable string held in this value
 642      */
 643     public String getPrintableString()
 644     throws IOException {
 645         if (tag != tag_PrintableString)
 646             throw new IOException(
 647                 "DerValue.getPrintableString, not a string " + tag);
 648 
 649         return new String(getDataBytes(), "ASCII");
 650     }
 651 
 652     /**
 653      * Returns an ASN.1 T61 (Teletype) STRING value
 654      *
 655      * @return the teletype string held in this value
 656      */
 657     public String getT61String() throws IOException {
 658         if (tag != tag_T61String)
 659             throw new IOException(
 660                 "DerValue.getT61String, not T61 " + tag);
 661 
 662         return new String(getDataBytes(), "ISO-8859-1");
 663     }
 664 
 665     /**
 666      * Returns an ASN.1 IA5 (ASCII) STRING value
 667      *
 668      * @return the ASCII string held in this value
 669      */
 670     public String getIA5String() throws IOException {
 671         if (tag != tag_IA5String)
 672             throw new IOException(
 673                 "DerValue.getIA5String, not IA5 " + tag);
 674 
 675         return new String(getDataBytes(), "ASCII");
 676     }
 677 
 678     /**
 679      * Returns the ASN.1 BMP (Unicode) STRING value as a Java string.
 680      *
 681      * @return a string corresponding to the encoded BMPString held in
 682      * this value
 683      */
 684     public String getBMPString() throws IOException {
 685         if (tag != tag_BMPString)
 686             throw new IOException(
 687                 "DerValue.getBMPString, not BMP " + tag);
 688 
 689         // BMPString is the same as Unicode in big endian, unmarked
 690         // format.
 691         return new String(getDataBytes(), "UnicodeBigUnmarked");
 692     }
 693 
 694     /**
 695      * Returns the ASN.1 UTF-8 STRING value as a Java String.
 696      *
 697      * @return a string corresponding to the encoded UTF8String held in
 698      * this value
 699      */
 700     public String getUTF8String() throws IOException {
 701         if (tag != tag_UTF8String)
 702             throw new IOException(
 703                 "DerValue.getUTF8String, not UTF-8 " + tag);
 704 
 705         return new String(getDataBytes(), "UTF8");
 706     }
 707 
 708     /**
 709      * Returns the ASN.1 GENERAL STRING value as a Java String.
 710      *
 711      * @return a string corresponding to the encoded GeneralString held in
 712      * this value
 713      */
 714     public String getGeneralString() throws IOException {
 715         if (tag != tag_GeneralString)
 716             throw new IOException(
 717                 "DerValue.getGeneralString, not GeneralString " + tag);
 718 
 719         return new String(getDataBytes(), "ASCII");
 720     }
 721 
 722     /**
 723      * Returns a Date if the DerValue is UtcTime.
 724      *
 725      * @return the Date held in this DER value
 726      */
 727     public Date getUTCTime() throws IOException {
 728         if (tag != tag_UtcTime) {
 729             throw new IOException("DerValue.getUTCTime, not a UtcTime: " + tag);
 730         }
 731         return buffer.getUTCTime(data.available());
 732     }
 733 
 734     /**
 735      * Returns a Date if the DerValue is GeneralizedTime.
 736      *
 737      * @return the Date held in this DER value
 738      */
 739     public Date getGeneralizedTime() throws IOException {
 740         if (tag != tag_GeneralizedTime) {
 741             throw new IOException(
 742                 "DerValue.getGeneralizedTime, not a GeneralizedTime: " + tag);
 743         }
 744         return buffer.getGeneralizedTime(data.available());
 745     }
 746 
 747     /**
 748      * Bitwise equality comparison.  DER encoded values have a single
 749      * encoding, so that bitwise equality of the encoded values is an
 750      * efficient way to establish equivalence of the unencoded values.
 751      *
 752      * @param o the object being compared with this one
 753      */
 754     @Override
 755     public boolean equals(Object o) {
 756         if (this == o) {
 757             return true;
 758         }
 759         if (!(o instanceof DerValue)) {
 760             return false;
 761         }
 762         DerValue other = (DerValue) o;
 763         if (tag != other.tag) {
 764             return false;
 765         }
 766         if (data == other.data) {
 767             return true;
 768         }
 769 
 770         // make sure the order of lock is always consistent to avoid a deadlock
 771         return (System.identityHashCode(this.data)
 772                 > System.identityHashCode(other.data)) ?
 773                 doEquals(this, other):
 774                 doEquals(other, this);
 775     }
 776 
 777     /**
 778      * Helper for public method equals()
 779      */
 780     private static boolean doEquals(DerValue d1, DerValue d2) {
 781         synchronized (d1.data) {
 782             synchronized (d2.data) {
 783                 d1.data.reset();
 784                 d2.data.reset();
 785                 return d1.buffer.equals(d2.buffer);
 786             }
 787         }
 788     }
 789 
 790     /**
 791      * Returns a printable representation of the value.
 792      *
 793      * @return printable representation of the value
 794      */
 795     @Override
 796     public String toString() {
 797         try {
 798 
 799             String str = getAsString();
 800             if (str != null)
 801                 return "\"" + str + "\"";
 802             if (tag == tag_Null)
 803                 return "[DerValue, null]";
 804             if (tag == tag_ObjectId)
 805                 return "OID." + getOID();
 806 
 807             // integers
 808             else
 809                 return "[DerValue, tag = " + tag
 810                         + ", length = " + length + "]";
 811         } catch (IOException e) {
 812             throw new IllegalArgumentException("misformatted DER value");
 813         }
 814     }
 815 
 816     /**
 817      * Returns a DER-encoded value, such that if it's passed to the
 818      * DerValue constructor, a value equivalent to "this" is returned.
 819      *
 820      * @return DER-encoded value, including tag and length.
 821      */
 822     public byte[] toByteArray() throws IOException {
 823         DerOutputStream out = new DerOutputStream();
 824 
 825         encode(out);
 826         data.reset();
 827         return out.toByteArray();
 828     }
 829 
 830     /**
 831      * For "set" and "sequence" types, this function may be used
 832      * to return a DER stream of the members of the set or sequence.
 833      * This operation is not supported for primitive types such as
 834      * integers or bit strings.
 835      */
 836     public DerInputStream toDerInputStream() throws IOException {
 837         if (tag == tag_Sequence || tag == tag_Set)
 838             return new DerInputStream(buffer);
 839         throw new IOException("toDerInputStream rejects tag type " + tag);
 840     }
 841 
 842     /**
 843      * Get the length of the encoded value.
 844      */
 845     public int length() {
 846         return length;
 847     }
 848 
 849     /**
 850      * Determine if a character is one of the permissible characters for
 851      * PrintableString:
 852      * A-Z, a-z, 0-9, space, apostrophe (39), left and right parentheses,
 853      * plus sign, comma, hyphen, period, slash, colon, equals sign,
 854      * and question mark.
 855      *
 856      * Characters that are *not* allowed in PrintableString include
 857      * exclamation point, quotation mark, number sign, dollar sign,
 858      * percent sign, ampersand, asterisk, semicolon, less than sign,
 859      * greater than sign, at sign, left and right square brackets,
 860      * backslash, circumflex (94), underscore, back quote (96),
 861      * left and right curly brackets, vertical line, tilde,
 862      * and the control codes (0-31 and 127).
 863      *
 864      * This list is based on X.680 (the ASN.1 spec).
 865      */
 866     public static boolean isPrintableStringChar(char ch) {
 867         if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
 868             (ch >= '0' && ch <= '9')) {
 869             return true;
 870         } else {
 871             switch (ch) {
 872                 case ' ':       /* space */
 873                 case '\'':      /* apostrophe */
 874                 case '(':       /* left paren */
 875                 case ')':       /* right paren */
 876                 case '+':       /* plus */
 877                 case ',':       /* comma */
 878                 case '-':       /* hyphen */
 879                 case '.':       /* period */
 880                 case '/':       /* slash */
 881                 case ':':       /* colon */
 882                 case '=':       /* equals */
 883                 case '?':       /* question mark */
 884                     return true;
 885                 default:
 886                     return false;
 887             }
 888         }
 889     }
 890 
 891     /**
 892      * Create the tag of the attribute.
 893      *
 894      * @param tagClass the tag class type, one of UNIVERSAL, CONTEXT,
 895      *               APPLICATION or PRIVATE
 896      * @param form if true, the value is constructed, otherwise it
 897      * is primitive.
 898      * @param val the tag value
 899      */
 900     public static byte createTag(byte tagClass, boolean form, byte val) {
 901         byte tag = (byte)(tagClass | val);
 902         if (form) {
 903             tag |= (byte)0x20;
 904         }
 905         return (tag);
 906     }
 907 
 908     /**
 909      * Set the tag of the attribute. Commonly used to reset the
 910      * tag value used for IMPLICIT encodings.
 911      *
 912      * @param tag the tag value
 913      */
 914     public void resetTag(byte tag) {
 915         this.tag = tag;
 916     }
 917 
 918     /**
 919      * Returns a hashcode for this DerValue.
 920      *
 921      * @return a hashcode for this DerValue.
 922      */
 923     @Override
 924     public int hashCode() {
 925         return toString().hashCode();
 926     }
 927 }
--- EOF ---