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