001: /*
002: *
003: *
004: * Copyright 1990-2007 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: package com.sun.satsa.util;
028:
029: import java.io.PrintStream;
030: import java.io.UnsupportedEncodingException;
031: import java.util.Calendar;
032: import java.util.TimeZone;
033:
034: /**
035: * Used to represent each Type, Length, Value structure in a DER buffer.
036: */
037: public class TLV {
038:
039: /*
040: * This class is used to parse DER encoded data (BER indefinite
041: * length is also supported) and assemble new DER data structures
042: * from elements. It is not safe to modify elements of constructed
043: * DER value that was created using parsing constructor or copy()
044: * method.
045: */
046:
047: /** ASN context specific flag used in types (0x80). */
048: public static final int CONTEXT = 0x80;
049: /** ASN constructed flag used in types (0x20). */
050: public static final int CONSTRUCTED = 0x20;
051: /** ASN constructed flag used in types (0x20). */
052: public static final int EXPLICIT = CONSTRUCTED;
053: /** ANY_STRING type used as a place holder. [UNIVERSAL 0] */
054: public static final int ANY_STRING_TYPE = 0x00; // our own impl
055: /** ASN BOOLEAN type used in certificate parsing. [UNIVERSAL 1] */
056: public static final int BOOLEAN_TYPE = 1;
057: /** ASN INTEGER type used in certificate parsing. [UNIVERSAL 2] */
058: public static final int INTEGER_TYPE = 2;
059: /** ASN BIT STRING type used in certificate parsing. [UNIVERSAL 3] */
060: public static final int BITSTRING_TYPE = 3;
061: /** ASN OCTET STRING type used in certificate parsing. [UNIVERSAL 4] */
062: public static final int OCTETSTR_TYPE = 4;
063: /** ASN NULL type used in certificate parsing. [UNIVERSAL 5] */
064: public static final int NULL_TYPE = 5;
065: /** ASN OBJECT ID type used in certificate parsing. [UNIVERSAL 6] */
066: public static final int OID_TYPE = 6;
067: /** ASN ENUMERATED type. [UNIVERSAL 10] */
068: public static final int ENUMERATED_TYPE = 10;
069: /** ASN UTF8String type used in certificate parsing. [UNIVERSAL 12] */
070: public static final int UTF8STR_TYPE = 12;
071: /**
072: * ASN SEQUENCE type used in certificate parsing.
073: * [UNIVERSAL CONSTRUCTED 16]
074: */
075: public static final int SEQUENCE_TYPE = CONSTRUCTED + 16;
076: /**
077: * ASN SET type used in certificate parsing.
078: * [UNIVERSAL CONSTRUCTED 17]
079: */
080: public static final int SET_TYPE = CONSTRUCTED + 17;
081: /** ASN PrintableString type used in certificate parsing. [UNIVERSAL 19] */
082: public static final int PRINTSTR_TYPE = 19;
083: /** ASN TELETEX STRING type used in certificate parsing. [UNIVERSAL 20] */
084: public static final int TELETEXSTR_TYPE = 20;
085: /** ASN IA5 STRING type used in certificate parsing. [UNIVERSAL 22] */
086: public static final int IA5STR_TYPE = 22;
087: /** ASN UCT time type used in certificate parsing. [UNIVERSAL 23] */
088: public static final int UCT_TIME_TYPE = 23;
089: /**
090: * ASN Generalized time type used in certificate parsing.
091: * [UNIVERSAL 24]
092: */
093: public static final int GEN_TIME_TYPE = 24;
094: /**
095: * ASN UniversalString type used in certificate parsing.
096: * [UNIVERSAL 28].
097: */
098: public static final int UNIVSTR_TYPE = 28;
099: /** ASN BIT STRING type used in certificate parsing. [UNIVERSAL 30] */
100: public static final int BMPSTR_TYPE = 30;
101: /**
102: * Context specific explicit type for certificate version.
103: * [CONTEXT EXPLICIT 0]
104: */
105: public static final int VERSION_TYPE = CONTEXT + EXPLICIT + 0;
106: /**
107: * Context specific explicit type for certificate extensions.
108: * [CONTEXT EXPLICIT 3]
109: */
110: public static final int EXTENSIONS_TYPE = CONTEXT + EXPLICIT + 3;
111:
112: /** Raw DER type. */
113: public int type;
114: /** Number of bytes that make up the value. */
115: public int length;
116: /** Offset of the value. */
117: public int valueOffset;
118: /** Non-null for constructed types, the first child TLV. */
119: public TLV child;
120: /** The next TLV in the parent sequence. */
121: public TLV next;
122: /** Buffer that contains the DER encoded TLV. */
123: public byte[] data;
124:
125: /**
126: * Constructs a TLV structure, recursing down for constructed types.
127: * @param buffer DER buffer
128: * @param offset where to start parsing
129: * @exception IndexOutOfBoundsException if the DER is corrupt
130: * @throws TLVException in case of parsing error
131: */
132: public TLV(byte[] buffer, int offset) throws TLVException {
133:
134: try {
135: data = buffer;
136: type = buffer[offset++] & 0xff;
137:
138: if ((type & 0x1f) == 0x1f) {
139: // multi byte type, 7 bits per byte,
140: // only last byte bit 8 as zero
141: throw new TLVException("Invalid tag");
142: }
143:
144: int size = buffer[offset++] & 0xff;
145: boolean indefinite = (size == 128);
146: if (indefinite) {
147: if ((type & 0x20) == 0) {
148: throw new TLVException("Invalid length");
149: }
150: } else if (size >= 128) {
151: int sizeLen = size - 128;
152:
153: // NOTE: for now, all sizes must fit int 3 bytes
154: if (sizeLen > 3) {
155: throw new TLVException("TLV is too large");
156: }
157:
158: size = 0;
159: while (sizeLen > 0) {
160: size = (size << 8) + (buffer[offset++] & 0xff);
161: sizeLen--;
162: }
163: }
164:
165: length = size;
166: valueOffset = offset;
167:
168: if ((type & 0x20) == 0 || length == 0) {
169: return;
170: }
171:
172: // constructed and not empty
173:
174: TLV prev = null;
175: while (true) {
176: if (indefinite && data[offset] == 0
177: && data[offset + 1] == 0) {
178: length = offset - valueOffset;
179: return;
180: }
181:
182: TLV temp = new TLV(buffer, offset);
183: offset = (data[offset + 1] == (byte) 0x80 ? 2 : 0)
184: + temp.valueOffset + temp.length;
185:
186: if (prev == null) {
187: child = temp;
188: } else {
189: prev.next = temp;
190: }
191: prev = temp;
192:
193: if (indefinite) {
194: continue;
195: }
196: if (offset == valueOffset + length) {
197: break;
198: }
199: if (offset > valueOffset + length) {
200: throw new TLVException("incorrect structure");
201: }
202: }
203: } catch (NullPointerException npe) {
204: throw new TLVException("parser error");
205: } catch (IndexOutOfBoundsException iobe) {
206: throw new TLVException("parser error");
207: } catch (NumberFormatException nfe) {
208: throw new TLVException("parser error");
209: }
210: }
211:
212: /**
213: * Constructs a TLV structure.
214: * @param tag tag of new TLV
215: */
216: public TLV(int tag) {
217: type = tag;
218: }
219:
220: /**
221: * Constructs a TLV structure.
222: * @param tag tag of the new TLV
223: * @param bytes value of the new TLV
224: */
225: public TLV(int tag, byte[] bytes) {
226: type = tag;
227: valueOffset = 0;
228: length = bytes.length;
229: data = bytes;
230: }
231:
232: /**
233: * Constructs a TLV structure.
234: * @param tag tag of the new TLV
235: * @param bytes data for new TLV
236: * @param offset of data
237: */
238: public TLV(int tag, byte[] bytes, int offset) {
239: type = tag;
240: valueOffset = offset;
241: length = bytes.length - offset;
242: data = bytes;
243: }
244:
245: /**
246: * Creates UTCTime TLV structure for given date.
247: * @param time date
248: * @return TLV value representing this date
249: */
250: public static TLV createUTCTime(Calendar time) {
251: byte[] data = new byte[13];
252: putDigits(data, 0, time.get(Calendar.YEAR));
253: putDigits(data, 2, time.get(Calendar.MONTH) + 1);
254: putDigits(data, 4, time.get(Calendar.DAY_OF_MONTH));
255: putDigits(data, 6, time.get(Calendar.HOUR_OF_DAY));
256: putDigits(data, 8, time.get(Calendar.MINUTE));
257: putDigits(data, 10, time.get(Calendar.SECOND));
258: data[12] = 0x5a;
259: return new TLV(UCT_TIME_TYPE, data);
260: }
261:
262: /**
263: * Creates TLV object of type sequence.
264: * @return new object
265: */
266: public static TLV createSequence() {
267: return new TLV(SEQUENCE_TYPE);
268: }
269:
270: /**
271: * Creates TLV object of type integer.
272: * @param data value
273: * @return new object
274: */
275: public static TLV createInteger(byte[] data) {
276: return new TLV(INTEGER_TYPE, data);
277: }
278:
279: /**
280: * Creates TLV object of type octet string.
281: * @param data value
282: * @return new object
283: */
284: public static TLV createOctetString(byte[] data) {
285: return new TLV(OCTETSTR_TYPE, data);
286: }
287:
288: /**
289: * Creates TLV object of type OID.
290: * @param oid OID in text form
291: * @return new object
292: */
293: public static TLV createOID(String oid) {
294: return new TLV(TLV.OID_TYPE, Utils.StringToOID(oid));
295: }
296:
297: /**
298: * Creates TLV object of type UTF8 string.
299: * @param s string value
300: * @return new object
301: */
302: public static TLV createUTF8String(String s) {
303: return new TLV(TLV.UTF8STR_TYPE, Utils.stringToBytes(s));
304: }
305:
306: /**
307: * Creates TLV object of type IA5 string.
308: * @param s string value
309: * @throws TLVException if illegal string has been provided
310: * @return new object
311: */
312: public static TLV createIA5String(String s) throws TLVException {
313: int len = (s == null ? 0 : s.length());
314:
315: if (len == 0) {
316: return new TLV(TLV.IA5STR_TYPE, new byte[] {});
317: }
318: byte[] b = new byte[len];
319:
320: for (int i = 0; i < len; i++) {
321: char c = s.charAt(i);
322: if (c >= 0 && c <= 127) {
323: b[i] = (byte) c;
324: } else {
325: throw new TLVException("Illegal string for IA5:" + s);
326: }
327: }
328: return new TLV(TLV.IA5STR_TYPE, b);
329: }
330:
331: /**
332: * Creates TLV object of type integer.
333: * @param value value
334: * @return new object
335: */
336: public static TLV createInteger(long value) {
337:
338: int check = (value < 0) ? -1 : 0;
339:
340: int i = 1;
341: while (i < 8) {
342: if (value >> (i * 8) == check) {
343: byte v = (byte) (value >> ((i - 1) * 8));
344: if (value < 0 ? v > 0 : v < 0) {
345: i++;
346: }
347: break;
348: }
349: i++;
350: }
351:
352: byte[] data = new byte[i];
353: while (i > 0) {
354: i--;
355: data[i] = (byte) value;
356: value = value >> 8;
357: }
358: return new TLV(TLV.INTEGER_TYPE, data);
359: }
360:
361: /**
362: * Creates a copy of this TLV. The value of field next of the new
363: * TLV is null.
364: * @return a copy of this TLV
365: */
366: public TLV copy() {
367: try {
368: return new TLV(getDERData(), 0);
369: } catch (TLVException e) {
370: }
371: return null;
372: }
373:
374: /**
375: * Sets next element for this TLV object.
376: * @param next the next object
377: * @return the passed value to allow chaining
378: */
379: public TLV setNext(TLV next) {
380: this .next = next;
381: return next;
382: }
383:
384: /**
385: * Sets child element for this TLV object.
386: * @param child the child object
387: * @return the passed value to allow chaining
388: */
389: public TLV setChild(TLV child) {
390: this .child = child;
391: return child;
392: }
393:
394: /**
395: * Sets the (implicit) tag value for this object.
396: * @param tag tag value
397: * @return <code>this</code> value to allow call chaining
398: */
399: public TLV setTag(int tag) {
400: this .type = tag;
401: return this ;
402: }
403:
404: /**
405: * Returns the value field of this TLV.
406: * @return the value field of this TLV
407: */
408: public byte[] getValue() {
409:
410: if (data == null) {
411: getDERSize();
412: }
413: byte[] x = new byte[length];
414: getValue_(x, 0);
415: return x;
416: }
417:
418: /**
419: * Returns DER encoded TLV.
420: * @return DER encoded TLV
421: */
422: public byte[] getDERData() {
423:
424: byte[] x = new byte[getDERSize()];
425: getDERData_(x, 0);
426: return x;
427: }
428:
429: /**
430: * Returns DER encoded TLV.
431: * @param buffer target buffer
432: * @param offset offset in the buffer
433: * @return value length
434: */
435: public int getDERData(byte[] buffer, int offset) {
436:
437: getDERSize();
438: return getDERData_(buffer, offset);
439: }
440:
441: /**
442: * Returns the size of DER encoded TLV.
443: * @return the size of DER encoded TLV
444: */
445: public int getDERSize() {
446:
447: if (data == null) {
448: length = 0;
449: TLV c = child;
450: while (c != null) {
451: length += c.getDERSize();
452: c = c.next;
453: }
454: }
455: return length + getTLSize();
456: }
457:
458: /**
459: * Returns integer value.
460: * @return integer value
461: * @throws TLVException if this TLV doesn't represent integer value
462: */
463: public int getInteger() throws TLVException {
464:
465: if (type != INTEGER_TYPE && ((type & 0xf0) != 0x80)) {
466: throw new TLVException("invalid type - getInteger");
467: }
468: return getIntegerValue();
469: }
470:
471: /**
472: * Returns octet string value as integer.
473: * @return integer value
474: * @throws TLVException if this TLV is not octet string
475: */
476: public int getId() throws TLVException {
477:
478: if (type != OCTETSTR_TYPE) {
479: throw new TLVException("invalid type - getId");
480: }
481: return getIntegerValue();
482: }
483:
484: /**
485: * Returns the value of enumerated type.
486: * @return the value
487: * @throws TLVException if TLV type is invalid
488: */
489: public int getEnumerated() throws TLVException {
490:
491: if (type != ENUMERATED_TYPE) {
492: throw new TLVException("invalid type - getEnumerated");
493: }
494: return getIntegerValue();
495: }
496:
497: /**
498: * Returns the value of TLV as integer.
499: * @return the integer value
500: * @throws TLVException if the value is too long
501: */
502: private int getIntegerValue() throws TLVException {
503:
504: int l = data[valueOffset] < 0 ? -1 : 0;
505: int check = l << 24;
506:
507: for (int i = 0; i < length; i++) {
508: if ((l & 0xff000000) != check) {
509: throw new TLVException("Integer value is too big");
510: }
511: l = (l << 8) | (data[valueOffset + i] & 0xff);
512: }
513: return l;
514: }
515:
516: /**
517: * Returns string represented by this UTF8 string.
518: * @return string value
519: * @throws TLVException if TLV type is invalid
520: */
521: public String getUTF8() throws TLVException {
522:
523: if (type != UTF8STR_TYPE && ((type & 0xf0) != 0x80)) {
524: throw new TLVException("invalid type - getUTF8");
525: }
526:
527: try {
528: return new String(data, valueOffset, length, Utils.utf8);
529: } catch (UnsupportedEncodingException e) {
530: throw new TLVException("invalid encoding");
531: }
532: }
533:
534: /**
535: * Returns true if this value represents string.
536: * @return true if this value represents string
537: */
538: public boolean isString() {
539: return (type == TELETEXSTR_TYPE || type == PRINTSTR_TYPE
540: || type == UNIVSTR_TYPE || type == UTF8STR_TYPE || type == BMPSTR_TYPE);
541: }
542:
543: /**
544: * Returns time represented by this TLV.
545: * @return time value
546: * @throws TLVException if TLV type is invalid
547: */
548: public Calendar getTime() throws TLVException {
549:
550: if (type != GEN_TIME_TYPE && type != UCT_TIME_TYPE) {
551: throw new TLVException("invalid type - getType");
552: }
553:
554: Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
555:
556: int offset;
557: int year;
558: if (type == GEN_TIME_TYPE) {
559: year = getTimeComponent(0, 4);
560: offset = 4;
561: } else {
562: year = getTimeComponent(0, 2);
563: year += (year >= 50) ? 1900 : 2000;
564: offset = 2;
565: }
566: c.set(Calendar.YEAR, year);
567: c.set(Calendar.MONTH, getTimeComponent(offset, 2) - 1);
568: offset += 2;
569: c.set(Calendar.DAY_OF_MONTH, getTimeComponent(offset, 2));
570: offset += 2;
571: c.set(Calendar.HOUR_OF_DAY, getTimeComponent(offset, 2));
572: offset += 2;
573: c.set(Calendar.MINUTE, getTimeComponent(offset, 2));
574: offset += 2;
575: c.set(Calendar.SECOND, getTimeComponent(offset, 2));
576:
577: return c;
578: }
579:
580: /**
581: * Returns decoded BCD value.
582: * @param offset value offset
583: * @param len value length
584: * @return decoded value
585: */
586: private int getTimeComponent(int offset, int len) {
587:
588: int value = 0;
589: while (len-- > 0) {
590: value = value * 10 + (data[valueOffset + offset++] - 0x30);
591: }
592: return value;
593: }
594:
595: /**
596: * Skips optional element of DER structure with given tag.
597: * @param type tag of optional value
598: * @return this object if type doesn't match or the next one if it
599: * does
600: */
601: public TLV skipOptional(int type) {
602:
603: if (this .type == type) {
604: return next;
605: }
606: return this ;
607: }
608:
609: /**
610: * Returns the value of flag stored in bitsring value.
611: * @param index flag index
612: * @return true if the flag is set
613: * @throws TLVException if TLV type is invalid
614: */
615: public boolean checkFlag(int index) throws TLVException {
616:
617: if (type != BITSTRING_TYPE) {
618: throw new TLVException("invalid type - checkFlag");
619: }
620:
621: int i = (length - 1) * 8 - data[valueOffset];
622: if (index >= i) {
623: return false;
624: }
625:
626: return ((data[valueOffset + 1 + (index / 8)] << index % 8) & 0x80) != 0;
627: }
628:
629: /**
630: * Compares the value of this TLV with given value.
631: * @param data the value to be compared
632: * @return true if TLV object contains the same value
633: */
634: public boolean valueEquals(byte[] data) {
635: return Utils.byteMatch(this .data, valueOffset, length, data, 0,
636: data.length);
637: }
638:
639: /**
640: * Places two ASCII encoded decimal digits into byte array.
641: * @param data byte aray
642: * @param offset the index of the first byte
643: * @param value the value to be placed into the buffer
644: */
645: private static void putDigits(byte[] data, int offset, int value) {
646:
647: value = value % 100;
648: data[offset++] = (byte) (0x30 | (value / 10));
649: data[offset++] = (byte) (0x30 | (value % 10));
650: }
651:
652: /**
653: * IMPL_NOTE delete
654: * Print the a TLV structure, recursing down for constructed types.
655: * /
656: public void print() {
657: print(System.out, 0);
658: }
659:
660: /**
661: * IMPL_NOTE delete
662: * Print the a TLV structure, recursing down for constructed types.
663: * @param out output stream
664: * /
665: public void print(PrintStream out) {
666: print(out, 0);
667: }
668:
669: /**
670: * IMPL_NOTE delete
671: * Prints the a TLV structure, recursing down for constructed types.
672: * @param out output stream
673: * @param level what level this TLV is at
674: * /
675: private void print(PrintStream out, int level) {
676:
677: for (int i = 0; i < level; i++) {
678: out.print(" ");
679: }
680:
681: byte[] buffer;
682:
683: if (data != null) {
684: buffer = data;
685: } else {
686: buffer = getDERData();
687: }
688:
689: if (child == null) {
690: out.print("Type: 0x" + Integer.toHexString(type) +
691: " length: " + length + " value: ");
692: if (type == PRINTSTR_TYPE ||
693: type == TELETEXSTR_TYPE ||
694: type == UTF8STR_TYPE ||
695: type == IA5STR_TYPE ||
696: type == UNIVSTR_TYPE) {
697: try {
698: out.print(new String(buffer, valueOffset, length,
699: Utils.utf8));
700: } catch (UnsupportedEncodingException e) {
701: // ignore
702: }
703: } else if (type == OID_TYPE) {
704: out.print(Utils.OIDtoString(buffer, valueOffset, length));
705: } else {
706: out.print(Utils.hexNumber(buffer, valueOffset, length));
707: }
708:
709: out.println("");
710: } else {
711: if (type == SET_TYPE) {
712: out.print("Set:");
713: } else {
714: out.print("Sequence:");
715: }
716:
717: out.println(" (0x" + Integer.toHexString(type) +
718: " " + length + ")");
719:
720: child.print(out, level + 1);
721: }
722:
723: if (next != null) {
724: next.print(out, level);
725: }
726: }
727: /* */
728:
729: /**
730: * Places the value field of this TLV into the buffer.
731: * @param buffer target buffer
732: * @param offset index of the first byte
733: * @return value length
734: */
735: private int getValue_(byte[] buffer, int offset) {
736:
737: if (data == null) {
738: TLV c = child;
739: while (c != null) {
740: offset += c.getDERData(buffer, offset);
741: c = c.next;
742: }
743: } else {
744: System.arraycopy(data, valueOffset, buffer, offset, length);
745: }
746: return length;
747: }
748:
749: /**
750: * Places tag and length values into the buffer.
751: * @param x byte buffer
752: * @param i offset
753: * @return value offset in the buffer
754: */
755: private int putHeader(byte[] x, int i) {
756:
757: x[i++] = (byte) type;
758:
759: if (length < 128) {
760: x[i++] = (byte) length;
761: } else if (length < 256) {
762: x[i++] = (byte) 0x81;
763: x[i++] = (byte) length;
764: } else {
765: x[i++] = (byte) 0x82;
766: x[i++] = (byte) (length >> 8);
767: x[i++] = (byte) length;
768: }
769: return i;
770: }
771:
772: /**
773: * Returns DER encoded TLV.
774: * @param buffer target buffer
775: * @param offset offset in the buffer
776: * @return value length
777: */
778: private int getDERData_(byte[] buffer, int offset) {
779:
780: int initialOffset = offset;
781: offset = putHeader(buffer, offset);
782:
783: if (data == null) {
784: TLV c = child;
785: while (c != null) {
786: offset += c.getDERData_(buffer, offset);
787: c = c.next;
788: }
789: } else {
790: System.arraycopy(data, valueOffset, buffer, offset, length);
791: offset += length;
792: }
793: return (offset - initialOffset);
794: }
795:
796: /**
797: * Returns the size of tag and length encoding.
798: * @return the size of tag and length encoding
799: */
800: private int getTLSize() {
801:
802: int TLSize = 2;
803: if (length >= 128) {
804: int i = length;
805: while (i != 0) {
806: TLSize++;
807: i = i >> 8;
808: }
809: }
810: return TLSize;
811: }
812:
813: /**
814: * Compares this object with other TLV object.
815: * @param t TLV object
816: * @return true if both objects have the same type and contain
817: * the same data
818: */
819: public boolean match(TLV t) {
820:
821: if (type != t.type) {
822: return false;
823: }
824: if (t.data == null) {
825: t = t.copy();
826: }
827: if (data == null) {
828: t.match(this);
829: }
830: return Utils.byteMatch(data, valueOffset, length, t.data,
831: t.valueOffset, t.length);
832: }
833:
834: }
|