1 /*
2 * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package sun.security.util;
27
28 import java.io.*;
29 import java.math.BigInteger;
30 import java.util.Date;
31 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 5280, section 8, we assume that this kind of string will contain
49 * 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 static final byte tag_Boolean = 0x01;
88
89 /** Tag value indicating an ASN.1 "INTEGER" value. */
90 public static final byte tag_Integer = 0x02;
91
92 /** Tag value indicating an ASN.1 "BIT STRING" value. */
93 public static final byte tag_BitString = 0x03;
94
95 /** Tag value indicating an ASN.1 "OCTET STRING" value. */
96 public static final byte tag_OctetString = 0x04;
97
98 /** Tag value indicating an ASN.1 "NULL" value. */
99 public static final byte tag_Null = 0x05;
100
101 /** Tag value indicating an ASN.1 "OBJECT IDENTIFIER" value. */
102 public static final byte tag_ObjectId = 0x06;
103
104 /** Tag value including an ASN.1 "ENUMERATED" value */
105 public static final byte tag_Enumerated = 0x0A;
106
107 /** Tag value indicating an ASN.1 "UTF8String" value. */
108 public static final byte tag_UTF8String = 0x0C;
109
110 /** Tag value including a "printable" string */
111 public static final byte tag_PrintableString = 0x13;
112
113 /** Tag value including a "teletype" string */
114 public static final byte tag_T61String = 0x14;
115
116 /** Tag value including an ASCII string */
117 public static final byte tag_IA5String = 0x16;
118
119 /** Tag value indicating an ASN.1 "UTCTime" value. */
120 public static final byte tag_UtcTime = 0x17;
121
122 /** Tag value indicating an ASN.1 "GeneralizedTime" value. */
123 public static final byte tag_GeneralizedTime = 0x18;
124
125 /** Tag value indicating an ASN.1 "GenerallString" value. */
126 public static final byte tag_GeneralString = 0x1B;
127
128 /** Tag value indicating an ASN.1 "UniversalString" value. */
129 public static final byte tag_UniversalString = 0x1C;
130
131 /** Tag value indicating an ASN.1 "BMPString" value. */
132 public static final 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 static final 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 static final 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 static final 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 static final 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.getDefiniteLength(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 len 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.getDefiniteLength(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 * @param 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 * @param 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 * Bitwise equality comparison. DER encoded values have a single
750 * encoding, so that bitwise equality of the encoded values is an
751 * efficient way to establish equivalence of the unencoded values.
752 *
753 * @param o the object being compared with this one
754 */
755 @Override
756 public boolean equals(Object o) {
757 if (this == o) {
758 return true;
759 }
760 if (!(o instanceof DerValue)) {
761 return false;
762 }
763 DerValue other = (DerValue) o;
764 if (tag != other.tag) {
765 return false;
766 }
767 if (data == other.data) {
768 return true;
769 }
770
771 // make sure the order of lock is always consistent to avoid a deadlock
772 return (System.identityHashCode(this.data)
773 > System.identityHashCode(other.data)) ?
774 doEquals(this, other):
775 doEquals(other, this);
776 }
777
778 /**
779 * Helper for public method equals()
780 */
781 private static boolean doEquals(DerValue d1, DerValue d2) {
782 synchronized (d1.data) {
783 synchronized (d2.data) {
784 d1.data.reset();
785 d2.data.reset();
786 return d1.buffer.equals(d2.buffer);
787 }
788 }
789 }
790
791 /**
792 * Returns a printable representation of the value.
793 *
794 * @return printable representation of the value
795 */
796 @Override
797 public String toString() {
798 try {
799
800 String str = getAsString();
801 if (str != null)
802 return "\"" + str + "\"";
803 if (tag == tag_Null)
804 return "[DerValue, null]";
805 if (tag == tag_ObjectId)
806 return "OID." + getOID();
807
808 // integers
809 else
810 return "[DerValue, tag = " + tag
811 + ", length = " + length + "]";
812 } catch (IOException e) {
813 throw new IllegalArgumentException("misformatted DER value");
814 }
815 }
816
817 /**
818 * Returns a DER-encoded value, such that if it's passed to the
819 * DerValue constructor, a value equivalent to "this" is returned.
820 *
821 * @return DER-encoded value, including tag and length.
822 */
823 public byte[] toByteArray() throws IOException {
824 DerOutputStream out = new DerOutputStream();
825
826 encode(out);
827 data.reset();
828 return out.toByteArray();
829 }
830
831 /**
832 * For "set" and "sequence" types, this function may be used
833 * to return a DER stream of the members of the set or sequence.
834 * This operation is not supported for primitive types such as
835 * integers or bit strings.
836 */
837 public DerInputStream toDerInputStream() throws IOException {
838 if (tag == tag_Sequence || tag == tag_Set)
839 return new DerInputStream(buffer);
840 throw new IOException("toDerInputStream rejects tag type " + tag);
841 }
842
843 /**
844 * Get the length of the encoded value.
845 */
846 public int length() {
847 return length;
848 }
849
850 /**
851 * Determine if a character is one of the permissible characters for
852 * PrintableString:
853 * A-Z, a-z, 0-9, space, apostrophe (39), left and right parentheses,
854 * plus sign, comma, hyphen, period, slash, colon, equals sign,
855 * and question mark.
856 *
857 * Characters that are *not* allowed in PrintableString include
858 * exclamation point, quotation mark, number sign, dollar sign,
859 * percent sign, ampersand, asterisk, semicolon, less than sign,
860 * greater than sign, at sign, left and right square brackets,
861 * backslash, circumflex (94), underscore, back quote (96),
862 * left and right curly brackets, vertical line, tilde,
863 * and the control codes (0-31 and 127).
864 *
865 * This list is based on X.680 (the ASN.1 spec).
866 */
867 public static boolean isPrintableStringChar(char ch) {
868 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
869 (ch >= '0' && ch <= '9')) {
870 return true;
871 } else {
872 switch (ch) {
873 case ' ': /* space */
874 case '\'': /* apostrophe */
875 case '(': /* left paren */
876 case ')': /* right paren */
877 case '+': /* plus */
878 case ',': /* comma */
879 case '-': /* hyphen */
880 case '.': /* period */
881 case '/': /* slash */
882 case ':': /* colon */
883 case '=': /* equals */
884 case '?': /* question mark */
885 return true;
886 default:
887 return false;
888 }
889 }
890 }
891
892 /**
893 * Create the tag of the attribute.
894 *
895 * @param tagClass the tag class type, one of UNIVERSAL, CONTEXT,
896 * APPLICATION or PRIVATE
897 * @param form if true, the value is constructed, otherwise it
898 * is primitive.
899 * @param val the tag value
900 */
901 public static byte createTag(byte tagClass, boolean form, byte val) {
902 byte tag = (byte)(tagClass | val);
903 if (form) {
904 tag |= (byte)0x20;
905 }
906 return (tag);
907 }
908
909 /**
910 * Set the tag of the attribute. Commonly used to reset the
911 * tag value used for IMPLICIT encodings.
912 *
913 * @param tag the tag value
914 */
915 public void resetTag(byte tag) {
916 this.tag = tag;
917 }
918
919 /**
920 * Returns a hashcode for this DerValue.
921 *
922 * @return a hashcode for this DerValue.
923 */
924 @Override
925 public int hashCode() {
926 return toString().hashCode();
927 }
928 }
--- EOF ---