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
23package org.apache.harmony.security.x509;
24
25import java.io.IOException;
26import java.net.InetAddress;
27import java.net.URI;
28import java.net.URISyntaxException;
29import java.net.UnknownHostException;
30import java.util.ArrayList;
31import java.util.Arrays;
32import java.util.Collection;
33import java.util.Collections;
34import java.util.List;
35import java.util.Locale;
36import javax.security.auth.x500.X500Principal;
37import org.apache.harmony.security.asn1.ASN1Choice;
38import org.apache.harmony.security.asn1.ASN1Implicit;
39import org.apache.harmony.security.asn1.ASN1OctetString;
40import org.apache.harmony.security.asn1.ASN1Oid;
41import org.apache.harmony.security.asn1.ASN1SequenceOf;
42import org.apache.harmony.security.asn1.ASN1StringType;
43import org.apache.harmony.security.asn1.ASN1Type;
44import org.apache.harmony.security.asn1.BerInputStream;
45import org.apache.harmony.security.asn1.ObjectIdentifier;
46import org.apache.harmony.security.utils.Array;
47import 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 */
98public 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@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