Home | History | Annotate | Download | only in x509
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 /**
     19 * @author Vladimir N. Molotkov, Alexander Y. Kleymenov
     20 * @version $Revision$
     21 */
     22 
     23 package org.apache.harmony.security.x509;
     24 
     25 import java.io.IOException;
     26 import java.net.InetAddress;
     27 import java.net.URI;
     28 import java.net.URISyntaxException;
     29 import java.net.UnknownHostException;
     30 import java.util.ArrayList;
     31 import java.util.Arrays;
     32 import java.util.Collection;
     33 import java.util.Collections;
     34 import java.util.List;
     35 import java.util.Locale;
     36 import javax.security.auth.x500.X500Principal;
     37 import org.apache.harmony.security.asn1.ASN1Choice;
     38 import org.apache.harmony.security.asn1.ASN1Implicit;
     39 import org.apache.harmony.security.asn1.ASN1OctetString;
     40 import org.apache.harmony.security.asn1.ASN1Oid;
     41 import org.apache.harmony.security.asn1.ASN1SequenceOf;
     42 import org.apache.harmony.security.asn1.ASN1StringType;
     43 import org.apache.harmony.security.asn1.ASN1Type;
     44 import org.apache.harmony.security.asn1.BerInputStream;
     45 import org.apache.harmony.security.asn1.ObjectIdentifier;
     46 import org.apache.harmony.security.utils.Array;
     47 import org.apache.harmony.security.x501.Name;
     48 
     49 /**
     50  * The class encapsulates the ASN.1 DER encoding/decoding work
     51  * with the GeneralName structure which is a part of X.509 certificate
     52  * (as specified in RFC 3280 -
     53  *  Internet X.509 Public Key Infrastructure.
     54  *  Certificate and Certificate Revocation List (CRL) Profile.
     55  *  http://www.ietf.org/rfc/rfc3280.txt):
     56  *
     57  * <pre>
     58  *
     59  *   GeneralName::= CHOICE {
     60  *        otherName                       [0]     OtherName,
     61  *        rfc822Name                      [1]     IA5String,
     62  *        dNSName                         [2]     IA5String,
     63  *        x400Address                     [3]     ORAddress,
     64  *        directoryName                   [4]     Name,
     65  *        ediPartyName                    [5]     EDIPartyName,
     66  *        uniformResourceIdentifier       [6]     IA5String,
     67  *        iPAddress                       [7]     OCTET STRING,
     68  *        registeredID                    [8]     OBJECT IDENTIFIER
     69  *   }
     70  *
     71  *   OtherName::= SEQUENCE {
     72  *        type-id    OBJECT IDENTIFIER,
     73  *        value      [0] EXPLICIT ANY DEFINED BY type-id
     74  *   }
     75  *
     76  *   EDIPartyName::= SEQUENCE {
     77  *        nameAssigner            [0]     DirectoryString OPTIONAL,
     78  *        partyName               [1]     DirectoryString
     79  *   }
     80  *
     81  *   DirectoryString::= CHOICE {
     82  *        teletexString             TeletexString   (SIZE (1..MAX)),
     83  *        printableString           PrintableString (SIZE (1..MAX)),
     84  *        universalString           UniversalString (SIZE (1..MAX)),
     85  *        utf8String              UTF8String      (SIZE (1..MAX)),
     86  *        bmpString               BMPString       (SIZE (1..MAX))
     87  *   }
     88  *
     89  * </pre>
     90  *
     91  * <p>This class doesn't support masked addresses like "10.9.8.0/255.255.255.0".
     92  * These are only necessary for NameConstraints, which are not exposed in the
     93  * Java certificate API.
     94  *
     95  * @see org.apache.harmony.security.x509.NameConstraints
     96  * @see org.apache.harmony.security.x509.GeneralSubtree
     97  */
     98 public final class GeneralName {
     99 
    100     /**
    101      * The values of the tags of fields
    102      */
    103     public static final int OTHER_NAME = 0;
    104     public static final int RFC822_NAME = 1;
    105     public static final int DNS_NAME = 2;
    106     public static final int X400_ADDR = 3;
    107     public static final int DIR_NAME = 4;
    108     public static final int EDIP_NAME = 5;
    109     public static final int UR_ID = 6;
    110     public static final int IP_ADDR = 7;
    111     public static final int REG_ID = 8;
    112 
    113     // ASN1 encoders/decoders for name choices
    114     private static ASN1Type[] nameASN1 = new ASN1Type[9];
    115 
    116     static {
    117         nameASN1[OTHER_NAME] = OtherName.ASN1;
    118         nameASN1[RFC822_NAME] = ASN1StringType.IA5STRING;
    119         nameASN1[DNS_NAME] = ASN1StringType.IA5STRING;
    120         nameASN1[UR_ID] = ASN1StringType.IA5STRING;
    121         nameASN1[X400_ADDR] = ORAddress.ASN1;
    122         nameASN1[DIR_NAME] = Name.ASN1;
    123         nameASN1[EDIP_NAME] = EDIPartyName.ASN1;
    124         nameASN1[IP_ADDR] = ASN1OctetString.getInstance();
    125         nameASN1[REG_ID] = ASN1Oid.getInstance();
    126     }
    127 
    128     /** the tag of the name type */
    129     private int tag;
    130     /** the name value (can be String or byte array) */
    131     private Object name;
    132     /** the ASN.1 encoded form of GeneralName */
    133     private byte[] encoding;
    134     /** the ASN.1 encoded form of GeneralName's field */
    135     private byte[] name_encoding;
    136 
    137     /**
    138      * Makes the GeneralName object from the tag type and corresponding
    139      * well established string representation of the name value.
    140      * The String representation of [7] iPAddress is such as:
    141      *  For IP v4, as specified in RFC 791, the address must
    142      *  contain exactly 4 byte component.  For IP v6, as specified in
    143      *  RFC 1883, the address must contain exactly 16 byte component.
    144      *  If GeneralName structure is used as a part of Name Constraints
    145      *  extension, to represent an address range the number of address
    146      *  component is doubled (to 8 and 32 bytes respectively).
    147      * Note that the names:
    148      * [0] otherName, [3] x400Address, [5] ediPartyName
    149      *   have no the string representation, so exception will be thrown.
    150      * To make the GeneralName object with such names use another constructor.
    151      * @param tag is an integer which value corresponds to the name type.
    152      * @param name is a name value corresponding to the tag.
    153      */
    154     public GeneralName(int tag, String name) throws IOException {
    155         if (name == null) {
    156             throw new IOException("name == null");
    157         }
    158         this.tag = tag;
    159         switch (tag) {
    160             case OTHER_NAME :
    161             case X400_ADDR :
    162             case EDIP_NAME :
    163                 throw new IOException("Unknown string representation for type [" + tag + "]");
    164             case DNS_NAME :
    165                 // according to RFC 3280 p.34 the DNS name should be
    166                 // checked against the
    167                 // RFC 1034 p.10 (3.5. Preferred name syntax):
    168                 checkDNS(name);
    169                 this.name = name;
    170                 break;
    171             case UR_ID :
    172                 // check the uniformResourceIdentifier for correctness
    173                 // according to RFC 3280 p.34
    174                 checkURI(name);
    175                 this.name = name;
    176                 break;
    177             case RFC822_NAME :
    178                 this.name = name;
    179                 break;
    180             case REG_ID:
    181                 this.name = oidStrToInts(name);
    182                 break;
    183             case DIR_NAME :
    184                 this.name = new Name(name);
    185                 break;
    186             case IP_ADDR :
    187                 this.name = ipStrToBytes(name);
    188                 break;
    189             default:
    190                 throw new IOException("Unknown type: [" + tag + "]");
    191         }
    192     }
    193 
    194     public GeneralName(OtherName name) {
    195         this.tag = OTHER_NAME;
    196         this.name = name;
    197     }
    198 
    199     public GeneralName(ORAddress name) {
    200         this.tag = X400_ADDR;
    201         this.name = name;
    202     }
    203 
    204     public GeneralName(Name name) {
    205         this.tag = DIR_NAME;
    206         this.name = name;
    207     }
    208 
    209     public GeneralName(EDIPartyName name) {
    210         this.tag = EDIP_NAME;
    211         this.name = name;
    212     }
    213     /**
    214      * Constructor for type [7] iPAddress.
    215      * name is an array of bytes such as:
    216      *  For IP v4, as specified in RFC 791, the address must
    217      *  contain exactly 4 byte component.  For IP v6, as specified in
    218      *  RFC 1883, the address must contain exactly 16 byte component.
    219      *  If GeneralName structure is used as a part of Name Constraints
    220      *  extension, to represent an address range the number of address
    221      *  component is doubled (to 8 and 32 bytes respectively).
    222      */
    223     public GeneralName(byte[] name) throws IllegalArgumentException {
    224         this.tag = IP_ADDR;
    225         this.name = new byte[name.length];
    226         System.arraycopy(name, 0, this.name, 0, name.length);
    227     }
    228 
    229     /**
    230      * Constructs an object representing the value of GeneralName.
    231      * @param tag is an integer which value corresponds
    232      * to the name type (0-8),
    233      * @param name is a DER encoded for of the name value
    234      */
    235     public GeneralName(int tag, byte[] name) throws IOException {
    236         if (name == null) {
    237             throw new NullPointerException("name == null");
    238         }
    239         if ((tag < 0) || (tag > 8)) {
    240             throw new IOException("GeneralName: unknown tag: " + tag);
    241         }
    242         this.tag = tag;
    243         this.name_encoding = new byte[name.length];
    244         System.arraycopy(name, 0, this.name_encoding, 0, name.length);
    245         this.name = nameASN1[tag].decode(this.name_encoding);
    246     }
    247 
    248     /**
    249      * Returns the tag of the name in the structure
    250      */
    251     public int getTag() {
    252         return tag;
    253     }
    254 
    255     /**
    256      * @return the value of the name.
    257      * The class of name object depends on the tag as follows:
    258      * [0] otherName - OtherName object,
    259      * [1] rfc822Name - String object,
    260      * [2] dNSName - String object,
    261      * [3] x400Address - ORAddress object,
    262      * [4] directoryName - instance of Name object,
    263      * [5] ediPartyName - EDIPartyName object,
    264      * [6] uniformResourceIdentifier - String object,
    265      * [7] iPAddress - array of bytes such as:
    266      *  For IP v4, as specified in RFC 791, the address must
    267      *  contain exactly 4 byte component.  For IP v6, as specified in
    268      *  RFC 1883, the address must contain exactly 16 byte component.
    269      *  If GeneralName structure is used as a part of Name Constraints
    270      *  extension, to represent an address range the number of address
    271      *  component is doubled (to 8 and 32 bytes respectively).
    272      * [8] registeredID - String.
    273      */
    274     public Object getName() {
    275         return name;
    276     }
    277 
    278     public boolean equals(Object other) {
    279         if (!(other instanceof GeneralName)) {
    280             return false;
    281         }
    282         GeneralName gname = (GeneralName) other;
    283         if (this.tag != gname.tag) {
    284             return false;
    285         }
    286         switch(tag) {
    287             case RFC822_NAME:
    288             case DNS_NAME:
    289             case UR_ID:
    290                 return ((String) name).equalsIgnoreCase(
    291                         (String) gname.getName());
    292             case REG_ID:
    293                 return Arrays.equals((int[]) name, (int[]) gname.name);
    294             case IP_ADDR:
    295                 // iPAddress [7], check by using ranges.
    296                 return Arrays.equals((byte[]) name, (byte[]) gname.name);
    297             case DIR_NAME:
    298             case X400_ADDR:
    299             case OTHER_NAME:
    300             case EDIP_NAME:
    301                 return Arrays.equals(getEncoded(), gname.getEncoded());
    302             default:
    303                 // should never happen
    304         }
    305         return false;
    306     }
    307 
    308     public int hashCode() {
    309         switch (tag) {
    310         case RFC822_NAME:
    311         case DNS_NAME:
    312         case UR_ID:
    313         case REG_ID:
    314         case IP_ADDR:
    315             return name.hashCode();
    316         case DIR_NAME:
    317         case X400_ADDR:
    318         case OTHER_NAME:
    319         case EDIP_NAME:
    320             return Arrays.hashCode(getEncoded());
    321         default:
    322             return super.hashCode();
    323         }
    324     }
    325 
    326     /**
    327      * Checks if the other general name is acceptable by this object.
    328      * The name is acceptable if it has the same type name and its
    329      * name value is equal to name value of this object. Also the name
    330      * is acceptable if this general name object is a part of name
    331      * constraints and the specified name is satisfied the restriction
    332      * provided by this object (for more detail see section 4.2.1.11
    333      * of rfc 3280).
    334      * Note that for X400Address [3] check procedure is unclear so method
    335      * just checks the equality of encoded forms.
    336      * For otherName [0], ediPartyName [5], and registeredID [8]
    337      * the check procedure if not defined by rfc 3280 and for names of
    338      * these types this method also checks only for equality of encoded forms.
    339      */
    340     public boolean isAcceptable(GeneralName gname) {
    341         if (this.tag != gname.getTag()) {
    342             return false;
    343         }
    344         switch (this.tag) {
    345             case RFC822_NAME:
    346                 // Mail address [1]:
    347                 // a (at) b.c - particular address is acceptable by the same address,
    348                 // or by b.c - host name.
    349                 return ((String) gname.getName()).toLowerCase(Locale.US)
    350                     .endsWith(((String) name).toLowerCase(Locale.US));
    351             case DNS_NAME:
    352                 // DNS name [2] that can be constructed by simply adding
    353                 // to the left hand side of the name satisfies the name
    354                 // constraint: aaa.aa.aa satisfies to aaa.aa.aa, aa.aa, ..
    355                 String dns = (String) name;
    356                 String _dns = (String) gname.getName();
    357                 if (dns.equalsIgnoreCase(_dns)) {
    358                     return true;
    359                 } else {
    360                     return _dns.toLowerCase(Locale.US).endsWith("." + dns.toLowerCase(Locale.US));
    361                 }
    362             case UR_ID:
    363                 // For URIs the constraint ".xyz.com" is satisfied by both
    364                 // abc.xyz.com and abc.def.xyz.com.  However, the constraint
    365                 // ".xyz.com" is not satisfied by "xyz.com".
    366                 // When the constraint does not begin with a period, it
    367                 // specifies a host.
    368                 // Extract the host from URI:
    369                 String uri = (String) name;
    370                 int begin = uri.indexOf("://")+3;
    371                 int end = uri.indexOf('/', begin);
    372                 String host = (end == -1)
    373                                 ? uri.substring(begin)
    374                                 : uri.substring(begin, end);
    375                 uri = (String) gname.getName();
    376                 begin = uri.indexOf("://")+3;
    377                 end = uri.indexOf('/', begin);
    378                 String _host = (end == -1)
    379                                 ? uri.substring(begin)
    380                                 : uri.substring(begin, end);
    381                 if (host.startsWith(".")) {
    382                     return _host.toLowerCase(Locale.US).endsWith(host.toLowerCase(Locale.US));
    383                 } else {
    384                     return host.equalsIgnoreCase(_host);
    385                 }
    386             case IP_ADDR:
    387                 // iPAddress [7], check by using ranges.
    388                 byte[] address = (byte[]) name;
    389                 byte[] _address = (byte[]) gname.getName();
    390                 int length = address.length;
    391 
    392                 /*
    393                  * For IP v4, as specified in RFC 791, the address must contain
    394                  * exactly 4 byte component. For IP v6, as specified in RFC
    395                  * 1883, the address must contain exactly 16 byte component. If
    396                  * GeneralName structure is used as a part of Name Constraints
    397                  * extension, to represent an address range the number of
    398                  * address component is doubled (to 8 and 32 bytes
    399                  * respectively).
    400                  */
    401                 if (length != 4 && length != 8 && length != 16 && length != 32) {
    402                     return false;
    403                 }
    404 
    405                 int _length = _address.length;
    406                 if (length == _length) {
    407                     return Arrays.equals(address, _address);
    408                 } else if (length == 2*_length) {
    409                     for (int i = 0; i < _address.length; i++) {
    410                         // TODO: should the 2nd IP address be treated as a range or as a mask?
    411                         int octet = _address[i] & 0xff;
    412                         int min = address[i] & 0xff;
    413                         int max = address[i + _length] & 0xff;
    414                         if ((octet < min) || (octet > max)) {
    415                             return false;
    416                         }
    417                     }
    418                     return true;
    419                 } else {
    420                     return false;
    421                 }
    422             case DIR_NAME:
    423                 // FIXME: false:
    424                 // directoryName according to 4.1.2.4
    425                 // comparing the encoded forms of the names
    426                 //TODO:
    427                 //Legacy implementations exist where an RFC 822 name
    428                 //is embedded in the subject distinguished name in an
    429                 //attribute of type EmailAddress
    430             case X400_ADDR:
    431             case OTHER_NAME:
    432             case EDIP_NAME:
    433             case REG_ID:
    434                 return Arrays.equals(getEncoded(), gname.getEncoded());
    435             default:
    436                 // should never happen
    437         }
    438         return true;
    439     }
    440 
    441     /**
    442      * Gets a list representation of this GeneralName object.
    443      * The first entry of the list is an Integer object representing
    444      * the type of mane (0-8), and the second entry is a value of the name:
    445      * string or ASN.1 DER encoded form depending on the type as follows:
    446      * rfc822Name, dNSName, uniformResourceIdentifier names are returned
    447      * as Strings, using the string formats for those types (rfc 3280)
    448      * IP v4 address names are returned using dotted quad notation.
    449      * IP v6 address names are returned in the form "p1:p2:...:p8",
    450      * where p1-p8 are hexadecimal values representing the eight 16-bit
    451      * pieces of the address. registeredID name are returned as Strings
    452      * represented as a series of nonnegative integers separated by periods.
    453      * And directory names (distinguished names) are returned in
    454      * RFC 2253 string format.
    455      * otherName, X400Address, ediPartyName returned as byte arrays
    456      * containing the ASN.1 DER encoded form of the name.
    457      */
    458     public List<Object> getAsList() {
    459         ArrayList<Object> result = new ArrayList<Object>();
    460         result.add(tag);
    461         switch (tag) {
    462             case OTHER_NAME:
    463                 result.add(((OtherName) name).getEncoded());
    464                 break;
    465             case RFC822_NAME:
    466             case DNS_NAME:
    467             case UR_ID:
    468                 result.add(name); // String
    469                 break;
    470             case REG_ID:
    471                 result.add(ObjectIdentifier.toString((int[]) name));
    472                 break;
    473             case X400_ADDR:
    474                 result.add(((ORAddress) name).getEncoded());
    475                 break;
    476             case DIR_NAME: // directoryName is returned as a String
    477                 result.add(((Name) name).getName(X500Principal.RFC2253));
    478                 break;
    479             case EDIP_NAME:
    480                 result.add(((EDIPartyName) name).getEncoded());
    481                 break;
    482             case IP_ADDR: //iPAddress is returned as a String, not as a byte array
    483                 result.add(ipBytesToStr((byte[]) name));
    484                 break;
    485             default:
    486                 // should never happen
    487         }
    488         return Collections.unmodifiableList(result);
    489     }
    490 
    491     public String toString() {
    492         String result = "";
    493         switch (tag) {
    494             case OTHER_NAME:
    495                 result = "otherName[0]: "
    496                          + Array.getBytesAsString(getEncoded());
    497                 break;
    498             case RFC822_NAME:
    499                 result = "rfc822Name[1]: " + name;
    500                 break;
    501             case DNS_NAME:
    502                 result = "dNSName[2]: " + name;
    503                 break;
    504             case UR_ID:
    505                 result = "uniformResourceIdentifier[6]: " + name;
    506                 break;
    507             case REG_ID:
    508                 result = "registeredID[8]: " + ObjectIdentifier.toString((int[]) name);
    509                 break;
    510             case X400_ADDR:
    511                 result = "x400Address[3]: "
    512                          + Array.getBytesAsString(getEncoded());
    513                 break;
    514             case DIR_NAME:
    515                 result = "directoryName[4]: "
    516                          + ((Name) name).getName(X500Principal.RFC2253);
    517                 break;
    518             case EDIP_NAME:
    519                 result = "ediPartyName[5]: "
    520                          + Array.getBytesAsString(getEncoded());
    521                 break;
    522             case IP_ADDR:
    523                 result = "iPAddress[7]: " + ipBytesToStr((byte[]) name);
    524                 break;
    525             default:
    526                 // should never happen
    527         }
    528         return result;
    529     }
    530 
    531     /**
    532      * Returns ASN.1 encoded form of this X.509 GeneralName value.
    533      */
    534     public byte[] getEncoded() {
    535         if (encoding == null) {
    536             encoding = ASN1.encode(this);
    537         }
    538         return encoding;
    539     }
    540 
    541     /**
    542      * @return the encoded value of the name without the tag associated
    543      *         with the name in the GeneralName structure
    544      * @throws  IOException
    545      */
    546     public byte[] getEncodedName() {
    547         if (name_encoding == null) {
    548             name_encoding = nameASN1[tag].encode(name);
    549         }
    550         return name_encoding;
    551     }
    552 
    553     /**
    554      * Checks the correctness of the string representation of DNS name as
    555      * specified in RFC 1034 p. 10 and RFC 1123 section 2.1.
    556      *
    557      * <p>This permits a wildcard character '*' anywhere in the name; it is up
    558      * to the application to check which wildcards are permitted. See RFC 6125
    559      * for recommended wildcard matching rules.
    560      */
    561     public static void checkDNS(String dns) throws IOException {
    562         String string = dns.toLowerCase(Locale.US);
    563         int length = string.length();
    564         // indicates if it is a first letter of the label
    565         boolean first_letter = true;
    566         for (int i = 0; i < length; i++) {
    567             char ch = string.charAt(i);
    568             if (first_letter) {
    569                 if ((ch > 'z' || ch < 'a') && (ch < '0' || ch > '9') && (ch != '*')) {
    570                     throw new IOException("DNS name must start with a letter: " + dns);
    571                 }
    572                 first_letter = false;
    573                 continue;
    574             }
    575             if (!((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')
    576                     || (ch == '-') || (ch == '.') || (ch == '*'))) {
    577                 throw new IOException("Incorrect DNS name: " + dns);
    578             }
    579             if (ch == '.') {
    580                 // check the end of the previous label, it should not
    581                 // be '-' sign
    582                 if (string.charAt(i-1) == '-') {
    583                     throw new IOException("Incorrect DNS name: label ends with '-': " + dns);
    584                 }
    585                 first_letter = true;
    586             }
    587         }
    588     }
    589 
    590     /**
    591      * Checks the correctness of the string representation of URI name.
    592      * The correctness is checked as pointed out in RFC 3280 p. 34.
    593      */
    594     public static void checkURI(String uri) throws IOException {
    595         try {
    596             URI ur = new URI(uri);
    597             if (ur.getScheme() == null || ur.getRawSchemeSpecificPart().isEmpty()) {
    598                 throw new IOException("No scheme or scheme-specific-part in uniformResourceIdentifier: " + uri);
    599             }
    600             if (!ur.isAbsolute()) {
    601                 throw new IOException("Relative uniformResourceIdentifier: " + uri);
    602             }
    603         } catch (URISyntaxException e) {
    604             throw (IOException) new IOException("Bad representation of uniformResourceIdentifier: " + uri).initCause(e);
    605 
    606         }
    607     }
    608 
    609     /**
    610      * Converts OID into array of ints.
    611      */
    612     public static int[] oidStrToInts(String oid) throws IOException {
    613         int length = oid.length();
    614         if (oid.charAt(length-1) == '.') {
    615             throw new IOException("Bad OID: " + oid);
    616         }
    617         int[] result = new int[length/2+1]; // best case: a.b.c.d.e
    618         int number = 0; // the number of OID's components
    619         for (int i = 0; i < length; i++) {
    620             int value = 0;
    621             int pos = i;
    622             for (; i < length; i++) {
    623                 char ch = oid.charAt(i);
    624                 if ((ch < '0') || (ch > '9')) {
    625                     break;
    626                 }
    627                 value = 10 * value + (ch - '0');
    628             }
    629             if (i == pos) {
    630                 // the number was not read
    631                 throw new IOException("Bad OID: " + oid);
    632             }
    633             result[number++] = value;
    634             if (i == length) {
    635                 break;
    636             }
    637             char ch = oid.charAt(i);
    638             if (ch != '.') {
    639                 throw new IOException("Bad OID: " + oid);
    640             }
    641         }
    642         if (number < 2) {
    643             throw new IOException("OID should consist of no less than 2 components: " + oid);
    644         }
    645         return Arrays.copyOfRange(result, 0, number);
    646     }
    647 
    648     /**
    649      * Returns the bytes of the given IP address or masked IP address.
    650      */
    651     public static byte[] ipStrToBytes(String ip) throws IOException {
    652         if (!InetAddress.isNumeric(ip)) {
    653             throw new IOException("Not an IP address: " + ip);
    654         }
    655         return InetAddress.getByName(ip).getAddress();
    656     }
    657 
    658     /**
    659      * Returns the string form of the given IP address. If the address is not 4
    660      * octets for IPv4 or 16 octets for IPv6, an IllegalArgumentException will
    661      * be thrown.
    662      */
    663     public static String ipBytesToStr(byte[] ip) {
    664         try {
    665             return InetAddress.getByAddress(null, ip).getHostAddress();
    666         } catch (UnknownHostException e) {
    667             throw new IllegalArgumentException("Unexpected IP address: " + Arrays.toString(ip));
    668         }
    669     }
    670 
    671     /**
    672      * The "Name" is actually a CHOICE of one entry, so we need to pretend it's
    673      * a SEQUENCE OF and just grab the first entry.
    674      */
    675     private static final ASN1SequenceOf NAME_ASN1 = new ASN1SequenceOf(Name.ASN1) {
    676         @Override
    677         public Object decode(BerInputStream in) throws IOException {
    678             return ((List<?>) super.decode(in)).get(0);
    679         }
    680     };
    681 
    682     public static final ASN1Choice ASN1 = new ASN1Choice(new ASN1Type[] {
    683            new ASN1Implicit(0, OtherName.ASN1),
    684            new ASN1Implicit(1, ASN1StringType.IA5STRING),
    685            new ASN1Implicit(2, ASN1StringType.IA5STRING),
    686            new ASN1Implicit(3, ORAddress.ASN1),
    687            new ASN1Implicit(4, NAME_ASN1),
    688            new ASN1Implicit(5, EDIPartyName.ASN1),
    689            new ASN1Implicit(6, ASN1StringType.IA5STRING),
    690            new ASN1Implicit(7, ASN1OctetString.getInstance()),
    691            new ASN1Implicit(8, ASN1Oid.getInstance()) }) {
    692 
    693         public Object getObjectToEncode(Object value) {
    694             return ((GeneralName) value).name;
    695         }
    696 
    697         public int getIndex(java.lang.Object object) {
    698             return  ((GeneralName) object).tag;
    699         }
    700 
    701         @Override public Object getDecodedObject(BerInputStream in) throws IOException {
    702             GeneralName result;
    703             switch (in.choiceIndex) {
    704                 case OTHER_NAME: // OtherName
    705                     result = new GeneralName((OtherName) in.content);
    706                     break;
    707                 case RFC822_NAME: // rfc822Name
    708                 case DNS_NAME: // dNSName
    709                     result = new GeneralName(in.choiceIndex, (String) in.content);
    710                     break;
    711                 case X400_ADDR:
    712                     result = new GeneralName((ORAddress) in.content);
    713                     break;
    714                 case DIR_NAME: // directoryName (X.500 Name)
    715                     result = new GeneralName((Name) in.content);
    716                     break;
    717                 case EDIP_NAME: // ediPartyName
    718                     result = new GeneralName((EDIPartyName) in.content);
    719                     break;
    720                 case UR_ID: // uniformResourceIdentifier
    721                     String uri = (String) in.content;
    722                     if (uri.indexOf(":") == -1) {
    723                         throw new IOException("GeneralName: scheme is missing in URI: " + uri);
    724                     }
    725                     result = new GeneralName(in.choiceIndex, uri);
    726                     break;
    727                 case IP_ADDR: // iPAddress
    728                     result = new GeneralName((byte[]) in.content);
    729                     break;
    730                 case REG_ID: // registeredID
    731                     result = new GeneralName(in.choiceIndex,
    732                             ObjectIdentifier.toString((int[]) in.content));
    733                     break;
    734                 default:
    735                     throw new IOException("GeneralName: unknown tag: " + in.choiceIndex);
    736             }
    737             result.encoding = in.getEncoded();
    738             return result;
    739         }
    740     };
    741 }
    742