content: /*
   *  Licensed to the Apache Software Foundation (ASF) under one or more
   *  contributor license agreements.  See the NOTICE file distributed with
   *  this work for additional information regarding copyright ownership.
   *  The ASF licenses this file to You under the Apache License, Version 2.0
   *  (the "License"); you may not use this file except in compliance with
   *  the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   *  Unless required by applicable law or agreed to in writing, software
   *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   */
  
  /**
  * @author Vladimir N. Molotkov, Alexander Y. Kleymenov
  * @version $Revision$
  */
  
  package android.framework.org.apache.harmony.security_custom.x509;
  
  import java.io.IOException;
  import java.net.InetAddress;
  import java.net.URI;
  import java.net.URISyntaxException;
  import java.net.UnknownHostException;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.List;
  import java.util.Locale;
  import javax.security.auth.x500.X500Principal;
  import android.framework.org.apache.harmony.security_custom.asn1.ASN1Choice;
  import android.framework.org.apache.harmony.security_custom.asn1.ASN1Implicit;
  import android.framework.org.apache.harmony.security_custom.asn1.ASN1OctetString;
  import android.framework.org.apache.harmony.security_custom.asn1.ASN1Oid;
  import android.framework.org.apache.harmony.security_custom.asn1.ASN1StringType;
  import android.framework.org.apache.harmony.security_custom.asn1.ASN1Type;
  import android.framework.org.apache.harmony.security_custom.asn1.BerInputStream;
  import android.framework.org.apache.harmony.security_custom.asn1.ObjectIdentifier;
  import android.framework.org.apache.harmony.security_custom.utils.Array;
  import android.framework.org.apache.harmony.security_custom.x501.Name;
  
  /**
   * The class encapsulates the ASN.1 DER encoding/decoding work
   * with the GeneralName structure which is a part of X.509 certificate
   * (as specified in RFC 3280 -
   *  Internet X.509 Public Key Infrastructure.
   *  Certificate and Certificate Revocation List (CRL) Profile.
   *  http://www.ietf.org/rfc/rfc3280.txt):
   *
   * <pre>
   *
   *   GeneralName::= CHOICE {
   *        otherName                       [0]     OtherName,
   *        rfc822Name                      [1]     IA5String,
   *        dNSName                         [2]     IA5String,
   *        x400Address                     [3]     ORAddress,
   *        directoryName                   [4]     Name,
   *        ediPartyName                    [5]     EDIPartyName,
   *        uniformResourceIdentifier       [6]     IA5String,
   *        iPAddress                       [7]     OCTET STRING,
   *        registeredID                    [8]     OBJECT IDENTIFIER
   *   }
   *
   *   OtherName::= SEQUENCE {
   *        type-id    OBJECT IDENTIFIER,
   *        value      [0] EXPLICIT ANY DEFINED BY type-id
   *   }
   *
   *   EDIPartyName::= SEQUENCE {
   *        nameAssigner            [0]     DirectoryString OPTIONAL,
   *        partyName               [1]     DirectoryString
   *   }
   *
   *   DirectoryString::= CHOICE {
   *        teletexString             TeletexString   (SIZE (1..MAX)),
   *        printableString           PrintableString (SIZE (1..MAX)),
   *        universalString           UniversalString (SIZE (1..MAX)),
   *        utf8String              UTF8String      (SIZE (1..MAX)),
   *        bmpString               BMPString       (SIZE (1..MAX))
   *   }
   *
   * </pre>
   *
   * <p>This class doesn't support masked addresses like "10.9.8.0/255.255.255.0".
   * These are only necessary for NameConstraints, which are not exposed in the
   * Java certificate API.
   *
   * @see android.framework.org.apache.harmony.security_custom.x509.NameConstraints
   * @see android.framework.org.apache.harmony.security_custom.x509.GeneralSubtree
   */
  public final class GeneralName {
  
      /**
       * The values of the tags of fields
       */
      public static final int OTHER_NAME = 0;
      public static final int RFC822_NAME = 1;
      public static final int DNS_NAME = 2;
      public static final int X400_ADDR = 3;
      public static final int DIR_NAME = 4;
      public static final int EDIP_NAME = 5;
      public static final int UR_ID = 6;
      public static final int IP_ADDR = 7;
      public static final int REG_ID = 8;
  
      // ASN1 encoders/decoders for name choices
      private static ASN1Type[] nameASN1 = new ASN1Type[9];
  
      static {
          nameASN1[OTHER_NAME] = OtherName.ASN1;
          nameASN1[RFC822_NAME] = ASN1StringType.IA5STRING;
          nameASN1[DNS_NAME] = ASN1StringType.IA5STRING;
          nameASN1[UR_ID] = ASN1StringType.IA5STRING;
          nameASN1[X400_ADDR] = ORAddress.ASN1;
          nameASN1[DIR_NAME] = Name.ASN1;
          nameASN1[EDIP_NAME] = EDIPartyName.ASN1;
          nameASN1[IP_ADDR] = ASN1OctetString.getInstance();
          nameASN1[REG_ID] = ASN1Oid.getInstance();
      }
  
      /** the tag of the name type */
      private int tag;
      /** the name value (can be String or byte array) */
      private Object name;
      /** the ASN.1 encoded form of GeneralName */
      private byte[] encoding;
      /** the ASN.1 encoded form of GeneralName's field */
      private byte[] name_encoding;
  
      /**
       * Makes the GeneralName object from the tag type and corresponding
       * well established string representation of the name value.
       * The String representation of [7] iPAddress is such as:
       *  For IP v4, as specified in RFC 791, the address must
       *  contain exactly 4 byte component.  For IP v6, as specified in
       *  RFC 1883, the address must contain exactly 16 byte component.
       *  If GeneralName structure is used as a part of Name Constraints
       *  extension, to represent an address range the number of address
       *  component is doubled (to 8 and 32 bytes respectively).
       * Note that the names:
       * [0] otherName, [3] x400Address, [5] ediPartyName
       *   have no the string representation, so exception will be thrown.
       * To make the GeneralName object with such names use another constructor.
       * @param tag is an integer which value corresponds to the name type.
       * @param name is a name value corresponding to the tag.
       */
      public GeneralName(int tag, String name) throws IOException {
          if (name == null) {
              throw new IOException("name == null");
          }
          this.tag = tag;
          switch (tag) {
              case OTHER_NAME :
              case X400_ADDR :
              case EDIP_NAME :
                  throw new IOException("Unknown string representation for type [" + tag + "]");
              case DNS_NAME :
                  // according to RFC 3280 p.34 the DNS name should be
                  // checked against the
                  // RFC 1034 p.10 (3.5. Preferred name syntax):
                  checkDNS(name);
                  this.name = name;
                  break;
              case UR_ID :
                  // check the uniformResourceIdentifier for correctness
                  // according to RFC 3280 p.34
                  checkURI(name);
                  this.name = name;
                  break;
              case RFC822_NAME :
                  this.name = name;
                  break;
              case REG_ID:
                  this.name = oidStrToInts(name);
                  break;
              case DIR_NAME :
                  this.name = new Name(name);
                  break;
              case IP_ADDR :
                  this.name = ipStrToBytes(name);
                  break;
              default:
                  throw new IOException("Unknown type: [" + tag + "]");
          }
      }
  
      public GeneralName(OtherName name) {
          this.tag = OTHER_NAME;
          this.name = name;
      }
  
      public GeneralName(ORAddress name) {
          this.tag = X400_ADDR;
          this.name = name;
      }
  
      public GeneralName(Name name) {
          this.tag = DIR_NAME;
          this.name = name;
      }
  
      public GeneralName(EDIPartyName name) {
          this.tag = EDIP_NAME;
          this.name = name;
      }
      /**
       * Constructor for type [7] iPAddress.
       * name is an array of bytes such as:
       *  For IP v4, as specified in RFC 791, the address must
       *  contain exactly 4 byte component.  For IP v6, as specified in
       *  RFC 1883, the address must contain exactly 16 byte component.
       *  If GeneralName structure is used as a part of Name Constraints
       *  extension, to represent an address range the number of address
       *  component is doubled (to 8 and 32 bytes respectively).
       */
      public GeneralName(byte[] name) throws IllegalArgumentException {
          int length = name.length;
          if (length != 4 && length != 8 && length != 16 && length != 32) {
              throw new IllegalArgumentException("name.length invalid");
          }
          this.tag = IP_ADDR;
          this.name = new byte[name.length];
          System.arraycopy(name, 0, this.name, 0, name.length);
      }
  
      /**
       * Constructs an object representing the value of GeneralName.
       * @param tag is an integer which value corresponds
       * to the name type (0-8),
       * @param name is a DER encoded for of the name value
       */
      public GeneralName(int tag, byte[] name) throws IOException {
          if (name == null) {
              throw new NullPointerException("name == null");
          }
          if ((tag < 0) || (tag > 8)) {
              throw new IOException("GeneralName: unknown tag: " + tag);
          }
          this.tag = tag;
          this.name_encoding = new byte[name.length];
          System.arraycopy(name, 0, this.name_encoding, 0, name.length);
          this.name = nameASN1[tag].decode(this.name_encoding);
      }
  
      /**
       * Returns the tag of the name in the structure
       */
      public int getTag() {
          return tag;
      }
  
      /**
       * @return the value of the name.
       * The class of name object depends on the tag as follows:
       * [0] otherName - OtherName object,
       * [1] rfc822Name - String object,
       * [2] dNSName - String object,
       * [3] x400Address - ORAddress object,
       * [4] directoryName - instance of Name object,
       * [5] ediPartyName - EDIPartyName object,
       * [6] uniformResourceIdentifier - String object,
       * [7] iPAddress - array of bytes such as:
       *  For IP v4, as specified in RFC 791, the address must
       *  contain exactly 4 byte component.  For IP v6, as specified in
       *  RFC 1883, the address must contain exactly 16 byte component.
       *  If GeneralName structure is used as a part of Name Constraints
       *  extension, to represent an address range the number of address
       *  component is doubled (to 8 and 32 bytes respectively).
       * [8] registeredID - String.
       */
      public Object getName() {
          return name;
      }
  
      public boolean equals(Object other) {
          if (!(other instanceof GeneralName)) {
              return false;
          }
          GeneralName gname = (GeneralName) other;
          if (this.tag != gname.tag) {
              return false;
          }
          switch(tag) {
              case RFC822_NAME:
              case DNS_NAME:
              case UR_ID:
                  return ((String) name).equalsIgnoreCase(
                          (String) gname.getName());
              case REG_ID:
                  return Arrays.equals((int[]) name, (int[]) gname.name);
              case IP_ADDR:
                  // iPAddress [7], check by using ranges.
                  return Arrays.equals((byte[]) name, (byte[]) gname.name);
              case DIR_NAME:
              case X400_ADDR:
              case OTHER_NAME:
              case EDIP_NAME:
                  return Arrays.equals(getEncoded(), gname.getEncoded());
              default:
                  // should never happen
          }
          return false;
      }
  
      public int hashCode() {
          switch (tag) {
          case RFC822_NAME:
          case DNS_NAME:
          case UR_ID:
          case REG_ID:
          case IP_ADDR:
              return name.hashCode();
          case DIR_NAME:
          case X400_ADDR:
          case OTHER_NAME:
          case EDIP_NAME:
              return Arrays.hashCode(getEncoded());
          default:
              return super.hashCode();
          }
      }
  
      /**
       * Checks if the other general name is acceptable by this object.
       * The name is acceptable if it has the same type name and its
       * name value is equal to name value of this object. Also the name
       * is acceptable if this general name object is a part of name
       * constraints and the specified name is satisfied the restriction
       * provided by this object (for more detail see section 4.2.1.11
       * of rfc 3280).
       * Note that for X400Address [3] check procedure is unclear so method
       * just checks the equality of encoded forms.
       * For otherName [0], ediPartyName [5], and registeredID [8]
       * the check procedure if not defined by rfc 3280 and for names of
       * these types this method also checks only for equality of encoded forms.
       */
      public boolean isAcceptable(GeneralName gname) {
          if (this.tag != gname.getTag()) {
              return false;
          }
          switch (this.tag) {
              case RFC822_NAME:
                  // Mail address [1]:
                  // a@b.c - particular address is acceptable by the same address,
                  // or by b.c - host name.
                  return ((String) gname.getName()).toLowerCase(Locale.US)
                      .endsWith(((String) name).toLowerCase(Locale.US));
              case DNS_NAME:
                  // DNS name [2] that can be constructed by simply adding
                  // to the left hand side of the name satisfies the name
                  // constraint: aaa.aa.aa satisfies to aaa.aa.aa, aa.aa, ..
                  String dns = (String) name;
                  String _dns = (String) gname.getName();
                  if (dns.equalsIgnoreCase(_dns)) {
                      return true;
                  } else {
                      return _dns.toLowerCase(Locale.US).endsWith("." + dns.toLowerCase(Locale.US));
                  }
              case UR_ID:
                  // For URIs the constraint ".xyz.com" is satisfied by both
                  // abc.xyz.com and abc.def.xyz.com.  However, the constraint
                  // ".xyz.com" is not satisfied by "xyz.com".
                  // When the constraint does not begin with a period, it
                  // specifies a host.
                  // Extract the host from URI:
                  String uri = (String) name;
                  int begin = uri.indexOf("://")+3;
                  int end = uri.indexOf('/', begin);
                  String host = (end == -1)
                                  ? uri.substring(begin)
                                  : uri.substring(begin, end);
                  uri = (String) gname.getName();
                  begin = uri.indexOf("://")+3;
                  end = uri.indexOf('/', begin);
                  String _host = (end == -1)
                                  ? uri.substring(begin)
                                  : uri.substring(begin, end);
                  if (host.startsWith(".")) {
                      return _host.toLowerCase(Locale.US).endsWith(host.toLowerCase(Locale.US));
                  } else {
                      return host.equalsIgnoreCase(_host);
                  }
              case IP_ADDR:
                  // iPAddress [7], check by using ranges.
                  byte[] address = (byte[]) name;
                  byte[] _address = (byte[]) gname.getName();
                  int length = address.length;
                  int _length = _address.length;
                  if (length == _length) {
                      return Arrays.equals(address, _address);
                  } else if (length == 2*_length) {
                      for (int i = 0; i < _address.length; i++) {
                          // TODO: should the 2nd IP address be treated as a range or as a mask?
                          int octet = _address[i] & 0xff;
                          int min = address[i] & 0xff;
                          int max = address[i + _length] & 0xff;
                          if ((octet < min) || (octet > max)) {
                              return false;
                          }
                      }
                      return true;
                  } else {
                      return false;
                  }
              case DIR_NAME:
                  // FIXME: false:
                  // directoryName according to 4.1.2.4
                  // comparing the encoded forms of the names
                  //TODO:
                  //Legacy implementations exist where an RFC 822 name
                  //is embedded in the subject distinguished name in an
                  //attribute of type EmailAddress
              case X400_ADDR:
              case OTHER_NAME:
              case EDIP_NAME:
              case REG_ID:
                  return Arrays.equals(getEncoded(), gname.getEncoded());
              default:
                  // should never happen
          }
          return true;
      }
  
      /**
       * Gets a list representation of this GeneralName object.
       * The first entry of the list is an Integer object representing
       * the type of mane (0-8), and the second entry is a value of the name:
       * string or ASN.1 DER encoded form depending on the type as follows:
       * rfc822Name, dNSName, uniformResourceIdentifier names are returned
       * as Strings, using the string formats for those types (rfc 3280)
       * IP v4 address names are returned using dotted quad notation.
       * IP v6 address names are returned in the form "p1:p2:...:p8",
       * where p1-p8 are hexadecimal values representing the eight 16-bit
       * pieces of the address. registeredID name are returned as Strings
       * represented as a series of nonnegative integers separated by periods.
       * And directory names (distinguished names) are returned in
       * RFC 2253 string format.
       * otherName, X400Address, ediPartyName returned as byte arrays
       * containing the ASN.1 DER encoded form of the name.
       */
      public List<Object> getAsList() {
          ArrayList<Object> result = new ArrayList<Object>();
          result.add(tag);
          switch (tag) {
              case OTHER_NAME:
                  result.add(((OtherName) name).getEncoded());
                  break;
              case RFC822_NAME:
              case DNS_NAME:
              case UR_ID:
                  result.add(name); // String
                  break;
              case REG_ID:
                  result.add(ObjectIdentifier.toString((int[]) name));
                  break;
              case X400_ADDR:
                  result.add(((ORAddress) name).getEncoded());
                  break;
              case DIR_NAME: // directoryName is returned as a String
                  result.add(((Name) name).getName(X500Principal.RFC2253));
                  break;
              case EDIP_NAME:
                  result.add(((EDIPartyName) name).getEncoded());
                  break;
              case IP_ADDR: //iPAddress is returned as a String, not as a byte array
                  result.add(ipBytesToStr((byte[]) name));
                  break;
              default:
                  // should never happen
          }
          return Collections.unmodifiableList(result);
      }
  
      public String toString() {
          String result = "";
          switch (tag) {
              case OTHER_NAME:
                  result = "otherName[0]: "
                           + Array.getBytesAsString(getEncoded());
                  break;
              case RFC822_NAME:
                  result = "rfc822Name[1]: " + name;
                  break;
              case DNS_NAME:
                  result = "dNSName[2]: " + name;
                  break;
              case UR_ID:
                  result = "uniformResourceIdentifier[6]: " + name;
                  break;
              case REG_ID:
                  result = "registeredID[8]: " + ObjectIdentifier.toString((int[]) name);
                  break;
              case X400_ADDR:
                  result = "x400Address[3]: "
                           + Array.getBytesAsString(getEncoded());
                  break;
              case DIR_NAME:
                  result = "directoryName[4]: "
                           + ((Name) name).getName(X500Principal.RFC2253);
                  break;
              case EDIP_NAME:
                  result = "ediPartyName[5]: "
                           + Array.getBytesAsString(getEncoded());
                  break;
              case IP_ADDR:
                  result = "iPAddress[7]: " + ipBytesToStr((byte[]) name);
                  break;
              default:
                  // should never happen
          }
          return result;
      }
  
      /**
       * Returns ASN.1 encoded form of this X.509 GeneralName value.
       */
      public byte[] getEncoded() {
          if (encoding == null) {
              encoding = ASN1.encode(this);
          }
          return encoding;
      }
  
      /**
       * @return the encoded value of the name without the tag associated
       *         with the name in the GeneralName structure
       * @throws  IOException
       */
      public byte[] getEncodedName() {
          if (name_encoding == null) {
              name_encoding = nameASN1[tag].encode(name);
          }
          return name_encoding;
      }
  
      /**
       * Checks the correctness of the string representation of DNS name as
       * specified in RFC 1034 p. 10 and RFC 1123 section 2.1.
       *
       * <p>This permits a wildcard character '*' anywhere in the name; it is up
       * to the application to check which wildcards are permitted. See RFC 6125
       * for recommended wildcard matching rules.
       */
      public static void checkDNS(String dns) throws IOException {
          String string = dns.toLowerCase(Locale.US);
          int length = string.length();
          // indicates if it is a first letter of the label
          boolean first_letter = true;
          for (int i = 0; i < length; i++) {
              char ch = string.charAt(i);
              if (first_letter) {
                  if ((ch > 'z' || ch < 'a') && (ch < '0' || ch > '9') && (ch != '*')) {
                      throw new IOException("DNS name must start with a letter: " + dns);
                  }
                  first_letter = false;
                  continue;
              }
              if (!((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')
                      || (ch == '-') || (ch == '.') || (ch == '*'))) {
                  throw new IOException("Incorrect DNS name: " + dns);
              }
              if (ch == '.') {
                  // check the end of the previous label, it should not
                  // be '-' sign
                  if (string.charAt(i-1) == '-') {
                      throw new IOException("Incorrect DNS name: label ends with '-': " + dns);
                  }
                  first_letter = true;
              }
          }
      }
  
      /**
       * Checks the correctness of the string representation of URI name.
       * The correctness is checked as pointed out in RFC 3280 p. 34.
       */
      public static void checkURI(String uri) throws IOException {
          try {
              URI ur = new URI(uri);
              if (ur.getScheme() == null || ur.getRawSchemeSpecificPart().isEmpty()) {
                  throw new IOException("No scheme or scheme-specific-part in uniformResourceIdentifier: " + uri);
              }
              if (!ur.isAbsolute()) {
                  throw new IOException("Relative uniformResourceIdentifier: " + uri);
              }
          } catch (URISyntaxException e) {
              throw (IOException) new IOException("Bad representation of uniformResourceIdentifier: " + uri).initCause(e);
  
          }
      }
  
      /**
       * Converts OID into array of ints.
       */
      public static int[] oidStrToInts(String oid) throws IOException {
          int length = oid.length();
          if (oid.charAt(length-1) == '.') {
              throw new IOException("Bad OID: " + oid);
          }
          int[] result = new int[length/2+1]; // best case: a.b.c.d.e
          int number = 0; // the number of OID's components
          for (int i = 0; i < length; i++) {
              int value = 0;
              int pos = i;
              for (; i < length; i++) {
                  char ch = oid.charAt(i);
                  if ((ch < '0') || (ch > '9')) {
                      break;
                  }
                  value = 10 * value + (ch - '0');
              }
              if (i == pos) {
                  // the number was not read
                  throw new IOException("Bad OID: " + oid);
              }
              result[number++] = value;
              if (i == length) {
                  break;
              }
              char ch = oid.charAt(i);
              if (ch != '.') {
                  throw new IOException("Bad OID: " + oid);
              }
          }
          if (number < 2) {
              throw new IOException("OID should consist of no less than 2 components: " + oid);
          }
          return Arrays.copyOfRange(result, 0, number);
      }
  
      /**
       * Returns the bytes of the given IP address or masked IP address.
       */
      public static byte[] ipStrToBytes(String ip) throws IOException {
          return InetAddress.getByName(ip).getAddress();
      }
  
      /**
       * Returns the string form of the given IP address. Addresses of length 2x
       * the canonical length are treated as a route/mask pair.
       */
      public static String ipBytesToStr(byte[] ip) {
          try {
              return InetAddress.getByAddress(null, ip).getHostAddress();
          } catch (UnknownHostException e) {
              throw new IllegalArgumentException("Unexpected IP address: " + Arrays.toString(ip));
          }
      }
  
      public static final ASN1Choice ASN1 = new ASN1Choice(new ASN1Type[] {
             new ASN1Implicit(0, OtherName.ASN1),
             new ASN1Implicit(1, ASN1StringType.IA5STRING),
             new ASN1Implicit(2, ASN1StringType.IA5STRING),
             new ASN1Implicit(3, ORAddress.ASN1),
             new ASN1Implicit(4, Name.ASN1),
             new ASN1Implicit(5, EDIPartyName.ASN1),
             new ASN1Implicit(6, ASN1StringType.IA5STRING),
             new ASN1Implicit(7, ASN1OctetString.getInstance()),
             new ASN1Implicit(8, ASN1Oid.getInstance()) }) {
  
          public Object getObjectToEncode(Object value) {
              return ((GeneralName) value).name;
          }
  
          public int getIndex(java.lang.Object object) {
              return  ((GeneralName) object).tag;
          }
  
          @Override public Object getDecodedObject(BerInputStream in) throws IOException {
              GeneralName result;
              switch (in.choiceIndex) {
                  case OTHER_NAME: // OtherName
                      result = new GeneralName((OtherName) in.content);
                      break;
                  case RFC822_NAME: // rfc822Name
                  case DNS_NAME: // dNSName
                      result = new GeneralName(in.choiceIndex, (String) in.content);
                      break;
                  case X400_ADDR:
                      result = new GeneralName((ORAddress) in.content);
                      break;
                  case DIR_NAME: // directoryName (X.500 Name)
                      result = new GeneralName((Name) in.content);
                      break;
                  case EDIP_NAME: // ediPartyName
                      result = new GeneralName((EDIPartyName) in.content);
                      break;
                  case UR_ID: // uniformResourceIdentifier
                      String uri = (String) in.content;
                      if (uri.indexOf(":") == -1) {
                          throw new IOException("GeneralName: scheme is missing in URI: " + uri);
                      }
                      result = new GeneralName(in.choiceIndex, uri);
                      break;
                  case IP_ADDR: // iPAddress
                      result = new GeneralName((byte[]) in.content);
                      break;
                  case REG_ID: // registeredID
                      result = new GeneralName(in.choiceIndex,
                              ObjectIdentifier.toString((int[]) in.content));
                      break;
                  default:
                      throw new IOException("GeneralName: unknown tag: " + in.choiceIndex);
              }
              result.encoding = in.getEncoded();
              return result;
          }
      };
  }
  