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