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      */
GeneralName( X509Name dirName)66         public GeneralName(
67         X509Name  dirName)
68     {
69         this.obj = X500Name.getInstance(dirName);
70         this.tag = 4;
71     }
72 
GeneralName( X500Name dirName)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      */
GeneralName( int tag, ASN1Encodable name)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      */
GeneralName( int tag, String name)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 
getInstance( Object obj)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 
getInstance( ASN1TaggedObject tagObj, boolean explicit)225     public static GeneralName getInstance(
226         ASN1TaggedObject tagObj,
227         boolean          explicit)
228     {
229         return GeneralName.getInstance(ASN1TaggedObject.getInstance(tagObj, true));
230     }
231 
getTagNo()232     public int getTagNo()
233     {
234         return tag;
235     }
236 
getName()237     public ASN1Encodable getName()
238     {
239         return obj;
240     }
241 
toString()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 
toGeneralNameEncoding(String ip)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 
parseIPv4Mask(String mask, byte[] addr, int offset)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 
parseIPv4(String ip, byte[] addr, int offset)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 
parseMask(String mask)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 
copyInts(int[] parsedIp, byte[] addr, int offSet)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 
parseIPv6(String ip)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 
toASN1Primitive()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