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