Home | History | Annotate | Download | only in x509
      1 package org.bouncycastle.asn1.x509;
      2 
      3 import java.io.IOException;
      4 import java.util.StringTokenizer;
      5 
      6 import org.bouncycastle.asn1.ASN1Choice;
      7 import org.bouncycastle.asn1.ASN1Encodable;
      8 import org.bouncycastle.asn1.ASN1Object;
      9 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
     10 import org.bouncycastle.asn1.ASN1OctetString;
     11 import org.bouncycastle.asn1.ASN1Primitive;
     12 import org.bouncycastle.asn1.ASN1Sequence;
     13 import org.bouncycastle.asn1.ASN1TaggedObject;
     14 import org.bouncycastle.asn1.DERIA5String;
     15 import org.bouncycastle.asn1.DEROctetString;
     16 import org.bouncycastle.asn1.DERTaggedObject;
     17 import org.bouncycastle.asn1.x500.X500Name;
     18 import org.bouncycastle.util.IPAddress;
     19 
     20 /**
     21  * The GeneralName object.
     22  * <pre>
     23  * GeneralName ::= CHOICE {
     24  *      otherName                       [0]     OtherName,
     25  *      rfc822Name                      [1]     IA5String,
     26  *      dNSName                         [2]     IA5String,
     27  *      x400Address                     [3]     ORAddress,
     28  *      directoryName                   [4]     Name,
     29  *      ediPartyName                    [5]     EDIPartyName,
     30  *      uniformResourceIdentifier       [6]     IA5String,
     31  *      iPAddress                       [7]     OCTET STRING,
     32  *      registeredID                    [8]     OBJECT IDENTIFIER}
     33  *
     34  * OtherName ::= SEQUENCE {
     35  *      type-id    OBJECT IDENTIFIER,
     36  *      value      [0] EXPLICIT ANY DEFINED BY type-id }
     37  *
     38  * EDIPartyName ::= SEQUENCE {
     39  *      nameAssigner            [0]     DirectoryString OPTIONAL,
     40  *      partyName               [1]     DirectoryString }
     41  *
     42  * Name ::= CHOICE { RDNSequence }
     43  * </pre>
     44  */
     45 public class GeneralName
     46     extends ASN1Object
     47     implements ASN1Choice
     48 {
     49     public static final int otherName                     = 0;
     50     public static final int rfc822Name                    = 1;
     51     public static final int dNSName                       = 2;
     52     public static final int x400Address                   = 3;
     53     public static final int directoryName                 = 4;
     54     public static final int ediPartyName                  = 5;
     55     public static final int uniformResourceIdentifier     = 6;
     56     public static final int iPAddress                     = 7;
     57     public static final int registeredID                  = 8;
     58 
     59     private ASN1Encodable obj;
     60     private int           tag;
     61 
     62     /**
     63      * @deprecated use X500Name constructor.
     64      * @param dirName
     65      */
     66         public GeneralName(
     67         X509Name  dirName)
     68     {
     69         this.obj = X500Name.getInstance(dirName);
     70         this.tag = 4;
     71     }
     72 
     73     public GeneralName(
     74         X500Name  dirName)
     75     {
     76         this.obj = dirName;
     77         this.tag = 4;
     78     }
     79 
     80     /**
     81      * When the subjectAltName extension contains an Internet mail address,
     82      * the address MUST be included as an rfc822Name. The format of an
     83      * rfc822Name is an "addr-spec" as defined in RFC 822 [RFC 822].
     84      *
     85      * When the subjectAltName extension contains a domain name service
     86      * label, the domain name MUST be stored in the dNSName (an IA5String).
     87      * The name MUST be in the "preferred name syntax," as specified by RFC
     88      * 1034 [RFC 1034].
     89      *
     90      * When the subjectAltName extension contains a URI, the name MUST be
     91      * stored in the uniformResourceIdentifier (an IA5String). The name MUST
     92      * be a non-relative URL, and MUST follow the URL syntax and encoding
     93      * rules specified in [RFC 1738].  The name must include both a scheme
     94      * (e.g., "http" or "ftp") and a scheme-specific-part.  The scheme-
     95      * specific-part must include a fully qualified domain name or IP
     96      * address as the host.
     97      *
     98      * When the subjectAltName extension contains a iPAddress, the address
     99      * MUST be stored in the octet string in "network byte order," as
    100      * specified in RFC 791 [RFC 791]. The least significant bit (LSB) of
    101      * each octet is the LSB of the corresponding byte in the network
    102      * address. For IP Version 4, as specified in RFC 791, the octet string
    103      * MUST contain exactly four octets.  For IP Version 6, as specified in
    104      * RFC 1883, the octet string MUST contain exactly sixteen octets [RFC
    105      * 1883].
    106      */
    107     public GeneralName(
    108         int           tag,
    109         ASN1Encodable name)
    110     {
    111         this.obj = name;
    112         this.tag = tag;
    113     }
    114 
    115     /**
    116      * Create a GeneralName for the given tag from the passed in String.
    117      * <p>
    118      * This constructor can handle:
    119      * <ul>
    120      * <li>rfc822Name
    121      * <li>iPAddress
    122      * <li>directoryName
    123      * <li>dNSName
    124      * <li>uniformResourceIdentifier
    125      * <li>registeredID
    126      * </ul>
    127      * For x400Address, otherName and ediPartyName there is no common string
    128      * format defined.
    129      * <p>
    130      * Note: A directory name can be encoded in different ways into a byte
    131      * representation. Be aware of this if the byte representation is used for
    132      * comparing results.
    133      *
    134      * @param tag tag number
    135      * @param name string representation of name
    136      * @throws IllegalArgumentException if the string encoding is not correct or     *             not supported.
    137      */
    138     public GeneralName(
    139         int       tag,
    140         String    name)
    141     {
    142         this.tag = tag;
    143 
    144         if (tag == rfc822Name || tag == dNSName || tag == uniformResourceIdentifier)
    145         {
    146             this.obj = new DERIA5String(name);
    147         }
    148         else if (tag == registeredID)
    149         {
    150             this.obj = new ASN1ObjectIdentifier(name);
    151         }
    152         else if (tag == directoryName)
    153         {
    154             this.obj = new X500Name(name);
    155         }
    156         else if (tag == iPAddress)
    157         {
    158             byte[] enc = toGeneralNameEncoding(name);
    159             if (enc != null)
    160             {
    161                 this.obj = new DEROctetString(enc);
    162             }
    163             else
    164             {
    165                 throw new IllegalArgumentException("IP Address is invalid");
    166             }
    167         }
    168         else
    169         {
    170             throw new IllegalArgumentException("can't process String for tag: " + tag);
    171         }
    172     }
    173 
    174     public static GeneralName getInstance(
    175         Object obj)
    176     {
    177         if (obj == null || obj instanceof GeneralName)
    178         {
    179             return (GeneralName)obj;
    180         }
    181 
    182         if (obj instanceof ASN1TaggedObject)
    183         {
    184             ASN1TaggedObject    tagObj = (ASN1TaggedObject)obj;
    185             int                 tag = tagObj.getTagNo();
    186 
    187             switch (tag)
    188             {
    189             case otherName:
    190                 return new GeneralName(tag, ASN1Sequence.getInstance(tagObj, false));
    191             case rfc822Name:
    192                 return new GeneralName(tag, DERIA5String.getInstance(tagObj, false));
    193             case dNSName:
    194                 return new GeneralName(tag, DERIA5String.getInstance(tagObj, false));
    195             case x400Address:
    196                 throw new IllegalArgumentException("unknown tag: " + tag);
    197             case directoryName:
    198                 return new GeneralName(tag, X500Name.getInstance(tagObj, true));
    199             case ediPartyName:
    200                 return new GeneralName(tag, ASN1Sequence.getInstance(tagObj, false));
    201             case uniformResourceIdentifier:
    202                 return new GeneralName(tag, DERIA5String.getInstance(tagObj, false));
    203             case iPAddress:
    204                 return new GeneralName(tag, ASN1OctetString.getInstance(tagObj, false));
    205             case registeredID:
    206                 return new GeneralName(tag, ASN1ObjectIdentifier.getInstance(tagObj, false));
    207             }
    208         }
    209 
    210         if (obj instanceof byte[])
    211         {
    212             try
    213             {
    214                 return getInstance(ASN1Primitive.fromByteArray((byte[])obj));
    215             }
    216             catch (IOException e)
    217             {
    218                 throw new IllegalArgumentException("unable to parse encoded general name");
    219             }
    220         }
    221 
    222         throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
    223     }
    224 
    225     public static GeneralName getInstance(
    226         ASN1TaggedObject tagObj,
    227         boolean          explicit)
    228     {
    229         return GeneralName.getInstance(ASN1TaggedObject.getInstance(tagObj, true));
    230     }
    231 
    232     public int getTagNo()
    233     {
    234         return tag;
    235     }
    236 
    237     public ASN1Encodable getName()
    238     {
    239         return obj;
    240     }
    241 
    242     public String toString()
    243     {
    244         StringBuffer buf = new StringBuffer();
    245 
    246         buf.append(tag);
    247         buf.append(": ");
    248         switch (tag)
    249         {
    250         case rfc822Name:
    251         case dNSName:
    252         case uniformResourceIdentifier:
    253             buf.append(DERIA5String.getInstance(obj).getString());
    254             break;
    255         case directoryName:
    256             buf.append(X500Name.getInstance(obj).toString());
    257             break;
    258         default:
    259             buf.append(obj.toString());
    260         }
    261         return buf.toString();
    262     }
    263 
    264     private byte[] toGeneralNameEncoding(String ip)
    265     {
    266         if (IPAddress.isValidIPv6WithNetmask(ip) || IPAddress.isValidIPv6(ip))
    267         {
    268             int    slashIndex = ip.indexOf('/');
    269 
    270             if (slashIndex < 0)
    271             {
    272                 byte[] addr = new byte[16];
    273                 int[]  parsedIp = parseIPv6(ip);
    274                 copyInts(parsedIp, addr, 0);
    275 
    276                 return addr;
    277             }
    278             else
    279             {
    280                 byte[] addr = new byte[32];
    281                 int[]  parsedIp = parseIPv6(ip.substring(0, slashIndex));
    282                 copyInts(parsedIp, addr, 0);
    283                 String mask = ip.substring(slashIndex + 1);
    284                 if (mask.indexOf(':') > 0)
    285                 {
    286                     parsedIp = parseIPv6(mask);
    287                 }
    288                 else
    289                 {
    290                     parsedIp = parseMask(mask);
    291                 }
    292                 copyInts(parsedIp, addr, 16);
    293 
    294                 return addr;
    295             }
    296         }
    297         else if (IPAddress.isValidIPv4WithNetmask(ip) || IPAddress.isValidIPv4(ip))
    298         {
    299             int    slashIndex = ip.indexOf('/');
    300 
    301             if (slashIndex < 0)
    302             {
    303                 byte[] addr = new byte[4];
    304 
    305                 parseIPv4(ip, addr, 0);
    306 
    307                 return addr;
    308             }
    309             else
    310             {
    311                 byte[] addr = new byte[8];
    312 
    313                 parseIPv4(ip.substring(0, slashIndex), addr, 0);
    314 
    315                 String mask = ip.substring(slashIndex + 1);
    316                 if (mask.indexOf('.') > 0)
    317                 {
    318                     parseIPv4(mask, addr, 4);
    319                 }
    320                 else
    321                 {
    322                     parseIPv4Mask(mask, addr, 4);
    323                 }
    324 
    325                 return addr;
    326             }
    327         }
    328 
    329         return null;
    330     }
    331 
    332     private void parseIPv4Mask(String mask, byte[] addr, int offset)
    333     {
    334         int   maskVal = Integer.parseInt(mask);
    335 
    336         for (int i = 0; i != maskVal; i++)
    337         {
    338             addr[(i / 8) + offset] |= 1 << (7 - (i % 8));
    339         }
    340     }
    341 
    342     private void parseIPv4(String ip, byte[] addr, int offset)
    343     {
    344         StringTokenizer sTok = new StringTokenizer(ip, "./");
    345         int    index = 0;
    346 
    347         while (sTok.hasMoreTokens())
    348         {
    349             addr[offset + index++] = (byte)Integer.parseInt(sTok.nextToken());
    350         }
    351     }
    352 
    353     private int[] parseMask(String mask)
    354     {
    355         int[] res = new int[8];
    356         int   maskVal = Integer.parseInt(mask);
    357 
    358         for (int i = 0; i != maskVal; i++)
    359         {
    360             res[i / 16] |= 1 << (15 - (i % 16));
    361         }
    362         return res;
    363     }
    364 
    365     private void copyInts(int[] parsedIp, byte[] addr, int offSet)
    366     {
    367         for (int i = 0; i != parsedIp.length; i++)
    368         {
    369             addr[(i * 2) + offSet] = (byte)(parsedIp[i] >> 8);
    370             addr[(i * 2 + 1) + offSet] = (byte)parsedIp[i];
    371         }
    372     }
    373 
    374     private int[] parseIPv6(String ip)
    375     {
    376         StringTokenizer sTok = new StringTokenizer(ip, ":", true);
    377         int index = 0;
    378         int[] val = new int[8];
    379 
    380         if (ip.charAt(0) == ':' && ip.charAt(1) == ':')
    381         {
    382            sTok.nextToken(); // skip the first one
    383         }
    384 
    385         int doubleColon = -1;
    386 
    387         while (sTok.hasMoreTokens())
    388         {
    389             String e = sTok.nextToken();
    390 
    391             if (e.equals(":"))
    392             {
    393                 doubleColon = index;
    394                 val[index++] = 0;
    395             }
    396             else
    397             {
    398                 if (e.indexOf('.') < 0)
    399                 {
    400                     val[index++] = Integer.parseInt(e, 16);
    401                     if (sTok.hasMoreTokens())
    402                     {
    403                         sTok.nextToken();
    404                     }
    405                 }
    406                 else
    407                 {
    408                     StringTokenizer eTok = new StringTokenizer(e, ".");
    409 
    410                     val[index++] = (Integer.parseInt(eTok.nextToken()) << 8) | Integer.parseInt(eTok.nextToken());
    411                     val[index++] = (Integer.parseInt(eTok.nextToken()) << 8) | Integer.parseInt(eTok.nextToken());
    412                 }
    413             }
    414         }
    415 
    416         if (index != val.length)
    417         {
    418             System.arraycopy(val, doubleColon, val, val.length - (index - doubleColon), index - doubleColon);
    419             for (int i = doubleColon; i != val.length - (index - doubleColon); i++)
    420             {
    421                 val[i] = 0;
    422             }
    423         }
    424 
    425         return val;
    426     }
    427 
    428     public ASN1Primitive toASN1Primitive()
    429     {
    430         if (tag == directoryName)       // directoryName is explicitly tagged as it is a CHOICE
    431         {
    432             return new DERTaggedObject(true, tag, obj);
    433         }
    434         else
    435         {
    436             return new DERTaggedObject(false, tag, obj);
    437         }
    438     }
    439 }
    440