/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @author Vladimir N. Molotkov, Alexander Y. Kleymenov * @version $Revision$ */ /* * Imported by CG 20090321 based on Apache Harmony ("enhanced") revision 541025. */ package org.apache.harmony.security.x509; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import javax.security.auth.x500.X500Principal; import org.apache.harmony.security.asn1.ASN1Choice; import org.apache.harmony.security.asn1.ASN1Implicit; import org.apache.harmony.security.asn1.ASN1OctetString; import org.apache.harmony.security.asn1.ASN1Oid; import org.apache.harmony.security.asn1.ASN1StringType; import org.apache.harmony.security.asn1.ASN1Type; import org.apache.harmony.security.asn1.BerInputStream; import org.apache.harmony.security.asn1.ObjectIdentifier; import org.apache.harmony.security.x501.Name; /** * The class encapsulates the ASN.1 DER encoding/decoding work * with the GeneralName structure which is a part of X.509 certificate * (as specified in RFC 3280 - * Internet X.509 Public Key Infrastructure. * Certificate and Certificate Revocation List (CRL) Profile. * http://www.ietf.org/rfc/rfc3280.txt): * * <pre> * * GeneralName::= CHOICE { * otherName [0] OtherName, * rfc822Name [1] IA5String, * dNSName [2] IA5String, * x400Address [3] ORAddress, * directoryName [4] Name, * ediPartyName [5] EDIPartyName, * uniformResourceIdentifier [6] IA5String, * iPAddress [7] OCTET STRING, * registeredID [8] OBJECT IDENTIFIER * } * * OtherName::= SEQUENCE { * type-id OBJECT IDENTIFIER, * value [0] EXPLICIT ANY DEFINED BY type-id * } * * EDIPartyName::= SEQUENCE { * nameAssigner [0] DirectoryString OPTIONAL, * partyName [1] DirectoryString * } * * DirectoryString::= CHOICE { * teletexString TeletexString (SIZE (1..MAX)), * printableString PrintableString (SIZE (1..MAX)), * universalString UniversalString (SIZE (1..MAX)), * utf8String UTF8String (SIZE (1..MAX)), * bmpString BMPString (SIZE (1..MAX)) * } * * </pre> * * @see org.apache.harmony.security.x509.NameConstraints * @see org.apache.harmony.security.x509.GeneralSubtree */ public class GeneralName { /** * The values of the tags of fields */ public static final int OTHER_NAME = 0; public static final int RFC822_NAME = 1; public static final int DNS_NAME = 2; public static final int X400_ADDR = 3; public static final int DIR_NAME = 4; public static final int EDIP_NAME = 5; public static final int UR_ID = 6; public static final int IP_ADDR = 7; public static final int REG_ID = 8; // ASN1 encoders/decoders for name choices private static ASN1Type[] nameASN1 = new ASN1Type[9]; static { nameASN1[OTHER_NAME] = OtherName.ASN1; nameASN1[RFC822_NAME] = ASN1StringType.IA5STRING; nameASN1[DNS_NAME] = ASN1StringType.IA5STRING; nameASN1[UR_ID] = ASN1StringType.IA5STRING; nameASN1[X400_ADDR] = ORAddress.ASN1; nameASN1[DIR_NAME] = Name.ASN1; nameASN1[EDIP_NAME] = EDIPartyName.ASN1; nameASN1[IP_ADDR] = ASN1OctetString.getInstance(); nameASN1[REG_ID] = ASN1Oid.getInstance(); } // the tag of the name type private int tag; // the name value (can be String or byte array) private Object name; // the ASN.1 encoded form of GeneralName private byte[] encoding; // the ASN.1 encoded form of GeneralName's field private byte[] name_encoding; /** * Makes the GeneralName object from the tag type and corresponding * well established string representation of the name value. * The String representation of [7] iPAddress is such as: * For IP v4, as specified in RFC 791, the address must * contain exactly 4 byte component. For IP v6, as specified in * RFC 1883, the address must contain exactly 16 byte component. * If GeneralName structure is used as a part of Name Constraints * extension, to represent an address range the number of address * component is doubled (to 8 and 32 bytes respectively). * Note that the names: * [0] otherName, [3] x400Address, [5] ediPartyName * have no the string representation, so exception will be thrown. * To make the GeneralName object with such names use another constructor. * @param tag is an integer which value corresponds to the name type. * @param name is a name value corresponding to the tag. * <pre> */ public GeneralName(int tag, String name) throws IOException { if (name == null) { throw new IOException("name must not be null"); } this.tag = tag; switch (tag) { case OTHER_NAME : case X400_ADDR : case EDIP_NAME : throw new IOException("Unknown string representation for type [" + tag + "]"); case DNS_NAME : // according to RFC 3280 p.34 the DNS name should be // checked against the // RFC 1034 p.10 (3.5. Preferred name syntax): checkDNS(name); this.name = name; break; case UR_ID : // check the uniformResourceIdentifier for correctness // according to RFC 3280 p.34 checkURI(name); this.name = name; break; case RFC822_NAME : this.name = name; break; case REG_ID: this.name = oidStrToInts(name); break; case DIR_NAME : this.name = new Name(name); break; case IP_ADDR : this.name = ipStrToBytes(name); break; default: throw new IOException("Unknown type: [" + tag + "]"); } } /** * TODO * @param name: OtherName */ public GeneralName(OtherName name) { this.tag = OTHER_NAME; this.name = name; } /** * TODO * @param name: ORAddress */ public GeneralName(ORAddress name) { this.tag = X400_ADDR; this.name = name; } /** * TODO * @param name: Name */ public GeneralName(Name name) { this.tag = DIR_NAME; this.name = name; } /** * TODO * @param name: EDIPartyName */ public GeneralName(EDIPartyName name) { this.tag = EDIP_NAME; this.name = name; } /** * Constructor for type [7] iPAddress. * name is an array of bytes such as: * For IP v4, as specified in RFC 791, the address must * contain exactly 4 byte component. For IP v6, as specified in * RFC 1883, the address must contain exactly 16 byte component. * If GeneralName structure is used as a part of Name Constraints * extension, to represent an address range the number of address * component is doubled (to 8 and 32 bytes respectively). */ public GeneralName(byte[] name) throws IllegalArgumentException { int length = name.length; if (length != 4 && length != 8 && length != 16 && length != 32) { throw new IllegalArgumentException( "Specified iPAddress is not correct."); } this.tag = IP_ADDR; this.name = new byte[name.length]; System.arraycopy(name, 0, this.name, 0, name.length); } /** * Constructs an object representing the value of GeneralName. * @param tag is an integer which value corresponds * to the name type (0-8), * @param name is a DER encoded for of the name value */ public GeneralName(int tag, byte[] name) throws IOException { if (name == null) { throw new NullPointerException("name must not be null"); } if ((tag < 0) || (tag > 8)) { throw new IOException("GeneralName: unknown tag: " + tag); } this.tag = tag; this.name_encoding = new byte[name.length]; System.arraycopy(name, 0, this.name_encoding, 0, name.length); this.name = nameASN1[tag].decode(this.name_encoding); } /** * Returns the tag of the name in the structure * @return the tag of the name */ public int getTag() { return tag; } /** * @return the value of the name. * The class of name object depends on the tag as follows: * [0] otherName - OtherName object, * [1] rfc822Name - String object, * [2] dNSName - String object, * [3] x400Address - ORAddress object, * [4] directoryName - instance of Name object, * [5] ediPartyName - EDIPartyName object, * [6] uniformResourceIdentifier - String object, * [7] iPAddress - array of bytes such as: * For IP v4, as specified in RFC 791, the address must * contain exactly 4 byte component. For IP v6, as specified in * RFC 1883, the address must contain exactly 16 byte component. * If GeneralName structure is used as a part of Name Constraints * extension, to represent an address range the number of address * component is doubled (to 8 and 32 bytes respectively). * [8] registeredID - String. */ public Object getName() { return name; } /** * TODO * @param _gname: Object * @return */ public boolean equals(Object _gname) { if (!(_gname instanceof GeneralName)) { return false; } GeneralName gname = (GeneralName) _gname; if (this.tag != gname.tag) { return false; } switch(tag) { case RFC822_NAME: case DNS_NAME: case UR_ID: return ((String) name).equalsIgnoreCase( (String) gname.getName()); case REG_ID: return Arrays.equals((int[]) name, (int[]) gname.name); case IP_ADDR: // iPAddress [7], check by using ranges. return Arrays.equals((byte[]) name, (byte[]) gname.name); case DIR_NAME: case X400_ADDR: case OTHER_NAME: case EDIP_NAME: return Arrays.equals(getEncoded(), gname.getEncoded()); default: // should never happen } //System.out.println(false); return false; } public int hashCode() { switch(tag) { case RFC822_NAME: case DNS_NAME: case UR_ID: case REG_ID: case IP_ADDR: return name.hashCode(); case DIR_NAME: case X400_ADDR: case OTHER_NAME: case EDIP_NAME: return getEncoded().hashCode(); default: return super.hashCode(); } } /** * Checks if the other general name is acceptable by this object. * The name is acceptable if it has the same type name and its * name value is equal to name value of this object. Also the name * is acceptable if this general name object is a part of name * constraints and the specified name is satisfied the restriction * provided by this object (for more detail see section 4.2.1.11 * of rfc 3280). * Note that for X400Address [3] check procedure is unclear so method * just checks the equality of encoded forms. * For otherName [0], ediPartyName [5], and registeredID [8] * the check procedure if not defined by rfc 3280 and for names of * these types this method also checks only for equality of encoded forms. */ public boolean isAcceptable(GeneralName gname) { if (this.tag != gname.getTag()) { return false; } switch (this.tag) { case RFC822_NAME: // Mail address [1]: // a@b.c - particular address is acceptable by the same address, // or by b.c - host name. return ((String) gname.getName()).toLowerCase() .endsWith(((String) name).toLowerCase()); case DNS_NAME: // DNS name [2] that can be constructed by simply adding // to the left hand side of the name satisfies the name // constraint: aaa.aa.aa satisfies to aaa.aa.aa, aa.aa, .. String dns = (String) name; String _dns = (String) gname.getName(); if (dns.equalsIgnoreCase(_dns)) { return true; } else { return _dns.toLowerCase().endsWith("." + dns.toLowerCase()); //$NON-NLS-1$ } case UR_ID: // For URIs the constraint ".xyz.com" is satisfied by both // abc.xyz.com and abc.def.xyz.com. However, the constraint // ".xyz.com" is not satisfied by "xyz.com". // When the constraint does not begin with a period, it // specifies a host. // Extract the host from URI: String uri = (String) name; int begin = uri.indexOf("://")+3; //$NON-NLS-1$ int end = uri.indexOf('/', begin); String host = (end == -1) ? uri.substring(begin) : uri.substring(begin, end); uri = (String) gname.getName(); begin = uri.indexOf("://")+3; //$NON-NLS-1$ end = uri.indexOf('/', begin); String _host = (end == -1) ? uri.substring(begin) : uri.substring(begin, end); if (host.startsWith(".")) { //$NON-NLS-1$ return _host.toLowerCase().endsWith(host.toLowerCase()); } else { return host.equalsIgnoreCase(_host); } case IP_ADDR: // iPAddress [7], check by using ranges. byte[] address = (byte[]) name; byte[] _address = (byte[]) gname.getName(); int length = address.length; int _length = _address.length; if (length == _length) { return Arrays.equals(address, _address); } else if (length == 2*_length) { for (int i=0; i<_address.length; i++) { if ((_address[i] < address[i]) || (_address[i] > address[i+_length])) { return false; } } return true; } else { return false; } case DIR_NAME: // FIXME: false: // directoryName according to 4.1.2.4 // comparing the encoded forms of the names //TODO: //Legacy implementations exist where an RFC 822 name //is embedded in the subject distinguished name in an //attribute of type EmailAddress case X400_ADDR: case OTHER_NAME: case EDIP_NAME: case REG_ID: return Arrays.equals(getEncoded(), gname.getEncoded()); default: // should never happen } return true; } /** * Gets a list representation of this GeneralName object. * The first entry of the list is an Integer object representing * the type of mane (0-8), and the second entry is a value of the name: * string or ASN.1 DER encoded form depending on the type as follows: * rfc822Name, dNSName, uniformResourceIdentifier names are returned * as Strings, using the string formats for those types (rfc 3280) * IP v4 address names are returned using dotted quad notation. * IP v6 address names are returned in the form "p1:p2:...:p8", * where p1-p8 are hexadecimal values representing the eight 16-bit * pieces of the address. registeredID name are returned as Strings * represented as a series of nonnegative integers separated by periods. * And directory names (distinguished names) are returned in * RFC 2253 string format. * otherName, X400Address, ediPartyName returned as byte arrays * containing the ASN.1 DER encoded form of the name. */ public List getAsList() { ArrayList result = new ArrayList(); result.add(new Integer(tag)); switch (tag) { case OTHER_NAME: result.add(((OtherName) name).getEncoded()); break; case RFC822_NAME: case DNS_NAME: case UR_ID: result.add(name); // String break; case REG_ID: result.add(ObjectIdentifier.toString((int[]) name)); break; case X400_ADDR: result.add(((ORAddress) name).getEncoded()); break; case DIR_NAME: // directoryName is returned as a String result.add(((Name) name).getName(X500Principal.RFC2253)); break; case EDIP_NAME: result.add(((EDIPartyName) name).getEncoded()); break; case IP_ADDR: //iPAddress is returned as a String, not as a byte array result.add(ipBytesToStr((byte[]) name)); break; default: // should never happen } return Collections.unmodifiableList(result); } // // TODO // @param data: byte[] // @return // private String getBytesAsString(byte[] data) { String result = ""; //$NON-NLS-1$ for (int i=0; i<data.length; i++) { String tail = Integer.toHexString(0x00ff & data[i]); if (tail.length() == 1) { tail = "0" + tail; //$NON-NLS-1$ } result += tail + " "; //$NON-NLS-1$ } return result; } /** * TODO * @return */ public String toString() { String result = ""; //$NON-NLS-1$ switch (tag) { case OTHER_NAME: result = "otherName[0]: " //$NON-NLS-1$ + getBytesAsString(getEncoded()); break; case RFC822_NAME: result = "rfc822Name[1]: " + name; //$NON-NLS-1$ break; case DNS_NAME: result = "dNSName[2]: " + name; //$NON-NLS-1$ break; case UR_ID: result = "uniformResourceIdentifier[6]: " + name; //$NON-NLS-1$ break; case REG_ID: result = "registeredID[8]: " + ObjectIdentifier.toString((int[]) name); //$NON-NLS-1$ break; case X400_ADDR: result = "x400Address[3]: " //$NON-NLS-1$ + getBytesAsString(getEncoded()); break; case DIR_NAME: result = "directoryName[4]: " //$NON-NLS-1$ + ((Name) name).getName(X500Principal.RFC2253); break; case EDIP_NAME: result = "ediPartyName[5]: " //$NON-NLS-1$ + getBytesAsString(getEncoded()); break; case IP_ADDR: result = "iPAddress[7]: " + ipBytesToStr((byte[]) name); //$NON-NLS-1$ break; default: // should never happen } return result; } /** * Returns ASN.1 encoded form of this X.509 GeneralName value. * @return a byte array containing ASN.1 encode form. */ public byte[] getEncoded() { if (encoding == null) { encoding = ASN1.encode(this); } return encoding; } /** * @return the encoded value of the name without the tag associated * with the name in the GeneralName structure * @throws IOException */ public byte[] getEncodedName() { if (name_encoding == null) { name_encoding = nameASN1[tag].encode(name); } return name_encoding; } /** * Checks the correctness of the string representation of DNS name. * The correctness is checked as specified in RFC 1034 p. 10. */ public static void checkDNS(String dns) throws IOException { byte[] bytes = dns.toLowerCase().getBytes(); // indicates if it is a first letter of the label boolean first_letter = true; for (int i=0; i<bytes.length; i++) { byte ch = bytes[i]; if (first_letter) { if (ch > 'z' || ch < 'a') { throw new IOException("DNS name must start with a letter:'" + (char)ch + "' " + dns); } first_letter = false; continue; } if (!((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || (ch == '-') || (ch == '.'))) { throw new IOException("Incorrect DNS name: " + dns); } if (ch == '.') { // check the end of the previous label, it should not // be '-' sign if (bytes[i-1] == '-') { throw new IOException( "Incorrect DNS name: label ends with '-': " + dns); } first_letter = true; } } } /** * Checks the correctness of the string representation of URI name. * The correctness is checked as pointed out in RFC 3280 p. 34. */ public static void checkURI(String uri) throws IOException { try { URI ur = new URI(uri); if ((ur.getScheme() == null) || (ur.getRawSchemeSpecificPart().length() == 0)) { throw new IOException("Bad representation of uniformResourceIdentifier. It must include the scheme and a scheme-specific-part: " + uri); } if (!ur.isAbsolute()) { throw new IOException("Bad representation of uniformResourceIdentifier. It should not be relative: " + uri); } } catch (URISyntaxException e) { throw (IOException) new IOException( "Bad representation of uniformResourceIdentifier." + uri).initCause(e); } } /** * Converts OID into array of bytes. */ public static int[] oidStrToInts(String oid) throws IOException { byte[] bytes = oid.getBytes(); if (bytes[bytes.length-1] == '.') { throw new IOException("The OID: '" + oid + "' is incorrect"); } int[] result = new int[bytes.length/2+1]; // best case: a.b.c.d.e int number = 0; // the number of OID's components for (int i=0; i<bytes.length; i++) { int value = 0; int pos = i; while ((i < bytes.length) && (bytes[i] >= '0') && (bytes[i] <= '9')) { value = 10 * value + (bytes[i++] - 48); } if (i == pos) { // the number was not read throw new IOException("The OID: '" + oid + "' is incorrect"); } result[number++] = value; if (i >= bytes.length) { break; } if (bytes[i] != '.') { throw new IOException("The OID: '" + oid + "' is incorrect"); } } if (number < 2) { throw new IOException("OID should consist of no less than 2 components:" + oid); } int[] res = new int[number]; for (int i=0; i<number; i++) { res[i] = result[i]; } return res; } /** * Helper method. Converts the String representation of IP address * to the array of bytes. IP addresses are expected in two versions:<br> * IPv4 - in dot-decimal notation<br> * IPv6 - in colon hexadecimal notation<br> * Also method works with the ranges of the addresses represented * as 2 addresses separated by '/' character. * @param address : String representation of IP address * @return byte representation of IP address */ public static byte[] ipStrToBytes(String ip) throws IOException { boolean isIPv4 = (ip.indexOf('.') > 0); // number of components (should be 4 or 8) int num_components = (isIPv4) ? 4 : 16; if (ip.indexOf('/') > 0) { num_components *= 2; // this is a range of addresses } // the resulting array byte[] result = new byte[num_components]; byte[] ip_bytes = ip.getBytes(); // number of address component to be read int component = 0; // if it is reading the second bound of a range boolean reading_second_bound = false; if (isIPv4) { // IPv4 address is expected in the form of dot-decimal notation: // 1.100.2.200 // or in the range form: // 1.100.2.200/1.100.3.300 int i = 0; while (i < ip_bytes.length) { int digits = 0; // the value of the address component int value = 0; while ((i < ip_bytes.length) && (ip_bytes[i] >= '0') && (ip_bytes[i] <= '9')) { digits++; if (digits > 3) { throw new IOException("Incorrect IPv6 representation: '" + ip + "'"); } value = 10 * value + (ip_bytes[i] - 48); i++; } if (digits == 0) { // ip_bytes[i] is not a number throw new IOException("Incorrect IP representation: " + ip); } result[component] = (byte) value; component++; if (i >= ip_bytes.length) { // no more bytes break; } // check the reached delimiter if ((ip_bytes[i] != '.' && ip_bytes[i] != '/')) { throw new IOException("Incorrect IP representation: " + ip); } // check the correctness of the range if (ip_bytes[i] == '/') { if (reading_second_bound) { // more than 2 bounds in the range throw new IOException("Incorrect IP representation: " + ip); } if (component != 4) { throw new IOException("IPv4 address should consist of 4 decimal numbers: " + ip); } reading_second_bound = true; } // check the number of the components if (component > ((reading_second_bound) ? 7 : 3)) { throw new IOException("IPv4 address should consist of 4 decimal numbers: " + ip); } i++; } // check the number of read components if (component != num_components) { throw new IOException("IPv4 address should consist of 4 decimal numbers: " + ip); } } else { // IPv6 address is expected in the form of // colon hexadecimal notation: // 010a:020b:3337:1000:FFFA:ABCD:9999:0000 // or in a range form: // 010a:020b:3337:1000:FFFA:ABCD:9999:0000/010a:020b:3337:1000:FFFA:ABCD:9999:1111 if (ip_bytes.length != 39 && ip_bytes.length != 79) { // incorrect length of the string representation throw new IOException("Incorrect IPv6 representation: '" + ip + "'"); } int value = 0; // indicates the reading of the second half of byte boolean second_hex = false; // if the delimiter (':' or '/') is expected boolean expect_delimiter = false; for (int i=0; i<ip_bytes.length; i++) { byte bytik = ip_bytes[i]; if ((bytik >= '0') && (bytik <= '9')) { value = (bytik - 48); // '0':0, '1':1, ... , '9':9 } else if ((bytik >= 'A') && (bytik <= 'F')) { value = (bytik - 55); // 'A':10, 'B':11, ... , 'F':15 } else if ((bytik >= 'a') && (bytik <= 'f')) { value = (bytik - 87); // 'a':10, 'b':11, ... , 'f':15 } else if (second_hex) { // second hex value of a byte is expected but was not read // (it is the situation like: ...ABCD:A:ABCD...) throw new IOException("Incorrect IPv6 representation: '" + ip + "'"); } else if ((bytik == ':') || (bytik == '/')) { if (component % 2 == 1) { // second byte of the component is omitted // (it is the situation like: ... ABDC:AB:ABCD ...) throw new IOException("Incorrect IPv6 representation: '" + ip + "'"); //$NON-NLS-1$ } if (bytik == '/') { if (reading_second_bound) { // more than 2 bounds in the range throw new IOException( "Incorrect IPv6 representation: '" + ip + "'"); } if (component != 16) { // check the number of read components throw new IOException("IPv6 address should consist of 8 hexadecimal numbers: " + ip); } reading_second_bound = true; } expect_delimiter = false; continue; } else { throw new IOException("Incorrect IPv6 representation: '" + ip + "'"); } if (expect_delimiter) { // delimiter is expected but was not read throw new IOException("Incorrect IPv6 representation: '" + ip + "'"); } if (!second_hex) { // first half of byte has been read result[component] = (byte) (value << 4); second_hex = true; } else { // second half of byte has been read result[component] = (byte) ((result[component] & 0xFF) | value); // delimiter is expected if 2 bytes were read expect_delimiter = (component % 2 == 1); second_hex = false; component++; } } // check the correctness of the read address: if (second_hex || (component % 2 == 1)) { throw new IOException("Incorrect IPv6 representation: '" + ip + "'"); } } return result; } /** * Helper method. Converts the byte array representation of ip address * to the String. * @param ip : byte array representation of ip address * If the length of byte array 4 then it represents an IP v4 * and the output String will be in the dotted quad form. * If the length is 16 then it represents an IP v6 * and the output String will be returned in format "p1:p2:...:p8", * where p1-p8 are hexadecimal values representing the eight 16-bit * pieces of the address. * If the length is 8 or 32 then it represents an address range (RFC 1519) * and the output String will contain 2 IP address divided by "/" * @return String representation of ip address */ public static String ipBytesToStr(byte[] ip) { String result = ""; //$NON-NLS-1$ if (ip.length < 9) { // IP v4 for (int i=0; i<ip.length; i++) { result += Integer.toString(ip[i] & 0xff); if (i != ip.length-1) { result += (i == 3) ? "/": "."; //$NON-NLS-1$ //$NON-NLS-2$ } } } else { for (int i=0; i<ip.length; i++) { result += Integer.toHexString(0x00ff & ip[i]); if ((i % 2 != 0) && (i != ip.length-1)) { result += (i == 15) ? "/": ":"; //$NON-NLS-1$ //$NON-NLS-2$ } } } return result; } public static final ASN1Choice ASN1 = new ASN1Choice(new ASN1Type[] { new ASN1Implicit(0, OtherName.ASN1), new ASN1Implicit(1, ASN1StringType.IA5STRING), new ASN1Implicit(2, ASN1StringType.IA5STRING), new ASN1Implicit(3, ORAddress.ASN1), new ASN1Implicit(4, Name.ASN1), new ASN1Implicit(5, EDIPartyName.ASN1), new ASN1Implicit(6, ASN1StringType.IA5STRING), new ASN1Implicit(7, ASN1OctetString.getInstance()), new ASN1Implicit(8, ASN1Oid.getInstance()) }) { public Object getObjectToEncode(Object value) { return ((GeneralName) value).name; } public int getIndex(java.lang.Object object) { return ((GeneralName) object).tag; } public Object getDecodedObject(BerInputStream in) throws IOException { GeneralName result; switch (in.choiceIndex) { case OTHER_NAME: // OtherName result = new GeneralName((OtherName) in.content); break; case RFC822_NAME: // rfc822Name case DNS_NAME: // dNSName result = new GeneralName(in.choiceIndex, (String) in.content); break; case X400_ADDR: result = new GeneralName((ORAddress) in.content); break; case DIR_NAME: // directoryName (X.500 Name) result = new GeneralName((Name) in.content); break; case EDIP_NAME: // ediPartyName result = new GeneralName((EDIPartyName) in.content); break; case UR_ID: // uniformResourceIdentifier String uri = (String) in.content; if (uri.indexOf(":") == -1) { //$NON-NLS-1$ throw new IOException( "GeneralName: scheme is missing in URI: " + uri); } result = new GeneralName(in.choiceIndex, uri); break; case IP_ADDR: // iPAddress result = new GeneralName((byte[]) in.content); break; case REG_ID: // registeredID result = new GeneralName(in.choiceIndex, ObjectIdentifier.toString((int[]) in.content)); break; default: throw new IOException("GeneralName: unknown tag: " + in.choiceIndex); } result.encoding = in.getEncoded(); return result; } }; // public static void printAsHex(int perLine, // String prefix, // String delimiter, // byte[] data) { // for (int i=0; i<data.length; i++) { // String tail = Integer.toHexString(0x000000ff & data[i]); // if (tail.length() == 1) { // tail = "0" + tail; // } // System.out.print(prefix + "0x" + tail + delimiter); // if (((i+1)%perLine) == 0) { // System.out.println(); // } // } // System.out.println(); // } // public static void main(String[] args) { // System.out.println(">> "+new BigInteger(new byte[] {(byte)23, (byte)255}).toString(2)); // System.out.println(ipBytesToStr(new byte[] {(byte)255, (byte)23, (byte)128, (byte)130})); // System.out.println(ipBytesToStr(new byte[] {(byte)255, (byte)23, (byte)128, (byte)130, // (byte)255, (byte)23, (byte)128, (byte)130})); // System.out.println(ipBytesToStr(new byte[] {(byte)255, (byte)23, (byte)128, (byte)130, // (byte)255, (byte)23, (byte)128, (byte)130, // (byte)255, (byte)23, (byte)128, (byte)130, // (byte)255, (byte)23, (byte)128, (byte)130})); // System.out.println(ipBytesToStr(new byte[] {(byte)255, (byte)23, (byte)128, (byte)130, // (byte)255, (byte)23, (byte)128, (byte)130, // (byte)255, (byte)23, (byte)128, (byte)130, // (byte)255, (byte)23, (byte)128, (byte)130, // (byte)255, (byte)23, (byte)128, (byte)130, // (byte)255, (byte)23, (byte)128, (byte)130, // (byte)255, (byte)23, (byte)128, (byte)130, // (byte)255, (byte)23, (byte)128, (byte)130})); // ipStrToBytes("1.2.3.4"); // ipStrToBytes("1.2.3.4/4.3.2.1"); // printAsHex(8, "", " ", ipStrToBytes("ff17:8082:ff17:8082:ff17:8082:ff17:8082/ff17:8082:ff17:8082:ff17:8082:ff17:8082")); // } }