001: /*
002: * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.security.util;
027:
028: import java.io.*;
029: import java.math.BigInteger;
030: import java.util.Date;
031:
032: /**
033: * Represents a single DER-encoded value. DER encoding rules are a subset
034: * of the "Basic" Encoding Rules (BER), but they only support a single way
035: * ("Definite" encoding) to encode any given value.
036: *
037: * <P>All DER-encoded data are triples <em>{type, length, data}</em>. This
038: * class represents such tagged values as they have been read (or constructed),
039: * and provides structured access to the encoded data.
040: *
041: * <P>At this time, this class supports only a subset of the types of DER
042: * data encodings which are defined. That subset is sufficient for parsing
043: * most X.509 certificates, and working with selected additional formats
044: * (such as PKCS #10 certificate requests, and some kinds of PKCS #7 data).
045: *
046: * A note with respect to T61/Teletex strings: From RFC 1617, section 4.1.3
047: * and RFC 3280, section 4.1.2.4., we assume that this kind of string will
048: * contain ISO-8859-1 characters only.
049: *
050: * @version 1.76, 05/05/07
051: *
052: * @author David Brownell
053: * @author Amit Kapoor
054: * @author Hemma Prafullchandra
055: */
056: public class DerValue {
057: /** The tag class types */
058: public static final byte TAG_UNIVERSAL = (byte) 0x000;
059: public static final byte TAG_APPLICATION = (byte) 0x040;
060: public static final byte TAG_CONTEXT = (byte) 0x080;
061: public static final byte TAG_PRIVATE = (byte) 0x0c0;
062:
063: /** The DER tag of the value; one of the tag_ constants. */
064: public byte tag;
065:
066: protected DerInputBuffer buffer;
067:
068: /**
069: * The DER-encoded data of the value.
070: */
071: public final DerInputStream data;
072:
073: private int length;
074:
075: /*
076: * The type starts at the first byte of the encoding, and
077: * is one of these tag_* values. That may be all the type
078: * data that is needed.
079: */
080:
081: /*
082: * These tags are the "universal" tags ... they mean the same
083: * in all contexts. (Mask with 0x1f -- five bits.)
084: */
085:
086: /** Tag value indicating an ASN.1 "BOOLEAN" value. */
087: public final static byte tag_Boolean = 0x01;
088:
089: /** Tag value indicating an ASN.1 "INTEGER" value. */
090: public final static byte tag_Integer = 0x02;
091:
092: /** Tag value indicating an ASN.1 "BIT STRING" value. */
093: public final static byte tag_BitString = 0x03;
094:
095: /** Tag value indicating an ASN.1 "OCTET STRING" value. */
096: public final static byte tag_OctetString = 0x04;
097:
098: /** Tag value indicating an ASN.1 "NULL" value. */
099: 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() {
168: return ((tag & 0x0c0) == 0x000);
169: }
170:
171: /**
172: * Returns true if the tag class is APPLICATION.
173: */
174: public boolean isApplication() {
175: return ((tag & 0x0c0) == 0x040);
176: }
177:
178: /**
179: * Returns true iff the CONTEXT SPECIFIC bit is set in the type tag.
180: * This is associated with the ASN.1 "DEFINED BY" syntax.
181: */
182: public boolean isContextSpecific() {
183: return ((tag & 0x0c0) == 0x080);
184: }
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() {
197: return ((tag & 0x0c0) == 0x0c0);
198: }
199:
200: /** Returns true iff the CONSTRUCTED bit is set in the type tag. */
201: public boolean isConstructed() {
202: return ((tag & 0x020) == 0x020);
203: }
204:
205: /**
206: * Returns true iff the CONSTRUCTED TAG matches the passed tag.
207: */
208: public boolean isConstructed(byte constructedTag) {
209: if (!isConstructed()) {
210: return false;
211: }
212: return ((tag & 0x01f) == constructedTag);
213: }
214:
215: /**
216: * Creates a PrintableString or UTF8string DER value from a string
217: */
218: public DerValue(String value) throws IOException {
219: boolean isPrintableString = true;
220: for (int i = 0; i < value.length(); i++) {
221: if (!isPrintableStringChar(value.charAt(i))) {
222: isPrintableString = false;
223: break;
224: }
225: }
226:
227: data = init(isPrintableString ? tag_PrintableString
228: : tag_UTF8String, value);
229: }
230:
231: /**
232: * Creates a string type DER value from a String object
233: * @param stringTag the tag for the DER value to create
234: * @param value the String object to use for the DER value
235: */
236: public DerValue(byte stringTag, String value) throws IOException {
237: data = init(stringTag, value);
238: }
239:
240: /**
241: * Creates a DerValue from a tag and some DER-encoded data.
242: *
243: * @param tag the DER type tag
244: * @param data the DER-encoded data
245: */
246: public DerValue(byte tag, byte[] data) {
247: this .tag = tag;
248: buffer = new DerInputBuffer(data.clone());
249: length = data.length;
250: this .data = new DerInputStream(buffer);
251: this .data.mark(Integer.MAX_VALUE);
252: }
253:
254: /*
255: * package private
256: */
257: DerValue(DerInputBuffer in) throws IOException {
258: // XXX must also parse BER-encoded constructed
259: // values such as sequences, sets...
260:
261: tag = (byte) in.read();
262: byte lenByte = (byte) in.read();
263: length = DerInputStream.getLength((lenByte & 0xff), in);
264: if (length == -1) { // indefinite length encoding found
265: DerInputBuffer inbuf = in.dup();
266: int readLen = inbuf.available();
267: int offset = 2; // for tag and length bytes
268: byte[] indefData = new byte[readLen + offset];
269: indefData[0] = tag;
270: indefData[1] = lenByte;
271: DataInputStream dis = new DataInputStream(inbuf);
272: dis.readFully(indefData, offset, readLen);
273: dis.close();
274: DerIndefLenConverter derIn = new DerIndefLenConverter();
275: inbuf = new DerInputBuffer(derIn.convert(indefData));
276: if (tag != inbuf.read())
277: throw new IOException(
278: "Indefinite length encoding not supported");
279: length = DerInputStream.getLength(inbuf);
280: buffer = inbuf.dup();
281: buffer.truncate(length);
282: data = new DerInputStream(buffer);
283: // indefinite form is encoded by sending a length field with a
284: // length of 0. - i.e. [1000|0000].
285: // the object is ended by sending two zero bytes.
286: in.skip(length + offset);
287: } else {
288:
289: buffer = in.dup();
290: buffer.truncate(length);
291: data = new DerInputStream(buffer);
292:
293: in.skip(length);
294: }
295: }
296:
297: /**
298: * Get an ASN.1/DER encoded datum from a buffer. The
299: * entire buffer must hold exactly one datum, including
300: * its tag and length.
301: *
302: * @param buf buffer holding a single DER-encoded datum.
303: */
304: public DerValue(byte[] buf) throws IOException {
305: data = init(true, new ByteArrayInputStream(buf));
306: }
307:
308: /**
309: * Get an ASN.1/DER encoded datum from part of a buffer.
310: * That part of the buffer must hold exactly one datum, including
311: * its tag and length.
312: *
313: * @param buf the buffer
314: * @param offset start point of the single DER-encoded dataum
315: * @param length how many bytes are in the encoded datum
316: */
317: public DerValue(byte[] buf, int offset, int len) throws IOException {
318: data = init(true, new ByteArrayInputStream(buf, offset, len));
319: }
320:
321: /**
322: * Get an ASN1/DER encoded datum from an input stream. The
323: * stream may have additional data following the encoded datum.
324: * In case of indefinite length encoded datum, the input stream
325: * must hold only one datum.
326: *
327: * @param in the input stream holding a single DER datum,
328: * which may be followed by additional data
329: */
330: public DerValue(InputStream in) throws IOException {
331: data = init(false, in);
332: }
333:
334: private DerInputStream init(byte stringTag, String value)
335: throws IOException {
336: String enc = null;
337:
338: tag = stringTag;
339:
340: switch (stringTag) {
341: case tag_PrintableString:
342: case tag_IA5String:
343: case tag_GeneralString:
344: enc = "ASCII";
345: break;
346: case tag_T61String:
347: enc = "ISO-8859-1";
348: break;
349: case tag_BMPString:
350: enc = "UnicodeBigUnmarked";
351: break;
352: case tag_UTF8String:
353: enc = "UTF8";
354: break;
355: // TBD: Need encoder for UniversalString before it can
356: // be handled.
357: default:
358: throw new IllegalArgumentException(
359: "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: if (length == 0)
396: return null;
397:
398: if (fullyBuffered && in.available() != length)
399: throw new IOException(
400: "extra data given to DerValue constructor");
401:
402: byte[] bytes = new byte[length];
403:
404: // n.b. readFully not needed in normal fullyBuffered case
405: DataInputStream dis = new DataInputStream(in);
406:
407: dis.readFully(bytes);
408: buffer = new DerInputBuffer(bytes);
409: return new DerInputStream(buffer);
410: }
411:
412: /**
413: * Encode an ASN1/DER encoded datum onto a DER output stream.
414: */
415: public void encode(DerOutputStream out) throws IOException {
416: out.write(tag);
417: out.putLength(length);
418: // XXX yeech, excess copies ... DerInputBuffer.write(OutStream)
419: if (length > 0) {
420: byte[] value = new byte[length];
421: // always synchronized on data
422: synchronized (data) {
423: buffer.reset();
424: if (buffer.read(value) != length) {
425: throw new IOException(
426: "short DER value read (encode)");
427: }
428: out.write(value);
429: }
430: }
431: }
432:
433: public final DerInputStream getData() {
434: return data;
435: }
436:
437: public final byte getTag() {
438: return tag;
439: }
440:
441: /**
442: * Returns an ASN.1 BOOLEAN
443: *
444: * @return the boolean held in this DER value
445: */
446: public boolean getBoolean() throws IOException {
447: if (tag != tag_Boolean) {
448: throw new IOException("DerValue.getBoolean, not a BOOLEAN "
449: + tag);
450: }
451: if (length != 1) {
452: throw new IOException(
453: "DerValue.getBoolean, invalid length " + length);
454: }
455: if (buffer.read() != 0) {
456: return true;
457: }
458: return false;
459: }
460:
461: /**
462: * Returns an ASN.1 OBJECT IDENTIFIER.
463: *
464: * @return the OID held in this DER value
465: */
466: public ObjectIdentifier getOID() throws IOException {
467: if (tag != tag_ObjectId)
468: throw new IOException("DerValue.getOID, not an OID " + tag);
469: return new ObjectIdentifier(buffer);
470: }
471:
472: private byte[] append(byte[] a, byte[] b) {
473: if (a == null)
474: return b;
475:
476: byte[] ret = new byte[a.length + b.length];
477: System.arraycopy(a, 0, ret, 0, a.length);
478: System.arraycopy(b, 0, ret, a.length, b.length);
479:
480: return ret;
481: }
482:
483: /**
484: * Returns an ASN.1 OCTET STRING
485: *
486: * @return the octet string held in this DER value
487: */
488: public byte[] getOctetString() throws IOException {
489: byte[] bytes;
490:
491: if (tag != tag_OctetString && !isConstructed(tag_OctetString)) {
492: throw new IOException(
493: "DerValue.getOctetString, not an Octet String: "
494: + tag);
495: }
496: bytes = new byte[length];
497: if (buffer.read(bytes) != length)
498: throw new IOException("short read on DerValue buffer");
499: if (isConstructed()) {
500: DerInputStream in = new DerInputStream(bytes);
501: bytes = null;
502: while (in.available() != 0) {
503: bytes = append(bytes, in.getOctetString());
504: }
505: }
506: return bytes;
507: }
508:
509: /**
510: * Returns an ASN.1 INTEGER value as an integer.
511: *
512: * @return the integer held in this DER value.
513: */
514: public int getInteger() throws IOException {
515: if (tag != tag_Integer) {
516: throw new IOException("DerValue.getInteger, not an int "
517: + tag);
518: }
519: return buffer.getInteger(data.available());
520: }
521:
522: /**
523: * Returns an ASN.1 INTEGER value as a BigInteger.
524: *
525: * @return the integer held in this DER value as a BigInteger.
526: */
527: public BigInteger getBigInteger() throws IOException {
528: if (tag != tag_Integer)
529: throw new IOException("DerValue.getBigInteger, not an int "
530: + tag);
531: return buffer.getBigInteger(data.available(), false);
532: }
533:
534: /**
535: * Returns an ASN.1 INTEGER value as a positive BigInteger.
536: * This is just to deal with implementations that incorrectly encode
537: * some values as negative.
538: *
539: * @return the integer held in this DER value as a BigInteger.
540: */
541: public BigInteger getPositiveBigInteger() throws IOException {
542: if (tag != tag_Integer)
543: throw new IOException("DerValue.getBigInteger, not an int "
544: + tag);
545: return buffer.getBigInteger(data.available(), true);
546: }
547:
548: /**
549: * Returns an ASN.1 ENUMERATED value.
550: *
551: * @return the integer held in this DER value.
552: */
553: public int getEnumerated() throws IOException {
554: if (tag != tag_Enumerated) {
555: throw new IOException(
556: "DerValue.getEnumerated, incorrect tag: " + tag);
557: }
558: return buffer.getInteger(data.available());
559: }
560:
561: /**
562: * Returns an ASN.1 BIT STRING value. The bit string must be byte-aligned.
563: *
564: * @return the bit string held in this value
565: */
566: public byte[] getBitString() throws IOException {
567: if (tag != tag_BitString)
568: throw new IOException(
569: "DerValue.getBitString, not a bit string " + tag);
570:
571: return buffer.getBitString();
572: }
573:
574: /**
575: * Returns an ASN.1 BIT STRING value that need not be byte-aligned.
576: *
577: * @return a BitArray representing the bit string held in this value
578: */
579: public BitArray getUnalignedBitString() throws IOException {
580: if (tag != tag_BitString)
581: throw new IOException(
582: "DerValue.getBitString, not a bit string " + tag);
583:
584: return buffer.getUnalignedBitString();
585: }
586:
587: /**
588: * Returns the name component as a Java string, regardless of its
589: * encoding restrictions (ASCII, T61, Printable, IA5, BMP, UTF8).
590: */
591: // TBD: Need encoder for UniversalString before it can be handled.
592: public String getAsString() throws IOException {
593: if (tag == tag_UTF8String)
594: return getUTF8String();
595: else if (tag == tag_PrintableString)
596: return getPrintableString();
597: else if (tag == tag_T61String)
598: return getT61String();
599: else if (tag == tag_IA5String)
600: return getIA5String();
601: /*
602: else if (tag == tag_UniversalString)
603: return getUniversalString();
604: */
605: else if (tag == tag_BMPString)
606: return getBMPString();
607: else if (tag == tag_GeneralString)
608: return getGeneralString();
609: else
610: return null;
611: }
612:
613: /**
614: * Returns an ASN.1 BIT STRING value, with the tag assumed implicit
615: * based on the parameter. The bit string must be byte-aligned.
616: *
617: * @params tagImplicit if true, the tag is assumed implicit.
618: * @return the bit string held in this value
619: */
620: public byte[] getBitString(boolean tagImplicit) throws IOException {
621: if (!tagImplicit) {
622: if (tag != tag_BitString)
623: throw new IOException(
624: "DerValue.getBitString, not a bit string "
625: + tag);
626: }
627: return buffer.getBitString();
628: }
629:
630: /**
631: * Returns an ASN.1 BIT STRING value, with the tag assumed implicit
632: * based on the parameter. The bit string need not be byte-aligned.
633: *
634: * @params tagImplicit if true, the tag is assumed implicit.
635: * @return the bit string held in this value
636: */
637: public BitArray getUnalignedBitString(boolean tagImplicit)
638: throws IOException {
639: if (!tagImplicit) {
640: if (tag != tag_BitString)
641: throw new IOException(
642: "DerValue.getBitString, not a bit string "
643: + tag);
644: }
645: return buffer.getUnalignedBitString();
646: }
647:
648: /**
649: * Helper routine to return all the bytes contained in the
650: * DerInputStream associated with this object.
651: */
652: public byte[] getDataBytes() throws IOException {
653: byte[] retVal = new byte[length];
654: synchronized (data) {
655: data.reset();
656: data.getBytes(retVal);
657: }
658: return retVal;
659: }
660:
661: /**
662: * Returns an ASN.1 STRING value
663: *
664: * @return the printable string held in this value
665: */
666: public String getPrintableString() throws IOException {
667: if (tag != tag_PrintableString)
668: throw new IOException(
669: "DerValue.getPrintableString, not a string " + tag);
670:
671: return new String(getDataBytes(), "ASCII");
672: }
673:
674: /**
675: * Returns an ASN.1 T61 (Teletype) STRING value
676: *
677: * @return the teletype string held in this value
678: */
679: public String getT61String() throws IOException {
680: if (tag != tag_T61String)
681: throw new IOException("DerValue.getT61String, not T61 "
682: + tag);
683:
684: return new String(getDataBytes(), "ISO-8859-1");
685: }
686:
687: /**
688: * Returns an ASN.1 IA5 (ASCII) STRING value
689: *
690: * @return the ASCII string held in this value
691: */
692: public String getIA5String() throws IOException {
693: if (tag != tag_IA5String)
694: throw new IOException("DerValue.getIA5String, not IA5 "
695: + tag);
696:
697: return new String(getDataBytes(), "ASCII");
698: }
699:
700: /**
701: * Returns the ASN.1 BMP (Unicode) STRING value as a Java string.
702: *
703: * @return a string corresponding to the encoded BMPString held in
704: * this value
705: */
706: public String getBMPString() throws IOException {
707: if (tag != tag_BMPString)
708: throw new IOException("DerValue.getBMPString, not BMP "
709: + tag);
710:
711: // BMPString is the same as Unicode in big endian, unmarked
712: // format.
713: return new String(getDataBytes(), "UnicodeBigUnmarked");
714: }
715:
716: /**
717: * Returns the ASN.1 UTF-8 STRING value as a Java String.
718: *
719: * @return a string corresponding to the encoded UTF8String held in
720: * this value
721: */
722: public String getUTF8String() throws IOException {
723: if (tag != tag_UTF8String)
724: throw new IOException("DerValue.getUTF8String, not UTF-8 "
725: + tag);
726:
727: return new String(getDataBytes(), "UTF8");
728: }
729:
730: /**
731: * Returns the ASN.1 GENERAL STRING value as a Java String.
732: *
733: * @return a string corresponding to the encoded GeneralString held in
734: * this value
735: */
736: public String getGeneralString() throws IOException {
737: if (tag != tag_GeneralString)
738: throw new IOException(
739: "DerValue.getGeneralString, not GeneralString "
740: + tag);
741:
742: return new String(getDataBytes(), "ASCII");
743: }
744:
745: /**
746: * Returns a Date if the DerValue is UtcTime.
747: *
748: * @return the Date held in this DER value
749: */
750: public Date getUTCTime() throws IOException {
751: if (tag != tag_UtcTime) {
752: throw new IOException(
753: "DerValue.getUTCTime, not a UtcTime: " + tag);
754: }
755: return buffer.getUTCTime(data.available());
756: }
757:
758: /**
759: * Returns a Date if the DerValue is GeneralizedTime.
760: *
761: * @return the Date held in this DER value
762: */
763: public Date getGeneralizedTime() throws IOException {
764: if (tag != tag_GeneralizedTime) {
765: throw new IOException(
766: "DerValue.getGeneralizedTime, not a GeneralizedTime: "
767: + tag);
768: }
769: return buffer.getGeneralizedTime(data.available());
770: }
771:
772: /**
773: * Returns true iff the other object is a DER value which
774: * is bitwise equal to this one.
775: *
776: * @param other the object being compared with this one
777: */
778: public boolean equals(Object other) {
779: if (other instanceof DerValue)
780: return equals((DerValue) other);
781: else
782: return false;
783: }
784:
785: /**
786: * Bitwise equality comparison. DER encoded values have a single
787: * encoding, so that bitwise equality of the encoded values is an
788: * efficient way to establish equivalence of the unencoded values.
789: *
790: * @param other the object being compared with this one
791: */
792: public boolean equals(DerValue other) {
793: if (this == other) {
794: return true;
795: }
796: if (tag != other.tag) {
797: return false;
798: }
799: if (data == other.data) {
800: return true;
801: }
802:
803: // make sure the order of lock is always consistent to avoid a deadlock
804: return (System.identityHashCode(this .data) > System
805: .identityHashCode(other.data)) ? doEquals(this , other)
806: : doEquals(other, this );
807: }
808:
809: /**
810: * Helper for public method equals()
811: */
812: private static boolean doEquals(DerValue d1, DerValue d2) {
813: synchronized (d1.data) {
814: synchronized (d2.data) {
815: d1.data.reset();
816: d2.data.reset();
817: return d1.buffer.equals(d2.buffer);
818: }
819: }
820: }
821:
822: /**
823: * Returns a printable representation of the value.
824: *
825: * @return printable representation of the value
826: */
827: public String toString() {
828: try {
829:
830: String str = getAsString();
831: if (str != null)
832: return "\"" + str + "\"";
833: if (tag == tag_Null)
834: return "[DerValue, null]";
835: if (tag == tag_ObjectId)
836: return "OID." + getOID();
837:
838: // integers
839: else
840: return "[DerValue, tag = " + tag + ", length = "
841: + length + "]";
842: } catch (IOException e) {
843: throw new IllegalArgumentException("misformatted DER value");
844: }
845: }
846:
847: /**
848: * Returns a DER-encoded value, such that if it's passed to the
849: * DerValue constructor, a value equivalent to "this" is returned.
850: *
851: * @return DER-encoded value, including tag and length.
852: */
853: public byte[] toByteArray() throws IOException {
854: DerOutputStream out = new DerOutputStream();
855:
856: encode(out);
857: data.reset();
858: return out.toByteArray();
859: }
860:
861: /**
862: * For "set" and "sequence" types, this function may be used
863: * to return a DER stream of the members of the set or sequence.
864: * This operation is not supported for primitive types such as
865: * integers or bit strings.
866: */
867: public DerInputStream toDerInputStream() throws IOException {
868: if (tag == tag_Sequence || tag == tag_Set)
869: return new DerInputStream(buffer);
870: throw new IOException("toDerInputStream rejects tag type "
871: + tag);
872: }
873:
874: /**
875: * Get the length of the encoded value.
876: */
877: public int length() {
878: return length;
879: }
880:
881: /**
882: * Determine if a character is one of the permissible characters for
883: * PrintableString:
884: * A-Z, a-z, 0-9, space, apostrophe (39), left and right parentheses,
885: * plus sign, comma, hyphen, period, slash, colon, equals sign,
886: * and question mark.
887: *
888: * Characters that are *not* allowed in PrintableString include
889: * exclamation point, quotation mark, number sign, dollar sign,
890: * percent sign, ampersand, asterisk, semicolon, less than sign,
891: * greater than sign, at sign, left and right square brackets,
892: * backslash, circumflex (94), underscore, back quote (96),
893: * left and right curly brackets, vertical line, tilde,
894: * and the control codes (0-31 and 127).
895: *
896: * This list is based on X.680 (the ASN.1 spec).
897: */
898: public static boolean isPrintableStringChar(char ch) {
899: if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
900: || (ch >= '0' && ch <= '9')) {
901: return true;
902: } else {
903: switch (ch) {
904: case ' ': /* space */
905: case '\'': /* apostrophe */
906: case '(': /* left paren */
907: case ')': /* right paren */
908: case '+': /* plus */
909: case ',': /* comma */
910: case '-': /* hyphen */
911: case '.': /* period */
912: case '/': /* slash */
913: case ':': /* colon */
914: case '=': /* equals */
915: case '?': /* question mark */
916: return true;
917: default:
918: return false;
919: }
920: }
921: }
922:
923: /**
924: * Create the tag of the attribute.
925: *
926: * @params class the tag class type, one of UNIVERSAL, CONTEXT,
927: * APPLICATION or PRIVATE
928: * @params form if true, the value is constructed, otherwise it
929: * is primitive.
930: * @params val the tag value
931: */
932: public static byte createTag(byte tagClass, boolean form, byte val) {
933: byte tag = (byte) (tagClass | val);
934: if (form) {
935: tag |= (byte) 0x20;
936: }
937: return (tag);
938: }
939:
940: /**
941: * Set the tag of the attribute. Commonly used to reset the
942: * tag value used for IMPLICIT encodings.
943: *
944: * @params tag the tag value
945: */
946: public void resetTag(byte tag) {
947: this .tag = tag;
948: }
949:
950: /**
951: * Returns a hashcode for this DerValue.
952: *
953: * @return a hashcode for this DerValue.
954: */
955: public int hashCode() {
956: return toString().hashCode();
957: }
958: }
|