0001: package org.bouncycastle.asn1.x509;
0002:
0003: import org.bouncycastle.asn1.ASN1Encodable;
0004: import org.bouncycastle.asn1.ASN1EncodableVector;
0005: import org.bouncycastle.asn1.ASN1Sequence;
0006: import org.bouncycastle.asn1.ASN1Set;
0007: import org.bouncycastle.asn1.ASN1TaggedObject;
0008: import org.bouncycastle.asn1.DEREncodable;
0009: import org.bouncycastle.asn1.DERObject;
0010: import org.bouncycastle.asn1.DERObjectIdentifier;
0011: import org.bouncycastle.asn1.DERSequence;
0012: import org.bouncycastle.asn1.DERSet;
0013: import org.bouncycastle.asn1.DERString;
0014: import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
0015: import org.bouncycastle.util.Strings;
0016: import org.bouncycastle.util.encoders.Hex;
0017:
0018: import java.util.Enumeration;
0019: import java.util.Hashtable;
0020: import java.util.Vector;
0021:
0022: /**
0023: * <pre>
0024: * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
0025: *
0026: * RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
0027: *
0028: * AttributeTypeAndValue ::= SEQUENCE {
0029: * type OBJECT IDENTIFIER,
0030: * value ANY }
0031: * </pre>
0032: */
0033: public class X509Name extends ASN1Encodable {
0034: /**
0035: * country code - StringType(SIZE(2))
0036: */
0037: public static final DERObjectIdentifier C = new DERObjectIdentifier(
0038: "2.5.4.6");
0039:
0040: /**
0041: * organization - StringType(SIZE(1..64))
0042: */
0043: public static final DERObjectIdentifier O = new DERObjectIdentifier(
0044: "2.5.4.10");
0045:
0046: /**
0047: * organizational unit name - StringType(SIZE(1..64))
0048: */
0049: public static final DERObjectIdentifier OU = new DERObjectIdentifier(
0050: "2.5.4.11");
0051:
0052: /**
0053: * Title
0054: */
0055: public static final DERObjectIdentifier T = new DERObjectIdentifier(
0056: "2.5.4.12");
0057:
0058: /**
0059: * common name - StringType(SIZE(1..64))
0060: */
0061: public static final DERObjectIdentifier CN = new DERObjectIdentifier(
0062: "2.5.4.3");
0063:
0064: /**
0065: * device serial number name - StringType(SIZE(1..64))
0066: */
0067: public static final DERObjectIdentifier SN = new DERObjectIdentifier(
0068: "2.5.4.5");
0069:
0070: /**
0071: * street - StringType(SIZE(1..64))
0072: */
0073: public static final DERObjectIdentifier STREET = new DERObjectIdentifier(
0074: "2.5.4.9");
0075:
0076: /**
0077: * device serial number name - StringType(SIZE(1..64))
0078: */
0079: public static final DERObjectIdentifier SERIALNUMBER = SN;
0080:
0081: /**
0082: * locality name - StringType(SIZE(1..64))
0083: */
0084: public static final DERObjectIdentifier L = new DERObjectIdentifier(
0085: "2.5.4.7");
0086:
0087: /**
0088: * state, or province name - StringType(SIZE(1..64))
0089: */
0090: public static final DERObjectIdentifier ST = new DERObjectIdentifier(
0091: "2.5.4.8");
0092:
0093: /**
0094: * Naming attributes of type X520name
0095: */
0096: public static final DERObjectIdentifier SURNAME = new DERObjectIdentifier(
0097: "2.5.4.4");
0098: public static final DERObjectIdentifier GIVENNAME = new DERObjectIdentifier(
0099: "2.5.4.42");
0100: public static final DERObjectIdentifier INITIALS = new DERObjectIdentifier(
0101: "2.5.4.43");
0102: public static final DERObjectIdentifier GENERATION = new DERObjectIdentifier(
0103: "2.5.4.44");
0104: public static final DERObjectIdentifier UNIQUE_IDENTIFIER = new DERObjectIdentifier(
0105: "2.5.4.45");
0106:
0107: /**
0108: * businessCategory - DirectoryString(SIZE(1..128)
0109: */
0110: public static final DERObjectIdentifier BUSINESS_CATEGORY = new DERObjectIdentifier(
0111: "2.5.4.15");
0112:
0113: /**
0114: * postalCode - DirectoryString(SIZE(1..40)
0115: */
0116: public static final DERObjectIdentifier POSTAL_CODE = new DERObjectIdentifier(
0117: "2.5.4.17");
0118:
0119: /**
0120: * dnQualifier - DirectoryString(SIZE(1..64)
0121: */
0122: public static final DERObjectIdentifier DN_QUALIFIER = new DERObjectIdentifier(
0123: "2.5.4.46");
0124:
0125: /**
0126: * RFC 3039 Pseudonym - DirectoryString(SIZE(1..64)
0127: */
0128: public static final DERObjectIdentifier PSEUDONYM = new DERObjectIdentifier(
0129: "2.5.4.65");
0130:
0131: /**
0132: * RFC 3039 DateOfBirth - GeneralizedTime - YYYYMMDD000000Z
0133: */
0134: public static final DERObjectIdentifier DATE_OF_BIRTH = new DERObjectIdentifier(
0135: "1.3.6.1.5.5.7.9.1");
0136:
0137: /**
0138: * RFC 3039 PlaceOfBirth - DirectoryString(SIZE(1..128)
0139: */
0140: public static final DERObjectIdentifier PLACE_OF_BIRTH = new DERObjectIdentifier(
0141: "1.3.6.1.5.5.7.9.2");
0142:
0143: /**
0144: * RFC 3039 Gender - PrintableString (SIZE(1)) -- "M", "F", "m" or "f"
0145: */
0146: public static final DERObjectIdentifier GENDER = new DERObjectIdentifier(
0147: "1.3.6.1.5.5.7.9.3");
0148:
0149: /**
0150: * RFC 3039 CountryOfCitizenship - PrintableString (SIZE (2)) -- ISO 3166
0151: * codes only
0152: */
0153: public static final DERObjectIdentifier COUNTRY_OF_CITIZENSHIP = new DERObjectIdentifier(
0154: "1.3.6.1.5.5.7.9.4");
0155:
0156: /**
0157: * RFC 3039 CountryOfResidence - PrintableString (SIZE (2)) -- ISO 3166
0158: * codes only
0159: */
0160: public static final DERObjectIdentifier COUNTRY_OF_RESIDENCE = new DERObjectIdentifier(
0161: "1.3.6.1.5.5.7.9.5");
0162:
0163: /**
0164: * ISIS-MTT NameAtBirth - DirectoryString(SIZE(1..64)
0165: */
0166: public static final DERObjectIdentifier NAME_AT_BIRTH = new DERObjectIdentifier(
0167: "1.3.36.8.3.14");
0168:
0169: /**
0170: * RFC 3039 PostalAddress - SEQUENCE SIZE (1..6) OF
0171: * DirectoryString(SIZE(1..30))
0172: */
0173: public static final DERObjectIdentifier POSTAL_ADDRESS = new DERObjectIdentifier(
0174: "2.5.4.16");
0175:
0176: /**
0177: * Email address (RSA PKCS#9 extension) - IA5String.
0178: * <p>Note: if you're trying to be ultra orthodox, don't use this! It shouldn't be in here.
0179: */
0180: public static final DERObjectIdentifier EmailAddress = PKCSObjectIdentifiers.pkcs_9_at_emailAddress;
0181:
0182: /**
0183: * more from PKCS#9
0184: */
0185: public static final DERObjectIdentifier UnstructuredName = PKCSObjectIdentifiers.pkcs_9_at_unstructuredName;
0186: public static final DERObjectIdentifier UnstructuredAddress = PKCSObjectIdentifiers.pkcs_9_at_unstructuredAddress;
0187:
0188: /**
0189: * email address in Verisign certificates
0190: */
0191: public static final DERObjectIdentifier E = EmailAddress;
0192:
0193: /*
0194: * others...
0195: */
0196: public static final DERObjectIdentifier DC = new DERObjectIdentifier(
0197: "0.9.2342.19200300.100.1.25");
0198:
0199: /**
0200: * LDAP User id.
0201: */
0202: public static final DERObjectIdentifier UID = new DERObjectIdentifier(
0203: "0.9.2342.19200300.100.1.1");
0204:
0205: /**
0206: * look up table translating OID values into their common symbols - this static is scheduled for deletion
0207: */
0208: public static Hashtable OIDLookUp = new Hashtable();
0209:
0210: /**
0211: * determines whether or not strings should be processed and printed
0212: * from back to front.
0213: */
0214: public static boolean DefaultReverse = false;
0215:
0216: /**
0217: * default look up table translating OID values into their common symbols following
0218: * the convention in RFC 2253 with a few extras
0219: */
0220: public static Hashtable DefaultSymbols = OIDLookUp;
0221:
0222: /**
0223: * look up table translating OID values into their common symbols following the convention in RFC 2253
0224: *
0225: */
0226: public static Hashtable RFC2253Symbols = new Hashtable();
0227:
0228: /**
0229: * look up table translating OID values into their common symbols following the convention in RFC 1779
0230: *
0231: */
0232: public static Hashtable RFC1779Symbols = new Hashtable();
0233:
0234: /**
0235: * look up table translating string values into their OIDS -
0236: * this static is scheduled for deletion
0237: */
0238: public static Hashtable SymbolLookUp = new Hashtable();
0239:
0240: /**
0241: * look up table translating common symbols into their OIDS.
0242: */
0243: public static Hashtable DefaultLookUp = SymbolLookUp;
0244:
0245: private static final Boolean TRUE = new Boolean(true); // for J2ME compatibility
0246: private static final Boolean FALSE = new Boolean(false);
0247:
0248: static {
0249: DefaultSymbols.put(C, "C");
0250: DefaultSymbols.put(O, "O");
0251: DefaultSymbols.put(T, "T");
0252: DefaultSymbols.put(OU, "OU");
0253: DefaultSymbols.put(CN, "CN");
0254: DefaultSymbols.put(L, "L");
0255: DefaultSymbols.put(ST, "ST");
0256: DefaultSymbols.put(SN, "SERIALNUMBER");
0257: DefaultSymbols.put(EmailAddress, "E");
0258: DefaultSymbols.put(DC, "DC");
0259: DefaultSymbols.put(UID, "UID");
0260: DefaultSymbols.put(STREET, "STREET");
0261: DefaultSymbols.put(SURNAME, "SURNAME");
0262: DefaultSymbols.put(GIVENNAME, "GIVENNAME");
0263: DefaultSymbols.put(INITIALS, "INITIALS");
0264: DefaultSymbols.put(GENERATION, "GENERATION");
0265: DefaultSymbols.put(UnstructuredAddress, "unstructuredAddress");
0266: DefaultSymbols.put(UnstructuredName, "unstructuredName");
0267: DefaultSymbols.put(UNIQUE_IDENTIFIER, "UniqueIdentifier");
0268: DefaultSymbols.put(DN_QUALIFIER, "DN");
0269: DefaultSymbols.put(PSEUDONYM, "Pseudonym");
0270: DefaultSymbols.put(POSTAL_ADDRESS, "PostalAddress");
0271: DefaultSymbols.put(NAME_AT_BIRTH, "NameAtBirth");
0272: DefaultSymbols.put(COUNTRY_OF_CITIZENSHIP,
0273: "CountryOfCitizenship");
0274: DefaultSymbols.put(COUNTRY_OF_RESIDENCE, "CountryOfResidence");
0275: DefaultSymbols.put(GENDER, "Gender");
0276: DefaultSymbols.put(PLACE_OF_BIRTH, "PlaceOfBirth");
0277: DefaultSymbols.put(DATE_OF_BIRTH, "DateOfBirth");
0278: DefaultSymbols.put(POSTAL_CODE, "PostalCode");
0279: DefaultSymbols.put(BUSINESS_CATEGORY, "BusinessCategory");
0280:
0281: RFC2253Symbols.put(C, "C");
0282: RFC2253Symbols.put(O, "O");
0283: RFC2253Symbols.put(OU, "OU");
0284: RFC2253Symbols.put(CN, "CN");
0285: RFC2253Symbols.put(L, "L");
0286: RFC2253Symbols.put(ST, "ST");
0287: RFC2253Symbols.put(STREET, "STREET");
0288: RFC2253Symbols.put(DC, "DC");
0289: RFC2253Symbols.put(UID, "UID");
0290:
0291: RFC1779Symbols.put(C, "C");
0292: RFC1779Symbols.put(O, "O");
0293: RFC1779Symbols.put(OU, "OU");
0294: RFC1779Symbols.put(CN, "CN");
0295: RFC1779Symbols.put(L, "L");
0296: RFC1779Symbols.put(ST, "ST");
0297: RFC1779Symbols.put(STREET, "STREET");
0298:
0299: DefaultLookUp.put("c", C);
0300: DefaultLookUp.put("o", O);
0301: DefaultLookUp.put("t", T);
0302: DefaultLookUp.put("ou", OU);
0303: DefaultLookUp.put("cn", CN);
0304: DefaultLookUp.put("l", L);
0305: DefaultLookUp.put("st", ST);
0306: DefaultLookUp.put("sn", SN);
0307: DefaultLookUp.put("serialnumber", SN);
0308: DefaultLookUp.put("street", STREET);
0309: DefaultLookUp.put("emailaddress", E);
0310: DefaultLookUp.put("dc", DC);
0311: DefaultLookUp.put("e", E);
0312: DefaultLookUp.put("uid", UID);
0313: DefaultLookUp.put("surname", SURNAME);
0314: DefaultLookUp.put("givenname", GIVENNAME);
0315: DefaultLookUp.put("initials", INITIALS);
0316: DefaultLookUp.put("generation", GENERATION);
0317: DefaultLookUp.put("unstructuredaddress", UnstructuredAddress);
0318: DefaultLookUp.put("unstructuredname", UnstructuredName);
0319: DefaultLookUp.put("uniqueidentifier", UNIQUE_IDENTIFIER);
0320: DefaultLookUp.put("dn", DN_QUALIFIER);
0321: DefaultLookUp.put("pseudonym", PSEUDONYM);
0322: DefaultLookUp.put("postaladdress", POSTAL_ADDRESS);
0323: DefaultLookUp.put("nameofbirth", NAME_AT_BIRTH);
0324: DefaultLookUp.put("countryofcitizenship",
0325: COUNTRY_OF_CITIZENSHIP);
0326: DefaultLookUp.put("countryofresidence", COUNTRY_OF_RESIDENCE);
0327: DefaultLookUp.put("gender", GENDER);
0328: DefaultLookUp.put("placeofbirth", PLACE_OF_BIRTH);
0329: DefaultLookUp.put("dateofbirth", DATE_OF_BIRTH);
0330: DefaultLookUp.put("postalcode", POSTAL_CODE);
0331: DefaultLookUp.put("businesscategory", BUSINESS_CATEGORY);
0332: }
0333:
0334: private X509NameEntryConverter converter = null;
0335: private Vector ordering = new Vector();
0336: private Vector values = new Vector();
0337: private Vector added = new Vector();
0338:
0339: private ASN1Sequence seq;
0340:
0341: /**
0342: * Return a X509Name based on the passed in tagged object.
0343: *
0344: * @param obj tag object holding name.
0345: * @param explicit true if explicitly tagged false otherwise.
0346: * @return the X509Name
0347: */
0348: public static X509Name getInstance(ASN1TaggedObject obj,
0349: boolean explicit) {
0350: return getInstance(ASN1Sequence.getInstance(obj, explicit));
0351: }
0352:
0353: public static X509Name getInstance(Object obj) {
0354: if (obj == null || obj instanceof X509Name) {
0355: return (X509Name) obj;
0356: } else if (obj instanceof ASN1Sequence) {
0357: return new X509Name((ASN1Sequence) obj);
0358: }
0359:
0360: throw new IllegalArgumentException(
0361: "unknown object in factory \""
0362: + obj.getClass().getName() + "\"");
0363: }
0364:
0365: /**
0366: * Constructor from ASN1Sequence
0367: *
0368: * the principal will be a list of constructed sets, each containing an (OID, String) pair.
0369: */
0370: public X509Name(ASN1Sequence seq) {
0371: this .seq = seq;
0372:
0373: Enumeration e = seq.getObjects();
0374:
0375: while (e.hasMoreElements()) {
0376: ASN1Set set = ASN1Set.getInstance(e.nextElement());
0377:
0378: for (int i = 0; i < set.size(); i++) {
0379: ASN1Sequence s = ASN1Sequence.getInstance(set
0380: .getObjectAt(i));
0381:
0382: if (s.size() != 2) {
0383: throw new IllegalArgumentException(
0384: "badly sized pair");
0385: }
0386:
0387: ordering.addElement(DERObjectIdentifier.getInstance(s
0388: .getObjectAt(0)));
0389:
0390: DEREncodable value = s.getObjectAt(1);
0391: if (value instanceof DERString) {
0392: values.addElement(((DERString) value).getString());
0393: } else {
0394: values.addElement("#"
0395: + bytesToString(Hex.encode(value
0396: .getDERObject().getDEREncoded())));
0397: }
0398: added.addElement((i != 0) ? TRUE : FALSE); // to allow earlier JDK compatibility
0399: }
0400: }
0401: }
0402:
0403: /**
0404: * constructor from a table of attributes.
0405: * <p>
0406: * it's is assumed the table contains OID/String pairs, and the contents
0407: * of the table are copied into an internal table as part of the
0408: * construction process.
0409: * <p>
0410: * <b>Note:</b> if the name you are trying to generate should be
0411: * following a specific ordering, you should use the constructor
0412: * with the ordering specified below.
0413: * @deprecated use an ordered constructor! The hashtable ordering is rarely correct
0414: */
0415: public X509Name(Hashtable attributes) {
0416: this (null, attributes);
0417: }
0418:
0419: /**
0420: * Constructor from a table of attributes with ordering.
0421: * <p>
0422: * it's is assumed the table contains OID/String pairs, and the contents
0423: * of the table are copied into an internal table as part of the
0424: * construction process. The ordering vector should contain the OIDs
0425: * in the order they are meant to be encoded or printed in toString.
0426: */
0427: public X509Name(Vector ordering, Hashtable attributes) {
0428: this (ordering, attributes, new X509DefaultEntryConverter());
0429: }
0430:
0431: /**
0432: * Constructor from a table of attributes with ordering.
0433: * <p>
0434: * it's is assumed the table contains OID/String pairs, and the contents
0435: * of the table are copied into an internal table as part of the
0436: * construction process. The ordering vector should contain the OIDs
0437: * in the order they are meant to be encoded or printed in toString.
0438: * <p>
0439: * The passed in converter will be used to convert the strings into their
0440: * ASN.1 counterparts.
0441: */
0442: public X509Name(Vector ordering, Hashtable attributes,
0443: X509NameEntryConverter converter) {
0444: this .converter = converter;
0445:
0446: if (ordering != null) {
0447: for (int i = 0; i != ordering.size(); i++) {
0448: this .ordering.addElement(ordering.elementAt(i));
0449: this .added.addElement(FALSE);
0450: }
0451: } else {
0452: Enumeration e = attributes.keys();
0453:
0454: while (e.hasMoreElements()) {
0455: this .ordering.addElement(e.nextElement());
0456: this .added.addElement(FALSE);
0457: }
0458: }
0459:
0460: for (int i = 0; i != this .ordering.size(); i++) {
0461: DERObjectIdentifier oid = (DERObjectIdentifier) this .ordering
0462: .elementAt(i);
0463:
0464: if (attributes.get(oid) == null) {
0465: throw new IllegalArgumentException(
0466: "No attribute for object id - " + oid.getId()
0467: + " - passed to distinguished name");
0468: }
0469:
0470: this .values.addElement(attributes.get(oid)); // copy the hash table
0471: }
0472: }
0473:
0474: /**
0475: * Takes two vectors one of the oids and the other of the values.
0476: */
0477: public X509Name(Vector oids, Vector values) {
0478: this (oids, values, new X509DefaultEntryConverter());
0479: }
0480:
0481: /**
0482: * Takes two vectors one of the oids and the other of the values.
0483: * <p>
0484: * The passed in converter will be used to convert the strings into their
0485: * ASN.1 counterparts.
0486: */
0487: public X509Name(Vector oids, Vector values,
0488: X509NameEntryConverter converter) {
0489: this .converter = converter;
0490:
0491: if (oids.size() != values.size()) {
0492: throw new IllegalArgumentException(
0493: "oids vector must be same length as values.");
0494: }
0495:
0496: for (int i = 0; i < oids.size(); i++) {
0497: this .ordering.addElement(oids.elementAt(i));
0498: this .values.addElement(values.elementAt(i));
0499: this .added.addElement(FALSE);
0500: }
0501: }
0502:
0503: /**
0504: * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
0505: * some such, converting it into an ordered set of name attributes.
0506: */
0507: public X509Name(String dirName) {
0508: this (DefaultReverse, DefaultLookUp, dirName);
0509: }
0510:
0511: /**
0512: * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
0513: * some such, converting it into an ordered set of name attributes with each
0514: * string value being converted to its associated ASN.1 type using the passed
0515: * in converter.
0516: */
0517: public X509Name(String dirName, X509NameEntryConverter converter) {
0518: this (DefaultReverse, DefaultLookUp, dirName, converter);
0519: }
0520:
0521: /**
0522: * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
0523: * some such, converting it into an ordered set of name attributes. If reverse
0524: * is true, create the encoded version of the sequence starting from the
0525: * last element in the string.
0526: */
0527: public X509Name(boolean reverse, String dirName) {
0528: this (reverse, DefaultLookUp, dirName);
0529: }
0530:
0531: /**
0532: * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
0533: * some such, converting it into an ordered set of name attributes with each
0534: * string value being converted to its associated ASN.1 type using the passed
0535: * in converter. If reverse is true the ASN.1 sequence representing the DN will
0536: * be built by starting at the end of the string, rather than the start.
0537: */
0538: public X509Name(boolean reverse, String dirName,
0539: X509NameEntryConverter converter) {
0540: this (reverse, DefaultLookUp, dirName, converter);
0541: }
0542:
0543: /**
0544: * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
0545: * some such, converting it into an ordered set of name attributes. lookUp
0546: * should provide a table of lookups, indexed by lowercase only strings and
0547: * yielding a DERObjectIdentifier, other than that OID. and numeric oids
0548: * will be processed automatically.
0549: * <br>
0550: * If reverse is true, create the encoded version of the sequence
0551: * starting from the last element in the string.
0552: * @param reverse true if we should start scanning from the end (RFC 2553).
0553: * @param lookUp table of names and their oids.
0554: * @param dirName the X.500 string to be parsed.
0555: */
0556: public X509Name(boolean reverse, Hashtable lookUp, String dirName) {
0557: this (reverse, lookUp, dirName, new X509DefaultEntryConverter());
0558: }
0559:
0560: private DERObjectIdentifier decodeOID(String name, Hashtable lookUp) {
0561: if (Strings.toUpperCase(name).startsWith("OID.")) {
0562: return new DERObjectIdentifier(name.substring(4));
0563: } else if (name.charAt(0) >= '0' && name.charAt(0) <= '9') {
0564: return new DERObjectIdentifier(name);
0565: }
0566:
0567: DERObjectIdentifier oid = (DERObjectIdentifier) lookUp
0568: .get(Strings.toLowerCase(name));
0569: if (oid == null) {
0570: throw new IllegalArgumentException("Unknown object id - "
0571: + name + " - passed to distinguished name");
0572: }
0573:
0574: return oid;
0575: }
0576:
0577: /**
0578: * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
0579: * some such, converting it into an ordered set of name attributes. lookUp
0580: * should provide a table of lookups, indexed by lowercase only strings and
0581: * yielding a DERObjectIdentifier, other than that OID. and numeric oids
0582: * will be processed automatically. The passed in converter is used to convert the
0583: * string values to the right of each equals sign to their ASN.1 counterparts.
0584: * <br>
0585: * @param reverse true if we should start scanning from the end, false otherwise.
0586: * @param lookUp table of names and oids.
0587: * @param dirName the string dirName
0588: * @param converter the converter to convert string values into their ASN.1 equivalents
0589: */
0590: public X509Name(boolean reverse, Hashtable lookUp, String dirName,
0591: X509NameEntryConverter converter) {
0592: this .converter = converter;
0593: X509NameTokenizer nTok = new X509NameTokenizer(dirName);
0594:
0595: while (nTok.hasMoreTokens()) {
0596: String token = nTok.nextToken();
0597: int index = token.indexOf('=');
0598:
0599: if (index == -1) {
0600: throw new IllegalArgumentException(
0601: "badly formated directory string");
0602: }
0603:
0604: String name = token.substring(0, index);
0605: String value = token.substring(index + 1);
0606: DERObjectIdentifier oid = decodeOID(name, lookUp);
0607:
0608: if (value.indexOf('+') > 0) {
0609: X509NameTokenizer vTok = new X509NameTokenizer(value,
0610: '+');
0611:
0612: this .ordering.addElement(oid);
0613: this .values.addElement(vTok.nextToken());
0614: this .added.addElement(FALSE);
0615:
0616: while (vTok.hasMoreTokens()) {
0617: String sv = vTok.nextToken();
0618: int ndx = sv.indexOf('=');
0619:
0620: String nm = sv.substring(0, ndx);
0621: String vl = sv.substring(ndx + 1);
0622: this .ordering.addElement(decodeOID(nm, lookUp));
0623: this .values.addElement(vl);
0624: this .added.addElement(TRUE);
0625: }
0626: } else {
0627: this .ordering.addElement(oid);
0628: this .values.addElement(value);
0629: this .added.addElement(FALSE);
0630: }
0631: }
0632:
0633: if (reverse) {
0634: Vector o = new Vector();
0635: Vector v = new Vector();
0636: Vector a = new Vector();
0637: int count = 1;
0638:
0639: for (int i = 0; i < this .ordering.size(); i++) {
0640: if (((Boolean) this .added.elementAt(i)).booleanValue()) {
0641: o
0642: .insertElementAt(
0643: this .ordering.elementAt(i), count);
0644: v.insertElementAt(this .values.elementAt(i), count);
0645: a.insertElementAt(this .added.elementAt(i), count);
0646: count++;
0647: } else {
0648: o.insertElementAt(this .ordering.elementAt(i), 0);
0649: v.insertElementAt(this .values.elementAt(i), 0);
0650: a.insertElementAt(this .added.elementAt(i), 0);
0651: count = 1;
0652: }
0653: }
0654:
0655: this .ordering = o;
0656: this .values = v;
0657: this .added = a;
0658: }
0659: }
0660:
0661: /**
0662: * return a vector of the oids in the name, in the order they were found.
0663: */
0664: public Vector getOIDs() {
0665: Vector v = new Vector();
0666:
0667: for (int i = 0; i != ordering.size(); i++) {
0668: v.addElement(ordering.elementAt(i));
0669: }
0670:
0671: return v;
0672: }
0673:
0674: /**
0675: * return a vector of the values found in the name, in the order they
0676: * were found.
0677: */
0678: public Vector getValues() {
0679: Vector v = new Vector();
0680:
0681: for (int i = 0; i != values.size(); i++) {
0682: v.addElement(values.elementAt(i));
0683: }
0684:
0685: return v;
0686: }
0687:
0688: /**
0689: * return a vector of the values found in the name, in the order they
0690: * were found, with the DN label corresponding to passed in oid.
0691: */
0692: public Vector getValues(DERObjectIdentifier oid) {
0693: Vector v = new Vector();
0694:
0695: for (int i = 0; i != values.size(); i++) {
0696: if (ordering.elementAt(i).equals(oid)) {
0697: v.addElement(values.elementAt(i));
0698: }
0699: }
0700:
0701: return v;
0702: }
0703:
0704: public DERObject toASN1Object() {
0705: if (seq == null) {
0706: ASN1EncodableVector vec = new ASN1EncodableVector();
0707: ASN1EncodableVector sVec = new ASN1EncodableVector();
0708: DERObjectIdentifier lstOid = null;
0709:
0710: for (int i = 0; i != ordering.size(); i++) {
0711: ASN1EncodableVector v = new ASN1EncodableVector();
0712: DERObjectIdentifier oid = (DERObjectIdentifier) ordering
0713: .elementAt(i);
0714:
0715: v.add(oid);
0716:
0717: String str = (String) values.elementAt(i);
0718:
0719: v.add(converter.getConvertedValue(oid, str));
0720:
0721: if (lstOid == null
0722: || ((Boolean) this .added.elementAt(i))
0723: .booleanValue()) {
0724: sVec.add(new DERSequence(v));
0725: } else {
0726: vec.add(new DERSet(sVec));
0727: sVec = new ASN1EncodableVector();
0728:
0729: sVec.add(new DERSequence(v));
0730: }
0731:
0732: lstOid = oid;
0733: }
0734:
0735: vec.add(new DERSet(sVec));
0736:
0737: seq = new DERSequence(vec);
0738: }
0739:
0740: return seq;
0741: }
0742:
0743: /**
0744: * @param inOrder if true the order of both X509 names must be the same,
0745: * as well as the values associated with each element.
0746: */
0747: public boolean equals(Object obj, boolean inOrder) {
0748: if (!inOrder) {
0749: return this .equals(obj);
0750: }
0751:
0752: if (obj == this ) {
0753: return true;
0754: }
0755:
0756: if (!(obj instanceof X509Name || obj instanceof ASN1Sequence)) {
0757: return false;
0758: }
0759:
0760: DERObject derO = ((DEREncodable) obj).getDERObject();
0761:
0762: if (this .getDERObject().equals(derO)) {
0763: return true;
0764: }
0765:
0766: X509Name other;
0767:
0768: try {
0769: other = X509Name.getInstance(obj);
0770: } catch (IllegalArgumentException e) {
0771: return false;
0772: }
0773:
0774: int orderingSize = ordering.size();
0775:
0776: if (orderingSize != other.ordering.size()) {
0777: return false;
0778: }
0779:
0780: for (int i = 0; i < orderingSize; i++) {
0781: DERObjectIdentifier oid = (DERObjectIdentifier) ordering
0782: .elementAt(i);
0783: DERObjectIdentifier oOid = (DERObjectIdentifier) other.ordering
0784: .elementAt(i);
0785:
0786: if (oid.equals(oOid)) {
0787: String value = (String) values.elementAt(i);
0788: String oValue = (String) other.values.elementAt(i);
0789:
0790: if (!equivalentStrings(value, oValue)) {
0791: return false;
0792: }
0793: } else {
0794: return false;
0795: }
0796: }
0797:
0798: return true;
0799: }
0800:
0801: /**
0802: * test for equality - note: case is ignored.
0803: */
0804: public boolean equals(Object obj) {
0805: if (obj == this ) {
0806: return true;
0807: }
0808:
0809: if (!(obj instanceof X509Name || obj instanceof ASN1Sequence)) {
0810: return false;
0811: }
0812:
0813: DERObject derO = ((DEREncodable) obj).getDERObject();
0814:
0815: if (this .getDERObject().equals(derO)) {
0816: return true;
0817: }
0818:
0819: X509Name other;
0820:
0821: try {
0822: other = X509Name.getInstance(obj);
0823: } catch (IllegalArgumentException e) {
0824: return false;
0825: }
0826:
0827: int orderingSize = ordering.size();
0828:
0829: if (orderingSize != other.ordering.size()) {
0830: return false;
0831: }
0832:
0833: boolean[] indexes = new boolean[orderingSize];
0834: int start, end, delta;
0835:
0836: if (ordering.elementAt(0).equals(other.ordering.elementAt(0))) // guess forward
0837: {
0838: start = 0;
0839: end = orderingSize;
0840: delta = 1;
0841: } else // guess reversed - most common problem
0842: {
0843: start = orderingSize - 1;
0844: end = -1;
0845: delta = -1;
0846: }
0847:
0848: for (int i = start; i != end; i += delta) {
0849: boolean found = false;
0850: DERObjectIdentifier oid = (DERObjectIdentifier) ordering
0851: .elementAt(i);
0852: String value = (String) values.elementAt(i);
0853:
0854: for (int j = 0; j < orderingSize; j++) {
0855: if (indexes[j]) {
0856: continue;
0857: }
0858:
0859: DERObjectIdentifier oOid = (DERObjectIdentifier) other.ordering
0860: .elementAt(j);
0861:
0862: if (oid.equals(oOid)) {
0863: String oValue = (String) other.values.elementAt(j);
0864:
0865: if (equivalentStrings(value, oValue)) {
0866: indexes[j] = true;
0867: found = true;
0868: break;
0869: }
0870: }
0871: }
0872:
0873: if (!found) {
0874: return false;
0875: }
0876: }
0877:
0878: return true;
0879: }
0880:
0881: private boolean equivalentStrings(String s1, String s2) {
0882: String value = Strings.toLowerCase(s1.trim());
0883: String oValue = Strings.toLowerCase(s2.trim());
0884:
0885: if (!value.equals(oValue)) {
0886: value = stripInternalSpaces(value);
0887: oValue = stripInternalSpaces(oValue);
0888:
0889: if (!value.equals(oValue)) {
0890: return false;
0891: }
0892: }
0893:
0894: return true;
0895: }
0896:
0897: private String stripInternalSpaces(String str) {
0898: StringBuffer res = new StringBuffer();
0899:
0900: if (str.length() != 0) {
0901: char c1 = str.charAt(0);
0902:
0903: res.append(c1);
0904:
0905: for (int k = 1; k < str.length(); k++) {
0906: char c2 = str.charAt(k);
0907: if (!(c1 == ' ' && c2 == ' ')) {
0908: res.append(c2);
0909: }
0910: c1 = c2;
0911: }
0912: }
0913:
0914: return res.toString();
0915: }
0916:
0917: public int hashCode() {
0918: ASN1Sequence seq = (ASN1Sequence) this .getDERObject();
0919: Enumeration e = seq.getObjects();
0920: int hashCode = 0;
0921:
0922: while (e.hasMoreElements()) {
0923: hashCode ^= e.nextElement().hashCode();
0924: }
0925:
0926: return hashCode;
0927: }
0928:
0929: private void appendValue(StringBuffer buf, Hashtable oidSymbols,
0930: DERObjectIdentifier oid, String value) {
0931: String sym = (String) oidSymbols.get(oid);
0932:
0933: if (sym != null) {
0934: buf.append(sym);
0935: } else {
0936: buf.append(oid.getId());
0937: }
0938:
0939: buf.append('=');
0940:
0941: int index = buf.length();
0942:
0943: buf.append(value);
0944:
0945: int end = buf.length();
0946:
0947: while (index != end) {
0948: if ((buf.charAt(index) == ',')
0949: || (buf.charAt(index) == '"')
0950: || (buf.charAt(index) == '\\')
0951: || (buf.charAt(index) == '+')
0952: || (buf.charAt(index) == '<')
0953: || (buf.charAt(index) == '>')
0954: || (buf.charAt(index) == ';')) {
0955: buf.insert(index, "\\");
0956: index++;
0957: end++;
0958: }
0959:
0960: index++;
0961: }
0962: }
0963:
0964: /**
0965: * convert the structure to a string - if reverse is true the
0966: * oids and values are listed out starting with the last element
0967: * in the sequence (ala RFC 2253), otherwise the string will begin
0968: * with the first element of the structure. If no string definition
0969: * for the oid is found in oidSymbols the string value of the oid is
0970: * added. Two standard symbol tables are provided DefaultSymbols, and
0971: * RFC2253Symbols as part of this class.
0972: *
0973: * @param reverse if true start at the end of the sequence and work back.
0974: * @param oidSymbols look up table strings for oids.
0975: */
0976: public String toString(boolean reverse, Hashtable oidSymbols) {
0977: StringBuffer buf = new StringBuffer();
0978: Vector components = new Vector();
0979: boolean first = true;
0980:
0981: StringBuffer ava = null;
0982:
0983: for (int i = 0; i < ordering.size(); i++) {
0984: if (((Boolean) added.elementAt(i)).booleanValue()) {
0985: ava.append('+');
0986: appendValue(ava, oidSymbols,
0987: (DERObjectIdentifier) ordering.elementAt(i),
0988: (String) values.elementAt(i));
0989: } else {
0990: ava = new StringBuffer();
0991: appendValue(ava, oidSymbols,
0992: (DERObjectIdentifier) ordering.elementAt(i),
0993: (String) values.elementAt(i));
0994: components.addElement(ava);
0995: }
0996: }
0997:
0998: if (reverse) {
0999: for (int i = components.size() - 1; i >= 0; i--) {
1000: if (first) {
1001: first = false;
1002: } else {
1003: buf.append(',');
1004: }
1005:
1006: buf.append(components.elementAt(i).toString());
1007: }
1008: } else {
1009: for (int i = 0; i < components.size(); i++) {
1010: if (first) {
1011: first = false;
1012: } else {
1013: buf.append(',');
1014: }
1015:
1016: buf.append(components.elementAt(i).toString());
1017: }
1018: }
1019:
1020: return buf.toString();
1021: }
1022:
1023: private String bytesToString(byte[] data) {
1024: char[] cs = new char[data.length];
1025:
1026: for (int i = 0; i != cs.length; i++) {
1027: cs[i] = (char) (data[i] & 0xff);
1028: }
1029:
1030: return new String(cs);
1031: }
1032:
1033: public String toString() {
1034: return toString(DefaultReverse, DefaultSymbols);
1035: }
1036: }
|