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.URI;
27import java.net.URISyntaxException;
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.Collections;
31import java.util.List;
32
33import javax.security.auth.x500.X500Principal;
34
35import org.apache.harmony.security.asn1.ASN1Choice;
36import org.apache.harmony.security.asn1.ASN1Implicit;
37import org.apache.harmony.security.asn1.ASN1OctetString;
38import org.apache.harmony.security.asn1.ASN1Oid;
39import org.apache.harmony.security.asn1.ASN1StringType;
40import org.apache.harmony.security.asn1.ASN1Type;
41import org.apache.harmony.security.asn1.BerInputStream;
42import org.apache.harmony.security.asn1.ObjectIdentifier;
43import org.apache.harmony.security.internal.nls.Messages;
44import org.apache.harmony.security.x501.Name;
45
46/**
47 * The class encapsulates the ASN.1 DER encoding/decoding work
48 * with the GeneralName structure which is a part of X.509 certificate
49 * (as specified in RFC 3280 -
50 *  Internet X.509 Public Key Infrastructure.
51 *  Certificate and Certificate Revocation List (CRL) Profile.
52 *  http://www.ietf.org/rfc/rfc3280.txt):
53 *
54 * <pre>
55 *
56 *   GeneralName::= CHOICE {
57 *        otherName                       [0]     OtherName,
58 *        rfc822Name                      [1]     IA5String,
59 *        dNSName                         [2]     IA5String,
60 *        x400Address                     [3]     ORAddress,
61 *        directoryName                   [4]     Name,
62 *        ediPartyName                    [5]     EDIPartyName,
63 *        uniformResourceIdentifier       [6]     IA5String,
64 *        iPAddress                       [7]     OCTET STRING,
65 *        registeredID                    [8]     OBJECT IDENTIFIER
66 *   }
67 *
68 *   OtherName::= SEQUENCE {
69 *        type-id    OBJECT IDENTIFIER,
70 *        value      [0] EXPLICIT ANY DEFINED BY type-id
71 *   }
72 *
73 *   EDIPartyName::= SEQUENCE {
74 *        nameAssigner            [0]     DirectoryString OPTIONAL,
75 *        partyName               [1]     DirectoryString
76 *   }
77 *
78 *   DirectoryString::= CHOICE {
79 *        teletexString             TeletexString   (SIZE (1..MAX)),
80 *        printableString           PrintableString (SIZE (1..MAX)),
81 *        universalString           UniversalString (SIZE (1..MAX)),
82 *        utf8String              UTF8String      (SIZE (1..MAX)),
83 *        bmpString               BMPString       (SIZE (1..MAX))
84 *   }
85 *
86 * </pre>
87 *
88 * @see org.apache.harmony.security.x509.NameConstraints
89 * @see org.apache.harmony.security.x509.GeneralSubtree
90 */
91public class GeneralName {
92
93    /**
94     * The values of the tags of fields
95     */
96    public static final int OTHER_NAME = 0;
97    public static final int RFC822_NAME = 1;
98    public static final int DNS_NAME = 2;
99    public static final int X400_ADDR = 3;
100    public static final int DIR_NAME = 4;
101    public static final int EDIP_NAME = 5;
102    public static final int UR_ID = 6;
103    public static final int IP_ADDR = 7;
104    public static final int REG_ID = 8;
105
106    // ASN1 encoders/decoders for name choices
107    private static ASN1Type[] nameASN1 = new ASN1Type[9];
108
109    static {
110        nameASN1[OTHER_NAME] = OtherName.ASN1;
111        nameASN1[RFC822_NAME] = ASN1StringType.IA5STRING;
112        nameASN1[DNS_NAME] = ASN1StringType.IA5STRING;
113        nameASN1[UR_ID] = ASN1StringType.IA5STRING;
114        nameASN1[X400_ADDR] = ORAddress.ASN1;
115        nameASN1[DIR_NAME] = Name.ASN1;
116        nameASN1[EDIP_NAME] = EDIPartyName.ASN1;
117        nameASN1[IP_ADDR] = ASN1OctetString.getInstance();
118        nameASN1[REG_ID] = ASN1Oid.getInstance();
119    }
120
121    // the tag of the name type
122    private int tag;
123    // the name value (can be String or byte array)
124    private Object name;
125    // the ASN.1 encoded form of GeneralName
126    private byte[] encoding;
127    // the ASN.1 encoded form of GeneralName's field
128    private byte[] name_encoding;
129
130    /**
131     * Makes the GeneralName object from the tag type and corresponding
132     * well established string representation of the name value.
133     * The String representation of [7] iPAddress is such as:
134     *  For IP v4, as specified in RFC 791, the address must
135     *  contain exactly 4 byte component.  For IP v6, as specified in
136     *  RFC 1883, the address must contain exactly 16 byte component.
137     *  If GeneralName structure is used as a part of Name Constraints
138     *  extension, to represent an address range the number of address
139     *  component is doubled (to 8 and 32 bytes respectively).
140     * Note that the names:
141     * [0] otherName, [3] x400Address, [5] ediPartyName
142     *   have no the string representation, so exception will be thrown.
143     * To make the GeneralName object with such names use another constructor.
144     * @param tag is an integer which value corresponds to the name type.
145     * @param name is a name value corresponding to the tag.
146     * <pre>
147     */
148    public GeneralName(int tag, String name) throws IOException {
149        if (name == null) {
150            throw new IOException(Messages.getString("security.28")); //$NON-NLS-1$
151        }
152        this.tag = tag;
153        switch (tag) {
154            case OTHER_NAME :
155            case X400_ADDR :
156            case EDIP_NAME :
157                throw new IOException( Messages.getString("security.180", tag )); //$NON-NLS-1$ //$NON-NLS-2$
158            case DNS_NAME :
159                // according to RFC 3280 p.34 the DNS name should be
160                // checked against the
161                // RFC 1034 p.10 (3.5. Preferred name syntax):
162                checkDNS(name);
163                this.name = name;
164                break;
165            case UR_ID :
166                // check the uniformResourceIdentifier for correctness
167                // according to RFC 3280 p.34
168                checkURI(name);
169                this.name = name;
170                break;
171            case RFC822_NAME :
172                this.name = name;
173                break;
174            case REG_ID:
175                this.name = oidStrToInts(name);
176                break;
177            case DIR_NAME :
178                this.name = new Name(name);
179                break;
180            case IP_ADDR :
181                this.name = ipStrToBytes(name);
182                break;
183            default:
184                throw new IOException(Messages.getString("security.181", tag)); //$NON-NLS-1$ //$NON-NLS-2$
185        }
186    }
187
188    /**
189     * TODO
190     * @param   name:   OtherName
191     */
192    public GeneralName(OtherName name) {
193        this.tag = OTHER_NAME;
194        this.name = name;
195    }
196
197    /**
198     * TODO
199     * @param   name:   ORAddress
200     */
201    public GeneralName(ORAddress name) {
202        this.tag = X400_ADDR;
203        this.name = name;
204    }
205
206    /**
207     * TODO
208     * @param   name:   Name
209     */
210    public GeneralName(Name name) {
211        this.tag = DIR_NAME;
212        this.name = name;
213    }
214
215    /**
216     * TODO
217     * @param   name:   EDIPartyName
218     */
219    public GeneralName(EDIPartyName name) {
220        this.tag = EDIP_NAME;
221        this.name = name;
222    }
223    /**
224     * Constructor for type [7] iPAddress.
225     * name is an array of bytes such as:
226     *  For IP v4, as specified in RFC 791, the address must
227     *  contain exactly 4 byte component.  For IP v6, as specified in
228     *  RFC 1883, the address must contain exactly 16 byte component.
229     *  If GeneralName structure is used as a part of Name Constraints
230     *  extension, to represent an address range the number of address
231     *  component is doubled (to 8 and 32 bytes respectively).
232     */
233    public GeneralName(byte[] name) throws IllegalArgumentException {
234        int length = name.length;
235        if (length != 4 && length != 8 && length != 16 && length != 32) {
236            throw new IllegalArgumentException(
237                    Messages.getString("security.182")); //$NON-NLS-1$
238        }
239        this.tag = IP_ADDR;
240        this.name = new byte[name.length];
241        System.arraycopy(name, 0, this.name, 0, name.length);
242    }
243
244    /**
245     * Constructs an object representing the value of GeneralName.
246     * @param tag is an integer which value corresponds
247     * to the name type (0-8),
248     * @param name is a DER encoded for of the name value
249     */
250    public GeneralName(int tag, byte[] name)
251                                    throws IOException {
252        if (name == null) {
253            throw new NullPointerException(Messages.getString("security.28")); //$NON-NLS-1$
254        }
255        if ((tag < 0) || (tag > 8)) {
256            throw new IOException(Messages.getString("security.183", tag)); //$NON-NLS-1$
257        }
258        this.tag = tag;
259        this.name_encoding = new byte[name.length];
260        System.arraycopy(name, 0, this.name_encoding, 0, name.length);
261        this.name = nameASN1[tag].decode(this.name_encoding);
262    }
263
264    /**
265     * Returns the tag of the name in the structure
266     * @return the tag of the name
267     */
268    public int getTag() {
269        return tag;
270    }
271
272    /**
273     * @return the value of the name.
274     * The class of name object depends on the tag as follows:
275     * [0] otherName - OtherName object,
276     * [1] rfc822Name - String object,
277     * [2] dNSName - String object,
278     * [3] x400Address - ORAddress object,
279     * [4] directoryName - instance of Name object,
280     * [5] ediPartyName - EDIPartyName object,
281     * [6] uniformResourceIdentifier - String object,
282     * [7] iPAddress - array of bytes such as:
283     *  For IP v4, as specified in RFC 791, the address must
284     *  contain exactly 4 byte component.  For IP v6, as specified in
285     *  RFC 1883, the address must contain exactly 16 byte component.
286     *  If GeneralName structure is used as a part of Name Constraints
287     *  extension, to represent an address range the number of address
288     *  component is doubled (to 8 and 32 bytes respectively).
289     * [8] registeredID - String.
290     */
291    public Object getName() {
292        return name;
293    }
294
295    /**
296     * TODO
297     * @param   _gname: Object
298     * @return
299     */
300    public boolean equals(Object _gname) {
301        if (!(_gname instanceof GeneralName)) {
302            return false;
303        }
304        GeneralName gname = (GeneralName) _gname;
305        if (this.tag != gname.tag) {
306            return false;
307        }
308        switch(tag) {
309            case RFC822_NAME:
310            case DNS_NAME:
311            case UR_ID:
312                return ((String) name).equalsIgnoreCase(
313                        (String) gname.getName());
314            case REG_ID:
315                return Arrays.equals((int[]) name, (int[]) gname.name);
316            case IP_ADDR:
317                // iPAddress [7], check by using ranges.
318                return Arrays.equals((byte[]) name, (byte[]) gname.name);
319            case DIR_NAME:
320            case X400_ADDR:
321            case OTHER_NAME:
322            case EDIP_NAME:
323                return Arrays.equals(getEncoded(), gname.getEncoded());
324            default:
325                // should never happen
326        }
327        //System.out.println(false);
328        return false;
329    }
330
331	public int hashCode() {
332		switch(tag) {
333	        case RFC822_NAME:
334	        case DNS_NAME:
335	        case UR_ID:
336	        case REG_ID:
337	        case IP_ADDR:
338	            return name.hashCode();
339	        case DIR_NAME:
340	        case X400_ADDR:
341	        case OTHER_NAME:
342	        case EDIP_NAME:
343	            return getEncoded().hashCode();
344	        default:
345	            return super.hashCode();
346		}
347	}
348
349    /**
350     * Checks if the other general name is acceptable by this object.
351     * The name is acceptable if it has the same type name and its
352     * name value is equal to name value of this object. Also the name
353     * is acceptable if this general name object is a part of name
354     * constraints and the specified name is satisfied the restriction
355     * provided by this object (for more detail see section 4.2.1.11
356     * of rfc 3280).
357     * Note that for X400Address [3] check procedure is unclear so method
358     * just checks the equality of encoded forms.
359     * For otherName [0], ediPartyName [5], and registeredID [8]
360     * the check procedure if not defined by rfc 3280 and for names of
361     * these types this method also checks only for equality of encoded forms.
362     */
363    public boolean isAcceptable(GeneralName gname) {
364        if (this.tag != gname.getTag()) {
365            return false;
366        }
367        switch (this.tag) {
368            case RFC822_NAME:
369                // Mail address [1]:
370                // a@b.c - particular address is acceptable by the same address,
371                // or by b.c - host name.
372                return ((String) gname.getName()).toLowerCase()
373                    .endsWith(((String) name).toLowerCase());
374            case DNS_NAME:
375                // DNS name [2] that can be constructed by simply adding
376                // to the left hand side of the name satisfies the name
377                // constraint: aaa.aa.aa satisfies to aaa.aa.aa, aa.aa, ..
378                String dns = (String) name;
379                String _dns = (String) gname.getName();
380                if (dns.equalsIgnoreCase(_dns)) {
381                    return true;
382                } else {
383                    return _dns.toLowerCase().endsWith("." + dns.toLowerCase()); //$NON-NLS-1$
384                }
385            case UR_ID:
386                // For URIs the constraint ".xyz.com" is satisfied by both
387                // abc.xyz.com and abc.def.xyz.com.  However, the constraint
388                // ".xyz.com" is not satisfied by "xyz.com".
389                // When the constraint does not begin with a period, it
390                // specifies a host.
391                // Extract the host from URI:
392                String uri = (String) name;
393                int begin = uri.indexOf("://")+3; //$NON-NLS-1$
394                int end = uri.indexOf('/', begin);
395                String host = (end == -1)
396                                ? uri.substring(begin)
397                                : uri.substring(begin, end);
398                uri = (String) gname.getName();
399                begin = uri.indexOf("://")+3; //$NON-NLS-1$
400                end = uri.indexOf('/', begin);
401                String _host = (end == -1)
402                                ? uri.substring(begin)
403                                : uri.substring(begin, end);
404                if (host.startsWith(".")) { //$NON-NLS-1$
405                    return _host.toLowerCase().endsWith(host.toLowerCase());
406                } else {
407                    return host.equalsIgnoreCase(_host);
408                }
409            case IP_ADDR:
410                // iPAddress [7], check by using ranges.
411                byte[] address = (byte[]) name;
412                byte[] _address = (byte[]) gname.getName();
413                int length = address.length;
414                int _length = _address.length;
415                if (length == _length) {
416                    return Arrays.equals(address, _address);
417                } else if (length == 2*_length) {
418                    for (int i=0; i<_address.length; i++) {
419                        if ((_address[i] < address[i])
420                                || (_address[i] > address[i+_length])) {
421                            return false;
422                        }
423                    }
424                    return true;
425                } else {
426                    return false;
427                }
428            case DIR_NAME:
429                // FIXME: false:
430                // directoryName according to 4.1.2.4
431                // comparing the encoded forms of the names
432                //TODO:
433                //Legacy implementations exist where an RFC 822 name
434                //is embedded in the subject distinguished name in an
435                //attribute of type EmailAddress
436            case X400_ADDR:
437            case OTHER_NAME:
438            case EDIP_NAME:
439            case REG_ID:
440                return Arrays.equals(getEncoded(), gname.getEncoded());
441            default:
442                // should never happen
443        }
444        return true;
445    }
446
447    /**
448     * Gets a list representation of this GeneralName object.
449     * The first entry of the list is an Integer object representing
450     * the type of mane (0-8), and the second entry is a value of the name:
451     * string or ASN.1 DER encoded form depending on the type as follows:
452     * rfc822Name, dNSName, uniformResourceIdentifier names are returned
453     * as Strings, using the string formats for those types (rfc 3280)
454     * IP v4 address names are returned using dotted quad notation.
455     * IP v6 address names are returned in the form "p1:p2:...:p8",
456     * where p1-p8 are hexadecimal values representing the eight 16-bit
457     * pieces of the address. registeredID name are returned as Strings
458     * represented as a series of nonnegative integers separated by periods.
459     * And directory names (distinguished names) are returned in
460     * RFC 2253 string format.
461     * otherName, X400Address, ediPartyName returned as byte arrays
462     * containing the ASN.1 DER encoded form of the name.
463     */
464    public List getAsList() {
465        ArrayList result = new ArrayList();
466        result.add(new Integer(tag));
467        switch (tag) {
468            case OTHER_NAME:
469                result.add(((OtherName) name).getEncoded());
470                break;
471            case RFC822_NAME:
472            case DNS_NAME:
473            case UR_ID:
474                result.add(name); // String
475                break;
476            case REG_ID:
477                result.add(ObjectIdentifier.toString((int[]) name));
478                break;
479            case X400_ADDR:
480                result.add(((ORAddress) name).getEncoded());
481                break;
482            case DIR_NAME: // directoryName is returned as a String
483                result.add(((Name) name).getName(X500Principal.RFC2253));
484                break;
485            case EDIP_NAME:
486                result.add(((EDIPartyName) name).getEncoded());
487                break;
488            case IP_ADDR: //iPAddress is returned as a String, not as a byte array
489                result.add(ipBytesToStr((byte[]) name));
490                break;
491            default:
492                // should never happen
493        }
494        return Collections.unmodifiableList(result);
495    }
496
497    //
498    // TODO
499    // @param   data:   byte[]
500    // @return
501    //
502    private String getBytesAsString(byte[] data) {
503        String result = ""; //$NON-NLS-1$
504        for (int i=0; i<data.length; i++) {
505            String tail = Integer.toHexString(0x00ff & data[i]);
506            if (tail.length() == 1) {
507                tail = "0" + tail;  //$NON-NLS-1$
508            }
509            result += tail + " "; //$NON-NLS-1$
510        }
511        return result;
512    }
513
514    /**
515     * TODO
516     * @return
517     */
518    public String toString() {
519        String result = ""; //$NON-NLS-1$
520        switch (tag) {
521            case OTHER_NAME:
522                result = "otherName[0]: "  //$NON-NLS-1$
523                         + getBytesAsString(getEncoded());
524                break;
525            case RFC822_NAME:
526                result = "rfc822Name[1]: " + name; //$NON-NLS-1$
527                break;
528            case DNS_NAME:
529                result = "dNSName[2]: " + name; //$NON-NLS-1$
530                break;
531            case UR_ID:
532                result = "uniformResourceIdentifier[6]: " + name; //$NON-NLS-1$
533                break;
534            case REG_ID:
535                result = "registeredID[8]: " + ObjectIdentifier.toString((int[]) name); //$NON-NLS-1$
536                break;
537            case X400_ADDR:
538                result = "x400Address[3]: "  //$NON-NLS-1$
539                         + getBytesAsString(getEncoded());
540                break;
541            case DIR_NAME:
542                result = "directoryName[4]: "  //$NON-NLS-1$
543                         + ((Name) name).getName(X500Principal.RFC2253);
544                break;
545            case EDIP_NAME:
546                result = "ediPartyName[5]: "  //$NON-NLS-1$
547                         + getBytesAsString(getEncoded());
548                break;
549            case IP_ADDR:
550                result = "iPAddress[7]: " + ipBytesToStr((byte[]) name); //$NON-NLS-1$
551                break;
552            default:
553                // should never happen
554        }
555        return result;
556    }
557
558    /**
559     * Returns ASN.1 encoded form of this X.509 GeneralName value.
560     * @return a byte array containing ASN.1 encode form.
561     */
562    public byte[] getEncoded() {
563        if (encoding == null) {
564            encoding = ASN1.encode(this);
565        }
566        return encoding;
567    }
568
569    /**
570     * @return the encoded value of the name without the tag associated
571     *         with the name in the GeneralName structure
572     * @throws  IOException
573     */
574    public byte[] getEncodedName() {
575        if (name_encoding == null) {
576            name_encoding = nameASN1[tag].encode(name);
577        }
578        return name_encoding;
579    }
580
581    /**
582     * Checks the correctness of the string representation of DNS name.
583     * The correctness is checked as specified in RFC 1034 p. 10, and modified
584     * by RFC 1123 (section 2.1).
585     */
586    public static void checkDNS(String dns) throws IOException {
587        byte[] bytes = dns.toLowerCase().getBytes("UTF-8"); //$NON-NLS-1$
588        // indicates if it is a first letter of the label
589        boolean first_letter = true;
590        for (int i=0; i<bytes.length; i++) {
591            byte ch = bytes[i];
592            if (first_letter) {
593                if ((bytes.length > 2) && (ch == '*') && (bytes[1] == '.')) {
594                    first_letter = false;
595                    continue;
596                }
597                if ((ch > 'z' || ch < 'a') && (ch < '0' || ch > '9')) {
598                    throw new IOException(Messages.getString("security.184", //$NON-NLS-1$
599                            (char)ch, dns));
600                }
601                first_letter = false;
602                continue;
603            }
604            if (!((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')
605                    || (ch == '-') || (ch == '.'))) {
606                throw new IOException(Messages.getString("security.185", dns)); //$NON-NLS-1$
607            }
608            if (ch == '.') {
609                // check the end of the previous label, it should not
610                // be '-' sign
611                if (bytes[i-1] == '-') {
612                    throw new IOException(
613                            Messages.getString("security.186", dns)); //$NON-NLS-1$
614                }
615                first_letter = true;
616            }
617        }
618    }
619
620    /**
621     * Checks the correctness of the string representation of URI name.
622     * The correctness is checked as pointed out in RFC 3280 p. 34.
623     */
624    public static void checkURI(String uri) throws IOException {
625        try {
626            URI ur = new URI(uri);
627            if ((ur.getScheme() == null)
628                    || (ur.getRawSchemeSpecificPart().length() == 0)) {
629                throw new IOException(Messages.getString("security.187", uri)); //$NON-NLS-1$
630            }
631            if (!ur.isAbsolute()) {
632                throw new IOException(Messages.getString("security.188", uri)); //$NON-NLS-1$
633            }
634        } catch (URISyntaxException e) {
635            throw (IOException) new IOException(
636                    Messages.getString("security.189", uri)).initCause(e);//$NON-NLS-1$
637
638        }
639    }
640
641    /**
642     * Converts OID into array of bytes.
643     */
644    public static int[] oidStrToInts(String oid) throws IOException {
645        byte[] bytes = oid.getBytes("UTF-8"); //$NON-NLS-1$
646        if (bytes[bytes.length-1] == '.') {
647            throw new IOException(Messages.getString("security.56", oid)); //$NON-NLS-1$
648        }
649        int[] result = new int[bytes.length/2+1]; // best case: a.b.c.d.e
650        int number = 0; // the number of OID's components
651        for (int i=0; i<bytes.length; i++) {
652            int value = 0;
653            int pos = i;
654            while ((i < bytes.length) && (bytes[i] >= '0')
655                        && (bytes[i] <= '9')) {
656                value = 10 * value + (bytes[i++] - 48);
657            }
658            if (i == pos) {
659                // the number was not read
660                throw new IOException(Messages.getString("security.56", oid)); //$NON-NLS-1$
661            }
662            result[number++] = value;
663            if (i >= bytes.length) {
664                break;
665            }
666            if (bytes[i] != '.') {
667                throw new IOException(Messages.getString("security.56", oid)); //$NON-NLS-1$
668            }
669        }
670        if (number < 2) {
671            throw new IOException(Messages.getString("security.18A", oid));//$NON-NLS-1$
672        }
673        int[] res = new int[number];
674        for (int i=0; i<number; i++) {
675            res[i] = result[i];
676        }
677        return res;
678    }
679
680    /**
681     * Helper method. Converts the String representation of IP address
682     * to the array of bytes. IP addresses are expected in two versions:<br>
683     * IPv4 - in dot-decimal notation<br>
684     * IPv6 - in colon hexadecimal notation<br>
685     * Also method works with the ranges of the addresses represented
686     * as 2 addresses separated by '/' character.
687     * @param   address :   String representation of IP address
688     * @return  byte representation of IP address
689     */
690    public static byte[] ipStrToBytes(String ip) throws IOException {
691        boolean isIPv4 = (ip.indexOf('.') > 0);
692        // number of components (should be 4 or 8)
693        int num_components = (isIPv4) ? 4 : 16;
694        if (ip.indexOf('/') > 0) {
695            num_components *= 2; // this is a range of addresses
696        }
697        // the resulting array
698        byte[] result = new byte[num_components];
699        byte[] ip_bytes = ip.getBytes("UTF-8"); //$NON-NLS-1$
700        // number of address component to be read
701        int component = 0;
702        // if it is reading the second bound of a range
703        boolean reading_second_bound = false;
704        if (isIPv4) {
705            // IPv4 address is expected in the form of dot-decimal notation:
706            //      1.100.2.200
707            // or in the range form:
708            //      1.100.2.200/1.100.3.300
709            int i = 0;
710            while (i < ip_bytes.length) {
711                int digits = 0;
712                // the value of the address component
713                int value = 0;
714                while ((i < ip_bytes.length) && (ip_bytes[i] >= '0')
715                        && (ip_bytes[i] <= '9')) {
716                    digits++;
717                    if (digits > 3) {
718                        throw new IOException(Messages.getString("security.18B", ip)); //$NON-NLS-1$
719                    }
720                    value = 10 * value + (ip_bytes[i] - 48);
721                    i++;
722                }
723                if (digits == 0) {
724                    // ip_bytes[i] is not a number
725                    throw new IOException(Messages.getString("security.18C", ip));//$NON-NLS-1$
726                }
727                result[component] = (byte) value;
728                component++;
729                if (i >= ip_bytes.length) {
730                    // no more bytes
731                    break;
732                }
733                // check the reached delimiter
734                if ((ip_bytes[i] != '.' && ip_bytes[i] != '/')) {
735                    throw new IOException(Messages.getString("security.18C", ip)); //$NON-NLS-1$
736                }
737                // check the correctness of the range
738                if (ip_bytes[i] == '/') {
739                    if (reading_second_bound) {
740                        // more than 2 bounds in the range
741                        throw new IOException(Messages.getString("security.18C", ip)); //$NON-NLS-1$
742                    }
743                    if (component != 4) {
744                        throw new IOException(Messages.getString("security.18D", ip)); //$NON-NLS-1$
745                    }
746                    reading_second_bound = true;
747                }
748                // check the number of the components
749                if (component > ((reading_second_bound) ? 7 : 3)) {
750                    throw new IOException(Messages.getString("security.18D", ip)); //$NON-NLS-1$
751                }
752                i++;
753            }
754            // check the number of read components
755            if (component != num_components) {
756                throw new IOException(Messages.getString("security.18D", ip)); //$NON-NLS-1$
757            }
758        } else {
759            // IPv6 address is expected in the form of
760            // colon hexadecimal notation:
761            // 010a:020b:3337:1000:FFFA:ABCD:9999:0000
762            // or in a range form:
763            // 010a:020b:3337:1000:FFFA:ABCD:9999:0000/010a:020b:3337:1000:FFFA:ABCD:9999:1111
764            if (ip_bytes.length != 39 && ip_bytes.length != 79) {
765                // incorrect length of the string representation
766                throw new IOException(Messages.getString("security.18E", ip)); //$NON-NLS-1$
767            }
768            int value = 0;
769            // indicates the reading of the second half of byte
770            boolean second_hex = false;
771            // if the delimiter (':' or '/') is expected
772            boolean expect_delimiter = false;
773            for (int i=0; i<ip_bytes.length; i++) {
774                byte bytik = ip_bytes[i];
775                if ((bytik >= '0') && (bytik <= '9')) {
776                    value = (bytik - 48); // '0':0, '1':1, ... , '9':9
777                } else if ((bytik >= 'A') && (bytik <= 'F')) {
778                    value = (bytik - 55); // 'A':10, 'B':11, ... , 'F':15
779                } else if ((bytik >= 'a') && (bytik <= 'f')) {
780                    value = (bytik - 87); // 'a':10, 'b':11, ... , 'f':15
781                } else if (second_hex) {
782                    // second hex value of a byte is expected but was not read
783                    // (it is the situation like: ...ABCD:A:ABCD...)
784                    throw new IOException(Messages.getString("security.18E", ip)); //$NON-NLS-1$
785                } else if ((bytik == ':') || (bytik == '/')) {
786                    if (component % 2 == 1) {
787                        // second byte of the component is omitted
788                        // (it is the situation like: ... ABDC:AB:ABCD ...)
789                        throw new IOException(Messages.getString("security.18E", ip)); //$NON-NLS-1$
790                    }
791                    if (bytik == '/') {
792                        if (reading_second_bound) {
793                            // more than 2 bounds in the range
794                            throw new IOException(
795                                    Messages.getString("security.18E", ip)); //$NON-NLS-1$
796                        }
797                        if (component != 16) {
798                            // check the number of read components
799                            throw new IOException(Messages.getString("security.18F", ip)); //$NON-NLS-1$
800                        }
801                        reading_second_bound = true;
802                    }
803                    expect_delimiter = false;
804                    continue;
805                } else {
806                    throw new IOException(Messages.getString("security.18E", ip)); //$NON-NLS-1$
807                }
808                if (expect_delimiter) { // delimiter is expected but was not read
809                    throw new IOException(Messages.getString("security.18E", ip)); //$NON-NLS-1$
810                }
811                if (!second_hex) {
812                    // first half of byte has been read
813                    result[component] = (byte) (value << 4);
814                    second_hex = true;
815                } else {
816                    // second half of byte has been read
817                    result[component] = (byte)
818                        ((result[component] & 0xFF) | value);
819                    // delimiter is expected if 2 bytes were read
820                    expect_delimiter = (component % 2 == 1);
821                    second_hex = false;
822                    component++;
823                }
824            }
825            // check the correctness of the read address:
826            if (second_hex || (component % 2 == 1)) {
827                throw new IOException(Messages.getString("security.18E", ip)); //$NON-NLS-1$
828            }
829        }
830        return result;
831    }
832
833
834    /**
835     * Helper method. Converts the byte array representation of ip address
836     * to the String.
837     * @param   ip :   byte array representation of ip address
838     *  If the length of byte array 4 then it represents an IP v4
839     *  and the output String will be in the dotted quad form.
840     *  If the length is 16 then it represents an IP v6
841     *  and the output String will be returned in format "p1:p2:...:p8",
842     *  where p1-p8 are hexadecimal values representing the eight 16-bit
843     *  pieces of the address.
844     *  If the length is 8 or 32 then it represents an address range (RFC 1519)
845     *  and the output String will contain 2 IP address divided by "/"
846     * @return  String representation of ip address
847     */
848    public static String ipBytesToStr(byte[] ip) {
849        String result = ""; //$NON-NLS-1$
850        if (ip.length < 9) { // IP v4
851            for (int i=0; i<ip.length; i++) {
852                result += Integer.toString(ip[i] & 0xff);
853                if (i != ip.length-1) {
854                    result += (i == 3) ? "/": "."; //$NON-NLS-1$ //$NON-NLS-2$
855                }
856            }
857        } else {
858            for (int i=0; i<ip.length; i++) {
859                result += Integer.toHexString(0x00ff & ip[i]);
860                if ((i % 2 != 0) && (i != ip.length-1)) {
861                    result += (i == 15) ? "/": ":"; //$NON-NLS-1$ //$NON-NLS-2$
862                }
863            }
864        }
865        return result;
866    }
867
868    public static final ASN1Choice ASN1 = new ASN1Choice(new ASN1Type[] {
869           new ASN1Implicit(0, OtherName.ASN1),
870           new ASN1Implicit(1, ASN1StringType.IA5STRING),
871           new ASN1Implicit(2, ASN1StringType.IA5STRING),
872           new ASN1Implicit(3, ORAddress.ASN1),
873           new ASN1Implicit(4, Name.ASN1),
874           new ASN1Implicit(5, EDIPartyName.ASN1),
875           new ASN1Implicit(6, ASN1StringType.IA5STRING),
876           new ASN1Implicit(7, ASN1OctetString.getInstance()),
877           new ASN1Implicit(8, ASN1Oid.getInstance()) }) {
878
879        public Object getObjectToEncode(Object value) {
880            return ((GeneralName) value).name;
881        }
882
883        public int getIndex(java.lang.Object object) {
884            return  ((GeneralName) object).tag;
885        }
886
887        public Object getDecodedObject(BerInputStream in) throws IOException {
888            GeneralName result;
889            switch (in.choiceIndex) {
890                case OTHER_NAME: // OtherName
891                    result = new GeneralName((OtherName) in.content);
892                    break;
893                case RFC822_NAME: // rfc822Name
894                case DNS_NAME: // dNSName
895                    result = new GeneralName(in.choiceIndex, (String) in.content);
896                    break;
897                case X400_ADDR:
898                    result = new GeneralName((ORAddress) in.content);
899                    break;
900                case DIR_NAME: // directoryName (X.500 Name)
901                    result = new GeneralName((Name) in.content);
902                    break;
903                case EDIP_NAME: // ediPartyName
904                    result = new GeneralName((EDIPartyName) in.content);
905                    break;
906                case UR_ID: // uniformResourceIdentifier
907                    String uri = (String) in.content;
908                    if (uri.indexOf(":") == -1) { //$NON-NLS-1$
909                        throw new IOException(
910                            Messages.getString("security.190", uri)); //$NON-NLS-1$
911                    }
912                    result = new GeneralName(in.choiceIndex, uri);
913                    break;
914                case IP_ADDR: // iPAddress
915                    result = new GeneralName((byte[]) in.content);
916                    break;
917                case REG_ID: // registeredID
918                    result = new GeneralName(in.choiceIndex,
919                            ObjectIdentifier.toString((int[]) in.content));
920                    break;
921                default:
922                    throw new IOException(Messages.getString("security.191", in.choiceIndex)); //$NON-NLS-1$
923            }
924            result.encoding = in.getEncoded();
925            return result;
926        }
927    };
928
929    // public static void printAsHex(int perLine,
930    //         String prefix,
931    //         String delimiter,
932    //         byte[] data) {
933    //     for (int i=0; i<data.length; i++) {
934    //         String tail = Integer.toHexString(0x000000ff & data[i]);
935    //         if (tail.length() == 1) {
936    //             tail = "0" + tail;
937    //         }
938    //         System.out.print(prefix + "0x" + tail + delimiter);
939
940    //         if (((i+1)%perLine) == 0) {
941    //             System.out.println();
942    //         }
943    //     }
944    //     System.out.println();
945    // }
946
947    // public static void main(String[] args) {
948    //     System.out.println(">> "+new BigInteger(new byte[] {(byte)23, (byte)255}).toString(2));
949    //     System.out.println(ipBytesToStr(new byte[] {(byte)255, (byte)23, (byte)128, (byte)130}));
950    //     System.out.println(ipBytesToStr(new byte[] {(byte)255, (byte)23, (byte)128, (byte)130,
951    //                                                 (byte)255, (byte)23, (byte)128, (byte)130}));
952    //     System.out.println(ipBytesToStr(new byte[] {(byte)255, (byte)23, (byte)128, (byte)130,
953    //                                                 (byte)255, (byte)23, (byte)128, (byte)130,
954    //                                                 (byte)255, (byte)23, (byte)128, (byte)130,
955    //                                                 (byte)255, (byte)23, (byte)128, (byte)130}));
956    //     System.out.println(ipBytesToStr(new byte[] {(byte)255, (byte)23, (byte)128, (byte)130,
957    //                                                 (byte)255, (byte)23, (byte)128, (byte)130,
958    //                                                 (byte)255, (byte)23, (byte)128, (byte)130,
959    //                                                 (byte)255, (byte)23, (byte)128, (byte)130,
960    //                                                 (byte)255, (byte)23, (byte)128, (byte)130,
961    //                                                 (byte)255, (byte)23, (byte)128, (byte)130,
962    //                                                 (byte)255, (byte)23, (byte)128, (byte)130,
963    //                                                 (byte)255, (byte)23, (byte)128, (byte)130}));
964    //     ipStrToBytes("1.2.3.4");
965    //     ipStrToBytes("1.2.3.4/4.3.2.1");
966    //     printAsHex(8, "", " ", ipStrToBytes("ff17:8082:ff17:8082:ff17:8082:ff17:8082/ff17:8082:ff17:8082:ff17:8082:ff17:8082"));
967    // }
968}
969