1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.apksig.internal.asn1;
18 
19 import com.android.apksig.internal.asn1.ber.BerDataValue;
20 import com.android.apksig.internal.asn1.ber.BerDataValueFormatException;
21 import com.android.apksig.internal.asn1.ber.BerDataValueReader;
22 import com.android.apksig.internal.asn1.ber.BerEncoding;
23 import com.android.apksig.internal.asn1.ber.ByteBufferBerDataValueReader;
24 import com.android.apksig.internal.util.ByteBufferUtils;
25 
26 import java.lang.reflect.Field;
27 import java.lang.reflect.Modifier;
28 import java.math.BigInteger;
29 import java.nio.ByteBuffer;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.List;
33 
34 /**
35  * Parser of ASN.1 BER-encoded structures.
36  *
37  * <p>Structure is described to the parser by providing a class annotated with {@link Asn1Class},
38  * containing fields annotated with {@link Asn1Field}.
39  */
40 public final class Asn1BerParser {
Asn1BerParser()41     private Asn1BerParser() {}
42 
43     /**
44      * Returns the ASN.1 structure contained in the BER encoded input.
45      *
46      * @param encoded encoded input. If the decoding operation succeeds, the position of this buffer
47      *        is advanced to the first position following the end of the consumed structure.
48      * @param containerClass class describing the structure of the input. The class must meet the
49      *        following requirements:
50      *        <ul>
51      *        <li>The class must be annotated with {@link Asn1Class}.</li>
52      *        <li>The class must expose a public no-arg constructor.</li>
53      *        <li>Member fields of the class which are populated with parsed input must be
54      *            annotated with {@link Asn1Field} and be public and non-final.</li>
55      *        </ul>
56      *
57      * @throws Asn1DecodingException if the input could not be decoded into the specified Java
58      *         object
59      */
parse(ByteBuffer encoded, Class<T> containerClass)60     public static <T> T parse(ByteBuffer encoded, Class<T> containerClass)
61             throws Asn1DecodingException {
62         BerDataValue containerDataValue;
63         try {
64             containerDataValue = new ByteBufferBerDataValueReader(encoded).readDataValue();
65         } catch (BerDataValueFormatException e) {
66             throw new Asn1DecodingException("Failed to decode top-level data value", e);
67         }
68         if (containerDataValue == null) {
69             throw new Asn1DecodingException("Empty input");
70         }
71         return parse(containerDataValue, containerClass);
72     }
73 
74     /**
75      * Returns the implicit {@code SET OF} contained in the provided ASN.1 BER input. Implicit means
76      * that this method does not care whether the tag number of this data structure is
77      * {@code SET OF} and whether the tag class is {@code UNIVERSAL}.
78      *
79      * <p>Note: The returned type is {@link List} rather than {@link java.util.Set} because ASN.1
80      * SET may contain duplicate elements.
81      *
82      * @param encoded encoded input. If the decoding operation succeeds, the position of this buffer
83      *        is advanced to the first position following the end of the consumed structure.
84      * @param elementClass class describing the structure of the values/elements contained in this
85      *        container. The class must meet the following requirements:
86      *        <ul>
87      *        <li>The class must be annotated with {@link Asn1Class}.</li>
88      *        <li>The class must expose a public no-arg constructor.</li>
89      *        <li>Member fields of the class which are populated with parsed input must be
90      *            annotated with {@link Asn1Field} and be public and non-final.</li>
91      *        </ul>
92      *
93      * @throws Asn1DecodingException if the input could not be decoded into the specified Java
94      *         object
95      */
parseImplicitSetOf(ByteBuffer encoded, Class<T> elementClass)96     public static <T> List<T> parseImplicitSetOf(ByteBuffer encoded, Class<T> elementClass)
97             throws Asn1DecodingException {
98         BerDataValue containerDataValue;
99         try {
100             containerDataValue = new ByteBufferBerDataValueReader(encoded).readDataValue();
101         } catch (BerDataValueFormatException e) {
102             throw new Asn1DecodingException("Failed to decode top-level data value", e);
103         }
104         if (containerDataValue == null) {
105             throw new Asn1DecodingException("Empty input");
106         }
107         return parseSetOf(containerDataValue, elementClass);
108     }
109 
parse(BerDataValue container, Class<T> containerClass)110     private static <T> T parse(BerDataValue container, Class<T> containerClass)
111             throws Asn1DecodingException {
112         if (container == null) {
113             throw new NullPointerException("container == null");
114         }
115         if (containerClass == null) {
116             throw new NullPointerException("containerClass == null");
117         }
118 
119         Asn1Type dataType = getContainerAsn1Type(containerClass);
120         switch (dataType) {
121             case CHOICE:
122                 return parseChoice(container, containerClass);
123 
124             case SEQUENCE:
125             {
126                 int expectedTagClass = BerEncoding.TAG_CLASS_UNIVERSAL;
127                 int expectedTagNumber = BerEncoding.getTagNumber(dataType);
128                 if ((container.getTagClass() != expectedTagClass)
129                         || (container.getTagNumber() != expectedTagNumber)) {
130                     throw new Asn1UnexpectedTagException(
131                             "Unexpected data value read as " + containerClass.getName()
132                                     + ". Expected " + BerEncoding.tagClassAndNumberToString(
133                                     expectedTagClass, expectedTagNumber)
134                                     + ", but read: " + BerEncoding.tagClassAndNumberToString(
135                                     container.getTagClass(), container.getTagNumber()));
136                 }
137                 return parseSequence(container, containerClass);
138             }
139             case UNENCODED_CONTAINER:
140                 return parseSequence(container, containerClass, true);
141             default:
142                 throw new Asn1DecodingException("Parsing container " + dataType + " not supported");
143         }
144     }
145 
parseChoice(BerDataValue dataValue, Class<T> containerClass)146     private static <T> T parseChoice(BerDataValue dataValue, Class<T> containerClass)
147             throws Asn1DecodingException {
148         List<AnnotatedField> fields = getAnnotatedFields(containerClass);
149         if (fields.isEmpty()) {
150             throw new Asn1DecodingException(
151                     "No fields annotated with " + Asn1Field.class.getName()
152                             + " in CHOICE class " + containerClass.getName());
153         }
154 
155         // Check that class + tagNumber don't clash between the choices
156         for (int i = 0; i < fields.size() - 1; i++) {
157             AnnotatedField f1 = fields.get(i);
158             int tagNumber1 = f1.getBerTagNumber();
159             int tagClass1 = f1.getBerTagClass();
160             for (int j = i + 1; j < fields.size(); j++) {
161                 AnnotatedField f2 = fields.get(j);
162                 int tagNumber2 = f2.getBerTagNumber();
163                 int tagClass2 = f2.getBerTagClass();
164                 if ((tagNumber1 == tagNumber2) && (tagClass1 == tagClass2)) {
165                     throw new Asn1DecodingException(
166                             "CHOICE fields are indistinguishable because they have the same tag"
167                                     + " class and number: " + containerClass.getName()
168                                     + "." + f1.getField().getName()
169                                     + " and ." + f2.getField().getName());
170                 }
171             }
172         }
173 
174         // Instantiate the container object / result
175         T obj;
176         try {
177             obj = containerClass.getConstructor().newInstance();
178         } catch (IllegalArgumentException | ReflectiveOperationException e) {
179             throw new Asn1DecodingException("Failed to instantiate " + containerClass.getName(), e);
180         }
181         // Set the matching field's value from the data value
182         for (AnnotatedField field : fields) {
183             try {
184                 field.setValueFrom(dataValue, obj);
185                 return obj;
186             } catch (Asn1UnexpectedTagException expected) {
187                 // not a match
188             }
189         }
190 
191         throw new Asn1DecodingException(
192                 "No options of CHOICE " + containerClass.getName() + " matched");
193     }
194 
parseSequence(BerDataValue container, Class<T> containerClass)195     private static <T> T parseSequence(BerDataValue container, Class<T> containerClass)
196             throws Asn1DecodingException {
197         return parseSequence(container, containerClass, false);
198     }
199 
parseSequence(BerDataValue container, Class<T> containerClass, boolean isUnencodedContainer)200     private static <T> T parseSequence(BerDataValue container, Class<T> containerClass,
201             boolean isUnencodedContainer) throws Asn1DecodingException {
202         List<AnnotatedField> fields = getAnnotatedFields(containerClass);
203         Collections.sort(
204                 fields, (f1, f2) -> f1.getAnnotation().index() - f2.getAnnotation().index());
205         // Check that there are no fields with the same index
206         if (fields.size() > 1) {
207             AnnotatedField lastField = null;
208             for (AnnotatedField field : fields) {
209                 if ((lastField != null)
210                         && (lastField.getAnnotation().index() == field.getAnnotation().index())) {
211                     throw new Asn1DecodingException(
212                             "Fields have the same index: " + containerClass.getName()
213                                     + "." + lastField.getField().getName()
214                                     + " and ." + field.getField().getName());
215                 }
216                 lastField = field;
217             }
218         }
219 
220         // Instantiate the container object / result
221         T t;
222         try {
223             t = containerClass.getConstructor().newInstance();
224         } catch (IllegalArgumentException | ReflectiveOperationException e) {
225             throw new Asn1DecodingException("Failed to instantiate " + containerClass.getName(), e);
226         }
227 
228         // Parse fields one by one. A complication is that there may be optional fields.
229         int nextUnreadFieldIndex = 0;
230         BerDataValueReader elementsReader = container.contentsReader();
231         while (nextUnreadFieldIndex < fields.size()) {
232             BerDataValue dataValue;
233             try {
234                 // if this is the first field of an unencoded container then the entire contents of
235                 // the container should be used when assigning to this field.
236                 if (isUnencodedContainer && nextUnreadFieldIndex == 0) {
237                     dataValue = container;
238                 } else {
239                     dataValue = elementsReader.readDataValue();
240                 }
241             } catch (BerDataValueFormatException e) {
242                 throw new Asn1DecodingException("Malformed data value", e);
243             }
244             if (dataValue == null) {
245                 break;
246             }
247 
248             for (int i = nextUnreadFieldIndex; i < fields.size(); i++) {
249                 AnnotatedField field = fields.get(i);
250                 try {
251                     if (field.isOptional()) {
252                         // Optional field -- might not be present and we may thus be trying to set
253                         // it from the wrong tag.
254                         try {
255                             field.setValueFrom(dataValue, t);
256                             nextUnreadFieldIndex = i + 1;
257                             break;
258                         } catch (Asn1UnexpectedTagException e) {
259                             // This field is not present, attempt to use this data value for the
260                             // next / iteration of the loop
261                             continue;
262                         }
263                     } else {
264                         // Mandatory field -- if we can't set its value from this data value, then
265                         // it's an error
266                         field.setValueFrom(dataValue, t);
267                         nextUnreadFieldIndex = i + 1;
268                         break;
269                     }
270                 } catch (Asn1DecodingException e) {
271                     throw new Asn1DecodingException(
272                             "Failed to parse " + containerClass.getName()
273                                     + "." + field.getField().getName(),
274                             e);
275                 }
276             }
277         }
278 
279         return t;
280     }
281 
282     // NOTE: This method returns List rather than Set because ASN.1 SET_OF does require uniqueness
283     // of elements -- it's an unordered collection.
284     @SuppressWarnings("unchecked")
parseSetOf(BerDataValue container, Class<T> elementClass)285     private static <T> List<T> parseSetOf(BerDataValue container, Class<T> elementClass)
286             throws Asn1DecodingException {
287         List<T> result = new ArrayList<>();
288         BerDataValueReader elementsReader = container.contentsReader();
289         while (true) {
290             BerDataValue dataValue;
291             try {
292                 dataValue = elementsReader.readDataValue();
293             } catch (BerDataValueFormatException e) {
294                 throw new Asn1DecodingException("Malformed data value", e);
295             }
296             if (dataValue == null) {
297                 break;
298             }
299             T element;
300             if (ByteBuffer.class.equals(elementClass)) {
301                 element = (T) dataValue.getEncodedContents();
302             } else if (Asn1OpaqueObject.class.equals(elementClass)) {
303                 element = (T) new Asn1OpaqueObject(dataValue.getEncoded());
304             } else {
305                 element = parse(dataValue, elementClass);
306             }
307             result.add(element);
308         }
309         return result;
310     }
311 
getContainerAsn1Type(Class<?> containerClass)312     private static Asn1Type getContainerAsn1Type(Class<?> containerClass)
313             throws Asn1DecodingException {
314         Asn1Class containerAnnotation = containerClass.getDeclaredAnnotation(Asn1Class.class);
315         if (containerAnnotation == null) {
316             throw new Asn1DecodingException(
317                     containerClass.getName() + " is not annotated with "
318                             + Asn1Class.class.getName());
319         }
320 
321         switch (containerAnnotation.type()) {
322             case CHOICE:
323             case SEQUENCE:
324             case UNENCODED_CONTAINER:
325                 return containerAnnotation.type();
326             default:
327                 throw new Asn1DecodingException(
328                         "Unsupported ASN.1 container annotation type: "
329                                 + containerAnnotation.type());
330         }
331     }
332 
getElementType(Field field)333     private static Class<?> getElementType(Field field)
334             throws Asn1DecodingException, ClassNotFoundException {
335         String type = field.getGenericType().getTypeName();
336         int delimiterIndex =  type.indexOf('<');
337         if (delimiterIndex == -1) {
338             throw new Asn1DecodingException("Not a container type: " + field.getGenericType());
339         }
340         int startIndex = delimiterIndex + 1;
341         int endIndex = type.indexOf('>', startIndex);
342         // TODO: handle comma?
343         if (endIndex == -1) {
344             throw new Asn1DecodingException("Not a container type: " + field.getGenericType());
345         }
346         String elementClassName = type.substring(startIndex, endIndex);
347         return Class.forName(elementClassName);
348     }
349 
350     private static final class AnnotatedField {
351         private final Field mField;
352         private final Asn1Field mAnnotation;
353         private final Asn1Type mDataType;
354         private final Asn1TagClass mTagClass;
355         private final int mBerTagClass;
356         private final int mBerTagNumber;
357         private final Asn1Tagging mTagging;
358         private final boolean mOptional;
359 
AnnotatedField(Field field, Asn1Field annotation)360         public AnnotatedField(Field field, Asn1Field annotation) throws Asn1DecodingException {
361             mField = field;
362             mAnnotation = annotation;
363             mDataType = annotation.type();
364 
365             Asn1TagClass tagClass = annotation.cls();
366             if (tagClass == Asn1TagClass.AUTOMATIC) {
367                 if (annotation.tagNumber() != -1) {
368                     tagClass = Asn1TagClass.CONTEXT_SPECIFIC;
369                 } else {
370                     tagClass = Asn1TagClass.UNIVERSAL;
371                 }
372             }
373             mTagClass = tagClass;
374             mBerTagClass = BerEncoding.getTagClass(mTagClass);
375 
376             int tagNumber;
377             if (annotation.tagNumber() != -1) {
378                 tagNumber = annotation.tagNumber();
379             } else if ((mDataType == Asn1Type.CHOICE) || (mDataType == Asn1Type.ANY)) {
380                 tagNumber = -1;
381             } else {
382                 tagNumber = BerEncoding.getTagNumber(mDataType);
383             }
384             mBerTagNumber = tagNumber;
385 
386             mTagging = annotation.tagging();
387             if (((mTagging == Asn1Tagging.EXPLICIT) || (mTagging == Asn1Tagging.IMPLICIT))
388                     && (annotation.tagNumber() == -1)) {
389                 throw new Asn1DecodingException(
390                         "Tag number must be specified when tagging mode is " + mTagging);
391             }
392 
393             mOptional = annotation.optional();
394         }
395 
getField()396         public Field getField() {
397             return mField;
398         }
399 
getAnnotation()400         public Asn1Field getAnnotation() {
401             return mAnnotation;
402         }
403 
isOptional()404         public boolean isOptional() {
405             return mOptional;
406         }
407 
getBerTagClass()408         public int getBerTagClass() {
409             return mBerTagClass;
410         }
411 
getBerTagNumber()412         public int getBerTagNumber() {
413             return mBerTagNumber;
414         }
415 
setValueFrom(BerDataValue dataValue, Object obj)416         public void setValueFrom(BerDataValue dataValue, Object obj) throws Asn1DecodingException {
417             int readTagClass = dataValue.getTagClass();
418             if (mBerTagNumber != -1) {
419                 int readTagNumber = dataValue.getTagNumber();
420                 if ((readTagClass != mBerTagClass) || (readTagNumber != mBerTagNumber)) {
421                     throw new Asn1UnexpectedTagException(
422                             "Tag mismatch. Expected: "
423                             + BerEncoding.tagClassAndNumberToString(mBerTagClass, mBerTagNumber)
424                             + ", but found "
425                             + BerEncoding.tagClassAndNumberToString(readTagClass, readTagNumber));
426                 }
427             } else {
428                 if (readTagClass != mBerTagClass) {
429                     throw new Asn1UnexpectedTagException(
430                             "Tag mismatch. Expected class: "
431                             + BerEncoding.tagClassToString(mBerTagClass)
432                             + ", but found "
433                             + BerEncoding.tagClassToString(readTagClass));
434                 }
435             }
436 
437             if (mTagging == Asn1Tagging.EXPLICIT) {
438                 try {
439                     dataValue = dataValue.contentsReader().readDataValue();
440                 } catch (BerDataValueFormatException e) {
441                     throw new Asn1DecodingException(
442                             "Failed to read contents of EXPLICIT data value", e);
443                 }
444             }
445 
446             BerToJavaConverter.setFieldValue(obj, mField, mDataType, dataValue);
447         }
448     }
449 
450     private static class Asn1UnexpectedTagException extends Asn1DecodingException {
451         private static final long serialVersionUID = 1L;
452 
Asn1UnexpectedTagException(String message)453         public Asn1UnexpectedTagException(String message) {
454             super(message);
455         }
456     }
457 
oidToString(ByteBuffer encodedOid)458     private static String oidToString(ByteBuffer encodedOid) throws Asn1DecodingException {
459         if (!encodedOid.hasRemaining()) {
460             throw new Asn1DecodingException("Empty OBJECT IDENTIFIER");
461         }
462 
463         // First component encodes the first two nodes, X.Y, as X * 40 + Y, with 0 <= X <= 2
464         long firstComponent = decodeBase128UnsignedLong(encodedOid);
465         int firstNode = (int) Math.min(firstComponent / 40, 2);
466         long secondNode = firstComponent - firstNode * 40;
467         StringBuilder result = new StringBuilder();
468         result.append(Long.toString(firstNode)).append('.')
469                 .append(Long.toString(secondNode));
470 
471         // Each consecutive node is encoded as a separate component
472         while (encodedOid.hasRemaining()) {
473             long node = decodeBase128UnsignedLong(encodedOid);
474             result.append('.').append(Long.toString(node));
475         }
476 
477         return result.toString();
478     }
479 
decodeBase128UnsignedLong(ByteBuffer encoded)480     private static long decodeBase128UnsignedLong(ByteBuffer encoded) throws Asn1DecodingException {
481         if (!encoded.hasRemaining()) {
482             return 0;
483         }
484         long result = 0;
485         while (encoded.hasRemaining()) {
486             if (result > Long.MAX_VALUE >>> 7) {
487                 throw new Asn1DecodingException("Base-128 number too large");
488             }
489             int b = encoded.get() & 0xff;
490             result <<= 7;
491             result |= b & 0x7f;
492             if ((b & 0x80) == 0) {
493                 return result;
494             }
495         }
496         throw new Asn1DecodingException(
497                 "Truncated base-128 encoded input: missing terminating byte, with highest bit not"
498                         + " set");
499     }
500 
integerToBigInteger(ByteBuffer encoded)501     private static BigInteger integerToBigInteger(ByteBuffer encoded) {
502         if (!encoded.hasRemaining()) {
503             return BigInteger.ZERO;
504         }
505         return new BigInteger(ByteBufferUtils.toByteArray(encoded));
506     }
507 
integerToInt(ByteBuffer encoded)508     private static int integerToInt(ByteBuffer encoded) throws Asn1DecodingException {
509         BigInteger value = integerToBigInteger(encoded);
510         try {
511             return value.intValueExact();
512         } catch (ArithmeticException e) {
513             throw new Asn1DecodingException(
514                     String.format("INTEGER cannot be represented as int: %1$d (0x%1$x)", value), e);
515         }
516     }
517 
integerToLong(ByteBuffer encoded)518     private static long integerToLong(ByteBuffer encoded) throws Asn1DecodingException {
519         BigInteger value = integerToBigInteger(encoded);
520         try {
521             return value.longValueExact();
522         } catch (ArithmeticException e) {
523             throw new Asn1DecodingException(
524                     String.format("INTEGER cannot be represented as long: %1$d (0x%1$x)", value),
525                     e);
526         }
527     }
528 
getAnnotatedFields(Class<?> containerClass)529     private static List<AnnotatedField> getAnnotatedFields(Class<?> containerClass)
530             throws Asn1DecodingException {
531         Field[] declaredFields = containerClass.getDeclaredFields();
532         List<AnnotatedField> result = new ArrayList<>(declaredFields.length);
533         for (Field field : declaredFields) {
534             Asn1Field annotation = field.getDeclaredAnnotation(Asn1Field.class);
535             if (annotation == null) {
536                 continue;
537             }
538             if (Modifier.isStatic(field.getModifiers())) {
539                 throw new Asn1DecodingException(
540                         Asn1Field.class.getName() + " used on a static field: "
541                                 + containerClass.getName() + "." + field.getName());
542             }
543 
544             AnnotatedField annotatedField;
545             try {
546                 annotatedField = new AnnotatedField(field, annotation);
547             } catch (Asn1DecodingException e) {
548                 throw new Asn1DecodingException(
549                         "Invalid ASN.1 annotation on "
550                                 + containerClass.getName() + "." + field.getName(),
551                         e);
552             }
553             result.add(annotatedField);
554         }
555         return result;
556     }
557 
558     private static final class BerToJavaConverter {
BerToJavaConverter()559         private BerToJavaConverter() {}
560 
setFieldValue( Object obj, Field field, Asn1Type type, BerDataValue dataValue)561         public static void setFieldValue(
562                 Object obj, Field field, Asn1Type type, BerDataValue dataValue)
563                         throws Asn1DecodingException {
564             try {
565                 switch (type) {
566                     case SET_OF:
567                     case SEQUENCE_OF:
568                         if (Asn1OpaqueObject.class.equals(field.getType())) {
569                             field.set(obj, convert(type, dataValue, field.getType()));
570                         } else {
571                             field.set(obj, parseSetOf(dataValue, getElementType(field)));
572                         }
573                         return;
574                     default:
575                         field.set(obj, convert(type, dataValue, field.getType()));
576                         break;
577                 }
578             } catch (ReflectiveOperationException e) {
579                 throw new Asn1DecodingException(
580                         "Failed to set value of " + obj.getClass().getName()
581                                 + "." + field.getName(),
582                         e);
583             }
584         }
585 
586         private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
587 
588         @SuppressWarnings("unchecked")
convert( Asn1Type sourceType, BerDataValue dataValue, Class<T> targetType)589         public static <T> T convert(
590                 Asn1Type sourceType,
591                 BerDataValue dataValue,
592                 Class<T> targetType) throws Asn1DecodingException {
593             if (ByteBuffer.class.equals(targetType)) {
594                 return (T) dataValue.getEncodedContents();
595             } else if (byte[].class.equals(targetType)) {
596                 ByteBuffer resultBuf = dataValue.getEncodedContents();
597                 if (!resultBuf.hasRemaining()) {
598                     return (T) EMPTY_BYTE_ARRAY;
599                 }
600                 byte[] result = new byte[resultBuf.remaining()];
601                 resultBuf.get(result);
602                 return (T) result;
603             } else if (Asn1OpaqueObject.class.equals(targetType)) {
604                 return (T) new Asn1OpaqueObject(dataValue.getEncoded());
605             }
606             ByteBuffer encodedContents = dataValue.getEncodedContents();
607             switch (sourceType) {
608                 case INTEGER:
609                     if ((int.class.equals(targetType)) || (Integer.class.equals(targetType))) {
610                         return (T) Integer.valueOf(integerToInt(encodedContents));
611                     } else if ((long.class.equals(targetType)) || (Long.class.equals(targetType))) {
612                         return (T) Long.valueOf(integerToLong(encodedContents));
613                     } else if (BigInteger.class.equals(targetType)) {
614                         return (T) integerToBigInteger(encodedContents);
615                     }
616                     break;
617                 case OBJECT_IDENTIFIER:
618                     if (String.class.equals(targetType)) {
619                         return (T) oidToString(encodedContents);
620                     }
621                     break;
622                 case UTC_TIME:
623                 case GENERALIZED_TIME:
624                     if (String.class.equals(targetType)) {
625                         return (T) new String(ByteBufferUtils.toByteArray(encodedContents));
626                     }
627                     break;
628                 case BOOLEAN:
629                     // A boolean should be encoded in a single byte with a value of 0 for false and
630                     // any non-zero value for true.
631                     if (boolean.class.equals(targetType)) {
632                         if (encodedContents.remaining() != 1) {
633                             throw new Asn1DecodingException(
634                                     "Incorrect encoded size of boolean value: "
635                                             + encodedContents.remaining());
636                         }
637                         boolean result;
638                         if (encodedContents.get() == 0) {
639                             result = false;
640                         } else {
641                             result = true;
642                         }
643                         return (T) new Boolean(result);
644                     }
645                     break;
646                 case SEQUENCE:
647                 {
648                     Asn1Class containerAnnotation =
649                             targetType.getDeclaredAnnotation(Asn1Class.class);
650                     if ((containerAnnotation != null)
651                             && (containerAnnotation.type() == Asn1Type.SEQUENCE)) {
652                         return parseSequence(dataValue, targetType);
653                     }
654                     break;
655                 }
656                 case CHOICE:
657                 {
658                     Asn1Class containerAnnotation =
659                             targetType.getDeclaredAnnotation(Asn1Class.class);
660                     if ((containerAnnotation != null)
661                             && (containerAnnotation.type() == Asn1Type.CHOICE)) {
662                         return parseChoice(dataValue, targetType);
663                     }
664                     break;
665                 }
666                 default:
667                     break;
668             }
669 
670             throw new Asn1DecodingException(
671                     "Unsupported conversion: ASN.1 " + sourceType + " to " + targetType.getName());
672         }
673     }
674 }
675