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