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