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