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
26package sun.security.util;
27
28import java.io.*;
29import java.math.BigInteger;
30import java.util.Date;
31import 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 */
56public 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