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 140 default: 141 throw new Asn1DecodingException("Parsing container " + dataType + " not supported"); 142 } 143 } 144 parseChoice(BerDataValue dataValue, Class<T> containerClass)145 private static <T> T parseChoice(BerDataValue dataValue, Class<T> containerClass) 146 throws Asn1DecodingException { 147 List<AnnotatedField> fields = getAnnotatedFields(containerClass); 148 if (fields.isEmpty()) { 149 throw new Asn1DecodingException( 150 "No fields annotated with " + Asn1Field.class.getName() 151 + " in CHOICE class " + containerClass.getName()); 152 } 153 154 // Check that class + tagNumber don't clash between the choices 155 for (int i = 0; i < fields.size() - 1; i++) { 156 AnnotatedField f1 = fields.get(i); 157 int tagNumber1 = f1.getBerTagNumber(); 158 int tagClass1 = f1.getBerTagClass(); 159 for (int j = i + 1; j < fields.size(); j++) { 160 AnnotatedField f2 = fields.get(j); 161 int tagNumber2 = f2.getBerTagNumber(); 162 int tagClass2 = f2.getBerTagClass(); 163 if ((tagNumber1 == tagNumber2) && (tagClass1 == tagClass2)) { 164 throw new Asn1DecodingException( 165 "CHOICE fields are indistinguishable because they have the same tag" 166 + " class and number: " + containerClass.getName() 167 + "." + f1.getField().getName() 168 + " and ." + f2.getField().getName()); 169 } 170 } 171 } 172 173 // Instantiate the container object / result 174 T obj; 175 try { 176 obj = containerClass.getConstructor().newInstance(); 177 } catch (IllegalArgumentException | ReflectiveOperationException e) { 178 throw new Asn1DecodingException("Failed to instantiate " + containerClass.getName(), e); 179 } 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 List<AnnotatedField> fields = getAnnotatedFields(containerClass); 198 Collections.sort( 199 fields, (f1, f2) -> f1.getAnnotation().index() - f2.getAnnotation().index()); 200 // Check that there are no fields with the same index 201 if (fields.size() > 1) { 202 AnnotatedField lastField = null; 203 for (AnnotatedField field : fields) { 204 if ((lastField != null) 205 && (lastField.getAnnotation().index() == field.getAnnotation().index())) { 206 throw new Asn1DecodingException( 207 "Fields have the same index: " + containerClass.getName() 208 + "." + lastField.getField().getName() 209 + " and ." + field.getField().getName()); 210 } 211 lastField = field; 212 } 213 } 214 215 // Instantiate the container object / result 216 T t; 217 try { 218 t = containerClass.getConstructor().newInstance(); 219 } catch (IllegalArgumentException | ReflectiveOperationException e) { 220 throw new Asn1DecodingException("Failed to instantiate " + containerClass.getName(), e); 221 } 222 223 // Parse fields one by one. A complication is that there may be optional fields. 224 int nextUnreadFieldIndex = 0; 225 BerDataValueReader elementsReader = container.contentsReader(); 226 while (nextUnreadFieldIndex < fields.size()) { 227 BerDataValue dataValue; 228 try { 229 dataValue = elementsReader.readDataValue(); 230 } catch (BerDataValueFormatException e) { 231 throw new Asn1DecodingException("Malformed data value", e); 232 } 233 if (dataValue == null) { 234 break; 235 } 236 237 for (int i = nextUnreadFieldIndex; i < fields.size(); i++) { 238 AnnotatedField field = fields.get(i); 239 try { 240 if (field.isOptional()) { 241 // Optional field -- might not be present and we may thus be trying to set 242 // it from the wrong tag. 243 try { 244 field.setValueFrom(dataValue, t); 245 nextUnreadFieldIndex = i + 1; 246 break; 247 } catch (Asn1UnexpectedTagException e) { 248 // This field is not present, attempt to use this data value for the 249 // next / iteration of the loop 250 continue; 251 } 252 } else { 253 // Mandatory field -- if we can't set its value from this data value, then 254 // it's an error 255 field.setValueFrom(dataValue, t); 256 nextUnreadFieldIndex = i + 1; 257 break; 258 } 259 } catch (Asn1DecodingException e) { 260 throw new Asn1DecodingException( 261 "Failed to parse " + containerClass.getName() 262 + "." + field.getField().getName(), 263 e); 264 } 265 } 266 } 267 268 return t; 269 } 270 271 // NOTE: This method returns List rather than Set because ASN.1 SET_OF does require uniqueness 272 // of elements -- it's an unordered collection. 273 @SuppressWarnings("unchecked") parseSetOf(BerDataValue container, Class<T> elementClass)274 private static <T> List<T> parseSetOf(BerDataValue container, Class<T> elementClass) 275 throws Asn1DecodingException { 276 List<T> result = new ArrayList<>(); 277 BerDataValueReader elementsReader = container.contentsReader(); 278 while (true) { 279 BerDataValue dataValue; 280 try { 281 dataValue = elementsReader.readDataValue(); 282 } catch (BerDataValueFormatException e) { 283 throw new Asn1DecodingException("Malformed data value", e); 284 } 285 if (dataValue == null) { 286 break; 287 } 288 T element; 289 if (ByteBuffer.class.equals(elementClass)) { 290 element = (T) dataValue.getEncodedContents(); 291 } else if (Asn1OpaqueObject.class.equals(elementClass)) { 292 element = (T) new Asn1OpaqueObject(dataValue.getEncoded()); 293 } else { 294 element = parse(dataValue, elementClass); 295 } 296 result.add(element); 297 } 298 return result; 299 } 300 getContainerAsn1Type(Class<?> containerClass)301 private static Asn1Type getContainerAsn1Type(Class<?> containerClass) 302 throws Asn1DecodingException { 303 Asn1Class containerAnnotation = containerClass.getDeclaredAnnotation(Asn1Class.class); 304 if (containerAnnotation == null) { 305 throw new Asn1DecodingException( 306 containerClass.getName() + " is not annotated with " 307 + Asn1Class.class.getName()); 308 } 309 310 switch (containerAnnotation.type()) { 311 case CHOICE: 312 case SEQUENCE: 313 return containerAnnotation.type(); 314 default: 315 throw new Asn1DecodingException( 316 "Unsupported ASN.1 container annotation type: " 317 + containerAnnotation.type()); 318 } 319 } 320 getElementType(Field field)321 private static Class<?> getElementType(Field field) 322 throws Asn1DecodingException, ClassNotFoundException { 323 String type = field.getGenericType().getTypeName(); 324 int delimiterIndex = type.indexOf('<'); 325 if (delimiterIndex == -1) { 326 throw new Asn1DecodingException("Not a container type: " + field.getGenericType()); 327 } 328 int startIndex = delimiterIndex + 1; 329 int endIndex = type.indexOf('>', startIndex); 330 // TODO: handle comma? 331 if (endIndex == -1) { 332 throw new Asn1DecodingException("Not a container type: " + field.getGenericType()); 333 } 334 String elementClassName = type.substring(startIndex, endIndex); 335 return Class.forName(elementClassName); 336 } 337 338 private static final class AnnotatedField { 339 private final Field mField; 340 private final Asn1Field mAnnotation; 341 private final Asn1Type mDataType; 342 private final Asn1TagClass mTagClass; 343 private final int mBerTagClass; 344 private final int mBerTagNumber; 345 private final Asn1Tagging mTagging; 346 private final boolean mOptional; 347 AnnotatedField(Field field, Asn1Field annotation)348 public AnnotatedField(Field field, Asn1Field annotation) throws Asn1DecodingException { 349 mField = field; 350 mAnnotation = annotation; 351 mDataType = annotation.type(); 352 353 Asn1TagClass tagClass = annotation.cls(); 354 if (tagClass == Asn1TagClass.AUTOMATIC) { 355 if (annotation.tagNumber() != -1) { 356 tagClass = Asn1TagClass.CONTEXT_SPECIFIC; 357 } else { 358 tagClass = Asn1TagClass.UNIVERSAL; 359 } 360 } 361 mTagClass = tagClass; 362 mBerTagClass = BerEncoding.getTagClass(mTagClass); 363 364 int tagNumber; 365 if (annotation.tagNumber() != -1) { 366 tagNumber = annotation.tagNumber(); 367 } else if ((mDataType == Asn1Type.CHOICE) || (mDataType == Asn1Type.ANY)) { 368 tagNumber = -1; 369 } else { 370 tagNumber = BerEncoding.getTagNumber(mDataType); 371 } 372 mBerTagNumber = tagNumber; 373 374 mTagging = annotation.tagging(); 375 if (((mTagging == Asn1Tagging.EXPLICIT) || (mTagging == Asn1Tagging.IMPLICIT)) 376 && (annotation.tagNumber() == -1)) { 377 throw new Asn1DecodingException( 378 "Tag number must be specified when tagging mode is " + mTagging); 379 } 380 381 mOptional = annotation.optional(); 382 } 383 getField()384 public Field getField() { 385 return mField; 386 } 387 getAnnotation()388 public Asn1Field getAnnotation() { 389 return mAnnotation; 390 } 391 isOptional()392 public boolean isOptional() { 393 return mOptional; 394 } 395 getBerTagClass()396 public int getBerTagClass() { 397 return mBerTagClass; 398 } 399 getBerTagNumber()400 public int getBerTagNumber() { 401 return mBerTagNumber; 402 } 403 setValueFrom(BerDataValue dataValue, Object obj)404 public void setValueFrom(BerDataValue dataValue, Object obj) throws Asn1DecodingException { 405 int readTagClass = dataValue.getTagClass(); 406 if (mBerTagNumber != -1) { 407 int readTagNumber = dataValue.getTagNumber(); 408 if ((readTagClass != mBerTagClass) || (readTagNumber != mBerTagNumber)) { 409 throw new Asn1UnexpectedTagException( 410 "Tag mismatch. Expected: " 411 + BerEncoding.tagClassAndNumberToString(mBerTagClass, mBerTagNumber) 412 + ", but found " 413 + BerEncoding.tagClassAndNumberToString(readTagClass, readTagNumber)); 414 } 415 } else { 416 if (readTagClass != mBerTagClass) { 417 throw new Asn1UnexpectedTagException( 418 "Tag mismatch. Expected class: " 419 + BerEncoding.tagClassToString(mBerTagClass) 420 + ", but found " 421 + BerEncoding.tagClassToString(readTagClass)); 422 } 423 } 424 425 if (mTagging == Asn1Tagging.EXPLICIT) { 426 try { 427 dataValue = dataValue.contentsReader().readDataValue(); 428 } catch (BerDataValueFormatException e) { 429 throw new Asn1DecodingException( 430 "Failed to read contents of EXPLICIT data value", e); 431 } 432 } 433 434 BerToJavaConverter.setFieldValue(obj, mField, mDataType, dataValue); 435 } 436 } 437 438 private static class Asn1UnexpectedTagException extends Asn1DecodingException { 439 private static final long serialVersionUID = 1L; 440 Asn1UnexpectedTagException(String message)441 public Asn1UnexpectedTagException(String message) { 442 super(message); 443 } 444 } 445 oidToString(ByteBuffer encodedOid)446 private static String oidToString(ByteBuffer encodedOid) throws Asn1DecodingException { 447 if (!encodedOid.hasRemaining()) { 448 throw new Asn1DecodingException("Empty OBJECT IDENTIFIER"); 449 } 450 451 // First component encodes the first two nodes, X.Y, as X * 40 + Y, with 0 <= X <= 2 452 long firstComponent = decodeBase128UnsignedLong(encodedOid); 453 int firstNode = (int) Math.min(firstComponent / 40, 2); 454 long secondNode = firstComponent - firstNode * 40; 455 StringBuilder result = new StringBuilder(); 456 result.append(Long.toString(firstNode)).append('.') 457 .append(Long.toString(secondNode)); 458 459 // Each consecutive node is encoded as a separate component 460 while (encodedOid.hasRemaining()) { 461 long node = decodeBase128UnsignedLong(encodedOid); 462 result.append('.').append(Long.toString(node)); 463 } 464 465 return result.toString(); 466 } 467 decodeBase128UnsignedLong(ByteBuffer encoded)468 private static long decodeBase128UnsignedLong(ByteBuffer encoded) throws Asn1DecodingException { 469 if (!encoded.hasRemaining()) { 470 return 0; 471 } 472 long result = 0; 473 while (encoded.hasRemaining()) { 474 if (result > Long.MAX_VALUE >>> 7) { 475 throw new Asn1DecodingException("Base-128 number too large"); 476 } 477 int b = encoded.get() & 0xff; 478 result <<= 7; 479 result |= b & 0x7f; 480 if ((b & 0x80) == 0) { 481 return result; 482 } 483 } 484 throw new Asn1DecodingException( 485 "Truncated base-128 encoded input: missing terminating byte, with highest bit not" 486 + " set"); 487 } 488 integerToBigInteger(ByteBuffer encoded)489 private static BigInteger integerToBigInteger(ByteBuffer encoded) { 490 if (!encoded.hasRemaining()) { 491 return BigInteger.ZERO; 492 } 493 return new BigInteger(ByteBufferUtils.toByteArray(encoded)); 494 } 495 integerToInt(ByteBuffer encoded)496 private static int integerToInt(ByteBuffer encoded) throws Asn1DecodingException { 497 BigInteger value = integerToBigInteger(encoded); 498 try { 499 return value.intValueExact(); 500 } catch (ArithmeticException e) { 501 throw new Asn1DecodingException( 502 String.format("INTEGER cannot be represented as int: %1$d (0x%1$x)", value), e); 503 } 504 } 505 integerToLong(ByteBuffer encoded)506 private static long integerToLong(ByteBuffer encoded) throws Asn1DecodingException { 507 BigInteger value = integerToBigInteger(encoded); 508 try { 509 return value.longValueExact(); 510 } catch (ArithmeticException e) { 511 throw new Asn1DecodingException( 512 String.format("INTEGER cannot be represented as long: %1$d (0x%1$x)", value), 513 e); 514 } 515 } 516 getAnnotatedFields(Class<?> containerClass)517 private static List<AnnotatedField> getAnnotatedFields(Class<?> containerClass) 518 throws Asn1DecodingException { 519 Field[] declaredFields = containerClass.getDeclaredFields(); 520 List<AnnotatedField> result = new ArrayList<>(declaredFields.length); 521 for (Field field : declaredFields) { 522 Asn1Field annotation = field.getDeclaredAnnotation(Asn1Field.class); 523 if (annotation == null) { 524 continue; 525 } 526 if (Modifier.isStatic(field.getModifiers())) { 527 throw new Asn1DecodingException( 528 Asn1Field.class.getName() + " used on a static field: " 529 + containerClass.getName() + "." + field.getName()); 530 } 531 532 AnnotatedField annotatedField; 533 try { 534 annotatedField = new AnnotatedField(field, annotation); 535 } catch (Asn1DecodingException e) { 536 throw new Asn1DecodingException( 537 "Invalid ASN.1 annotation on " 538 + containerClass.getName() + "." + field.getName(), 539 e); 540 } 541 result.add(annotatedField); 542 } 543 return result; 544 } 545 546 private static final class BerToJavaConverter { BerToJavaConverter()547 private BerToJavaConverter() {} 548 setFieldValue( Object obj, Field field, Asn1Type type, BerDataValue dataValue)549 public static void setFieldValue( 550 Object obj, Field field, Asn1Type type, BerDataValue dataValue) 551 throws Asn1DecodingException { 552 try { 553 switch (type) { 554 case SET_OF: 555 case SEQUENCE_OF: 556 if (Asn1OpaqueObject.class.equals(field.getType())) { 557 field.set(obj, convert(type, dataValue, field.getType())); 558 } else { 559 field.set(obj, parseSetOf(dataValue, getElementType(field))); 560 } 561 return; 562 default: 563 field.set(obj, convert(type, dataValue, field.getType())); 564 break; 565 } 566 } catch (ReflectiveOperationException e) { 567 throw new Asn1DecodingException( 568 "Failed to set value of " + obj.getClass().getName() 569 + "." + field.getName(), 570 e); 571 } 572 } 573 574 private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; 575 576 @SuppressWarnings("unchecked") convert( Asn1Type sourceType, BerDataValue dataValue, Class<T> targetType)577 public static <T> T convert( 578 Asn1Type sourceType, 579 BerDataValue dataValue, 580 Class<T> targetType) throws Asn1DecodingException { 581 if (ByteBuffer.class.equals(targetType)) { 582 return (T) dataValue.getEncodedContents(); 583 } else if (byte[].class.equals(targetType)) { 584 ByteBuffer resultBuf = dataValue.getEncodedContents(); 585 if (!resultBuf.hasRemaining()) { 586 return (T) EMPTY_BYTE_ARRAY; 587 } 588 byte[] result = new byte[resultBuf.remaining()]; 589 resultBuf.get(result); 590 return (T) result; 591 } else if (Asn1OpaqueObject.class.equals(targetType)) { 592 return (T) new Asn1OpaqueObject(dataValue.getEncoded()); 593 } 594 595 ByteBuffer encodedContents = dataValue.getEncodedContents(); 596 switch (sourceType) { 597 case INTEGER: 598 if ((int.class.equals(targetType)) || (Integer.class.equals(targetType))) { 599 return (T) Integer.valueOf(integerToInt(encodedContents)); 600 } else if ((long.class.equals(targetType)) || (Long.class.equals(targetType))) { 601 return (T) Long.valueOf(integerToLong(encodedContents)); 602 } else if (BigInteger.class.equals(targetType)) { 603 return (T) integerToBigInteger(encodedContents); 604 } 605 break; 606 case OBJECT_IDENTIFIER: 607 if (String.class.equals(targetType)) { 608 return (T) oidToString(encodedContents); 609 } 610 break; 611 case SEQUENCE: 612 { 613 Asn1Class containerAnnotation = 614 targetType.getDeclaredAnnotation(Asn1Class.class); 615 if ((containerAnnotation != null) 616 && (containerAnnotation.type() == Asn1Type.SEQUENCE)) { 617 return parseSequence(dataValue, targetType); 618 } 619 break; 620 } 621 case CHOICE: 622 { 623 Asn1Class containerAnnotation = 624 targetType.getDeclaredAnnotation(Asn1Class.class); 625 if ((containerAnnotation != null) 626 && (containerAnnotation.type() == Asn1Type.CHOICE)) { 627 return parseChoice(dataValue, targetType); 628 } 629 break; 630 } 631 default: 632 break; 633 } 634 635 throw new Asn1DecodingException( 636 "Unsupported conversion: ASN.1 " + sourceType + " to " + targetType.getName()); 637 } 638 } 639 } 640