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