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.URI;
     27 import java.net.URISyntaxException;
     28 import java.util.ArrayList;
     29 import java.util.Arrays;
     30 import java.util.Collections;
     31 import java.util.List;
     32 
     33 import javax.security.auth.x500.X500Principal;
     34 
     35 import org.apache.harmony.security.asn1.ASN1Choice;
     36 import org.apache.harmony.security.asn1.ASN1Implicit;
     37 import org.apache.harmony.security.asn1.ASN1OctetString;
     38 import org.apache.harmony.security.asn1.ASN1Oid;
     39 import org.apache.harmony.security.asn1.ASN1StringType;
     40 import org.apache.harmony.security.asn1.ASN1Type;
     41 import org.apache.harmony.security.asn1.BerInputStream;
     42 import org.apache.harmony.security.asn1.ObjectIdentifier;
     43 import org.apache.harmony.security.internal.nls.Messages;
     44 import 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  */
     91 public 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 (at) 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