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: */package org.apache.geronimo.crypto.asn1.x509;
017:
018: import java.util.Enumeration;
019: import java.util.Hashtable;
020: import java.util.Vector;
021:
022: import org.apache.geronimo.crypto.asn1.*;
023: import org.apache.geronimo.crypto.asn1.pkcs.PKCSObjectIdentifiers;
024:
025: /**
026: * <pre>
027: * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
028: *
029: * RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
030: *
031: * AttributeTypeAndValue ::= SEQUENCE {
032: * type OBJECT IDENTIFIER,
033: * value ANY }
034: * </pre>
035: */
036: public class X509Name extends ASN1Encodable {
037: /**
038: * country code - StringType(SIZE(2))
039: */
040: public static final DERObjectIdentifier C = new DERObjectIdentifier(
041: "2.5.4.6");
042:
043: /**
044: * organization - StringType(SIZE(1..64))
045: */
046: public static final DERObjectIdentifier O = new DERObjectIdentifier(
047: "2.5.4.10");
048:
049: /**
050: * organizational unit name - StringType(SIZE(1..64))
051: */
052: public static final DERObjectIdentifier OU = new DERObjectIdentifier(
053: "2.5.4.11");
054:
055: /**
056: * Title
057: */
058: public static final DERObjectIdentifier T = new DERObjectIdentifier(
059: "2.5.4.12");
060:
061: /**
062: * common name - StringType(SIZE(1..64))
063: */
064: public static final DERObjectIdentifier CN = new DERObjectIdentifier(
065: "2.5.4.3");
066:
067: /**
068: * device serial number name - StringType(SIZE(1..64))
069: */
070: public static final DERObjectIdentifier SN = new DERObjectIdentifier(
071: "2.5.4.5");
072:
073: /**
074: * locality name - StringType(SIZE(1..64))
075: */
076: public static final DERObjectIdentifier L = new DERObjectIdentifier(
077: "2.5.4.7");
078:
079: /**
080: * state, or province name - StringType(SIZE(1..64))
081: */
082: public static final DERObjectIdentifier ST = new DERObjectIdentifier(
083: "2.5.4.8");
084:
085: /**
086: * Naming attributes of type X520name
087: */
088: public static final DERObjectIdentifier SURNAME = new DERObjectIdentifier(
089: "2.5.4.4");
090: public static final DERObjectIdentifier GIVENNAME = new DERObjectIdentifier(
091: "2.5.4.42");
092: public static final DERObjectIdentifier INITIALS = new DERObjectIdentifier(
093: "2.5.4.43");
094: public static final DERObjectIdentifier GENERATION = new DERObjectIdentifier(
095: "2.5.4.44");
096: public static final DERObjectIdentifier UNIQUE_IDENTIFIER = new DERObjectIdentifier(
097: "2.5.4.45");
098:
099: /**
100: * Email address (RSA PKCS#9 extension) - IA5String.
101: * <p>Note: if you're trying to be ultra orthodox, don't use this! It shouldn't be in here.
102: */
103: public static final DERObjectIdentifier EmailAddress = PKCSObjectIdentifiers.pkcs_9_at_emailAddress;
104:
105: /**
106: * more from PKCS#9
107: */
108: public static final DERObjectIdentifier UnstructuredName = PKCSObjectIdentifiers.pkcs_9_at_unstructuredName;
109: public static final DERObjectIdentifier UnstructuredAddress = PKCSObjectIdentifiers.pkcs_9_at_unstructuredAddress;
110:
111: /**
112: * email address in Verisign certificates
113: */
114: public static final DERObjectIdentifier E = EmailAddress;
115:
116: /*
117: * others...
118: */
119: public static final DERObjectIdentifier DC = new DERObjectIdentifier(
120: "0.9.2342.19200300.100.1.25");
121:
122: /**
123: * LDAP User id.
124: */
125: public static final DERObjectIdentifier UID = new DERObjectIdentifier(
126: "0.9.2342.19200300.100.1.1");
127:
128: /**
129: * look up table translating OID values into their common symbols - this static is scheduled for deletion
130: */
131: public static Hashtable OIDLookUp = new Hashtable();
132:
133: /**
134: * determines whether or not strings should be processed and printed
135: * from back to front.
136: */
137: public static boolean DefaultReverse = false;
138:
139: /**
140: * default look up table translating OID values into their common symbols following
141: * the convention in RFC 2253 with a few extras
142: */
143: public static Hashtable DefaultSymbols = OIDLookUp;
144:
145: /**
146: * look up table translating OID values into their common symbols following the convention in RFC 2253
147: * with a few extras
148: */
149: public static Hashtable RFC2253Symbols = new Hashtable();
150:
151: /**
152: * look up table translating string values into their OIDS -
153: * this static is scheduled for deletion
154: */
155: public static Hashtable SymbolLookUp = new Hashtable();
156:
157: /**
158: * look up table translating common symbols into their OIDS.
159: */
160: public static Hashtable DefaultLookUp = SymbolLookUp;
161:
162: static {
163: DefaultSymbols.put(C, "C");
164: DefaultSymbols.put(O, "O");
165: DefaultSymbols.put(T, "T");
166: DefaultSymbols.put(OU, "OU");
167: DefaultSymbols.put(CN, "CN");
168: DefaultSymbols.put(L, "L");
169: DefaultSymbols.put(ST, "ST");
170: DefaultSymbols.put(SN, "SN");
171: DefaultSymbols.put(EmailAddress, "E");
172: DefaultSymbols.put(DC, "DC");
173: DefaultSymbols.put(UID, "UID");
174: DefaultSymbols.put(SURNAME, "SURNAME");
175: DefaultSymbols.put(GIVENNAME, "GIVENNAME");
176: DefaultSymbols.put(INITIALS, "INITIALS");
177: DefaultSymbols.put(GENERATION, "GENERATION");
178: DefaultSymbols.put(UnstructuredAddress, "unstructuredAddress");
179: DefaultSymbols.put(UnstructuredName, "unstructuredName");
180:
181: RFC2253Symbols.put(C, "C");
182: RFC2253Symbols.put(O, "O");
183: RFC2253Symbols.put(T, "T");
184: RFC2253Symbols.put(OU, "OU");
185: RFC2253Symbols.put(CN, "CN");
186: RFC2253Symbols.put(L, "L");
187: RFC2253Symbols.put(ST, "ST");
188: RFC2253Symbols.put(SN, "SN");
189: RFC2253Symbols.put(EmailAddress, "EMAILADDRESS");
190: RFC2253Symbols.put(DC, "DC");
191: RFC2253Symbols.put(UID, "UID");
192: RFC2253Symbols.put(SURNAME, "SURNAME");
193: RFC2253Symbols.put(GIVENNAME, "GIVENNAME");
194: RFC2253Symbols.put(INITIALS, "INITIALS");
195: RFC2253Symbols.put(GENERATION, "GENERATION");
196:
197: DefaultLookUp.put("c", C);
198: DefaultLookUp.put("o", O);
199: DefaultLookUp.put("t", T);
200: DefaultLookUp.put("ou", OU);
201: DefaultLookUp.put("cn", CN);
202: DefaultLookUp.put("l", L);
203: DefaultLookUp.put("st", ST);
204: DefaultLookUp.put("sn", SN);
205: DefaultLookUp.put("emailaddress", E);
206: DefaultLookUp.put("dc", DC);
207: DefaultLookUp.put("e", E);
208: DefaultLookUp.put("uid", UID);
209: DefaultLookUp.put("surname", SURNAME);
210: DefaultLookUp.put("givenname", GIVENNAME);
211: DefaultLookUp.put("initials", INITIALS);
212: DefaultLookUp.put("generation", GENERATION);
213: DefaultLookUp.put("unstructuredaddress", UnstructuredAddress);
214: DefaultLookUp.put("unstructuredname", UnstructuredName);
215: }
216:
217: private X509NameEntryConverter converter = null;
218: private Vector ordering = new Vector();
219: private Vector values = new Vector();
220: private Vector added = new Vector();
221:
222: private ASN1Sequence seq;
223:
224: /**
225: * Return a X509Name based on the passed in tagged object.
226: *
227: * @param obj tag object holding name.
228: * @param explicit true if explicitly tagged false otherwise.
229: * @return the X509Name
230: */
231: public static X509Name getInstance(ASN1TaggedObject obj,
232: boolean explicit) {
233: return getInstance(ASN1Sequence.getInstance(obj, explicit));
234: }
235:
236: public static X509Name getInstance(Object obj) {
237: if (obj == null || obj instanceof X509Name) {
238: return (X509Name) obj;
239: } else if (obj instanceof ASN1Sequence) {
240: return new X509Name((ASN1Sequence) obj);
241: }
242:
243: throw new IllegalArgumentException("unknown object in factory");
244: }
245:
246: /**
247: * Constructor from ASN1Sequence
248: *
249: * the principal will be a list of constructed sets, each containing an (OID, String) pair.
250: */
251: public X509Name(ASN1Sequence seq) {
252: this .seq = seq;
253:
254: Enumeration e = seq.getObjects();
255:
256: while (e.hasMoreElements()) {
257: ASN1Set set = (ASN1Set) e.nextElement();
258:
259: for (int i = 0; i < set.size(); i++) {
260: ASN1Sequence s = (ASN1Sequence) set.getObjectAt(i);
261:
262: ordering.addElement(s.getObjectAt(0));
263: values.addElement(((DERString) s.getObjectAt(1))
264: .getString());
265: added.addElement((i != 0) ? new Boolean(true)
266: : new Boolean(false));
267: }
268: }
269: }
270:
271: /**
272: * constructor from a table of attributes.
273: * <p>
274: * it's is assumed the table contains OID/String pairs, and the contents
275: * of the table are copied into an internal table as part of the
276: * construction process.
277: * <p>
278: * <b>Note:</b> if the name you are trying to generate should be
279: * following a specific ordering, you should use the constructor
280: * with the ordering specified below.
281: */
282: public X509Name(Hashtable attributes) {
283: this (null, attributes);
284: }
285:
286: /**
287: * Constructor from a table of attributes with ordering.
288: * <p>
289: * it's is assumed the table contains OID/String pairs, and the contents
290: * of the table are copied into an internal table as part of the
291: * construction process. The ordering vector should contain the OIDs
292: * in the order they are meant to be encoded or printed in toString.
293: */
294: public X509Name(Vector ordering, Hashtable attributes) {
295: this (ordering, attributes, new X509DefaultEntryConverter());
296: }
297:
298: /**
299: * Constructor from a table of attributes with ordering.
300: * <p>
301: * it's is assumed the table contains OID/String pairs, and the contents
302: * of the table are copied into an internal table as part of the
303: * construction process. The ordering vector should contain the OIDs
304: * in the order they are meant to be encoded or printed in toString.
305: * <p>
306: * The passed in converter will be used to convert the strings into their
307: * ASN.1 counterparts.
308: */
309: public X509Name(Vector ordering, Hashtable attributes,
310: X509DefaultEntryConverter converter) {
311: this .converter = converter;
312:
313: if (ordering != null) {
314: for (int i = 0; i != ordering.size(); i++) {
315: this .ordering.addElement(ordering.elementAt(i));
316: this .added.addElement(new Boolean(false));
317: }
318: } else {
319: Enumeration e = attributes.keys();
320:
321: while (e.hasMoreElements()) {
322: this .ordering.addElement(e.nextElement());
323: this .added.addElement(new Boolean(false));
324: }
325: }
326:
327: for (int i = 0; i != this .ordering.size(); i++) {
328: DERObjectIdentifier oid = (DERObjectIdentifier) this .ordering
329: .elementAt(i);
330:
331: if (attributes.get(oid) == null) {
332: throw new IllegalArgumentException(
333: "No attribute for object id - " + oid.getId()
334: + " - passed to distinguished name");
335: }
336:
337: this .values.addElement(attributes.get(oid)); // copy the hash table
338: }
339: }
340:
341: /**
342: * Takes two vectors one of the oids and the other of the values.
343: */
344: public X509Name(Vector oids, Vector values) {
345: this (oids, values, new X509DefaultEntryConverter());
346: }
347:
348: /**
349: * Takes two vectors one of the oids and the other of the values.
350: * <p>
351: * The passed in converter will be used to convert the strings into their
352: * ASN.1 counterparts.
353: */
354: public X509Name(Vector oids, Vector values,
355: X509NameEntryConverter converter) {
356: this .converter = converter;
357:
358: if (oids.size() != values.size()) {
359: throw new IllegalArgumentException(
360: "oids vector must be same length as values.");
361: }
362:
363: for (int i = 0; i < oids.size(); i++) {
364: this .ordering.addElement(oids.elementAt(i));
365: this .values.addElement(values.elementAt(i));
366: this .added.addElement(new Boolean(false));
367: }
368: }
369:
370: /**
371: * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
372: * some such, converting it into an ordered set of name attributes.
373: */
374: public X509Name(String dirName) {
375: this (DefaultReverse, DefaultLookUp, dirName);
376: }
377:
378: /**
379: * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
380: * some such, converting it into an ordered set of name attributes with each
381: * string value being converted to its associated ASN.1 type using the passed
382: * in converter.
383: */
384: public X509Name(String dirName, X509NameEntryConverter converter) {
385: this (DefaultReverse, DefaultLookUp, dirName, converter);
386: }
387:
388: /**
389: * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
390: * some such, converting it into an ordered set of name attributes. If reverse
391: * is true, create the encoded version of the sequence starting from the
392: * last element in the string.
393: */
394: public X509Name(boolean reverse, String dirName) {
395: this (reverse, DefaultLookUp, dirName);
396: }
397:
398: /**
399: * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
400: * some such, converting it into an ordered set of name attributes with each
401: * string value being converted to its associated ASN.1 type using the passed
402: * in converter. If reverse is true the ASN.1 sequence representing the DN will
403: * be built by starting at the end of the string, rather than the start.
404: */
405: public X509Name(boolean reverse, String dirName,
406: X509NameEntryConverter converter) {
407: this (reverse, DefaultLookUp, dirName, converter);
408: }
409:
410: /**
411: * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
412: * some such, converting it into an ordered set of name attributes. lookUp
413: * should provide a table of lookups, indexed by lowercase only strings and
414: * yielding a DERObjectIdentifier, other than that OID. and numeric oids
415: * will be processed automatically.
416: * <br>
417: * If reverse is true, create the encoded version of the sequence
418: * starting from the last element in the string.
419: * @param reverse true if we should start scanning from the end (RFC 2553).
420: * @param lookUp table of names and their oids.
421: * @param dirName the X.500 string to be parsed.
422: */
423: public X509Name(boolean reverse, Hashtable lookUp, String dirName) {
424: this (reverse, lookUp, dirName, new X509DefaultEntryConverter());
425: }
426:
427: private DERObjectIdentifier decodeOID(String name, Hashtable lookUp) {
428: if (name.toUpperCase().startsWith("OID.")) {
429: return new DERObjectIdentifier(name.substring(4));
430: } else if (name.charAt(0) >= '0' && name.charAt(0) <= '9') {
431: return new DERObjectIdentifier(name);
432: }
433:
434: DERObjectIdentifier oid = (DERObjectIdentifier) lookUp.get(name
435: .toLowerCase());
436: if (oid == null) {
437: throw new IllegalArgumentException("Unknown object id - "
438: + name + " - passed to distinguished name");
439: }
440:
441: return oid;
442: }
443:
444: /**
445: * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
446: * some such, converting it into an ordered set of name attributes. lookUp
447: * should provide a table of lookups, indexed by lowercase only strings and
448: * yielding a DERObjectIdentifier, other than that OID. and numeric oids
449: * will be processed automatically. The passed in converter is used to convert the
450: * string values to the right of each equals sign to their ASN.1 counterparts.
451: * <br>
452: * @param reverse true if we should start scanning from the end, false otherwise.
453: * @param lookUp table of names and oids.
454: * @param dirName the string dirName
455: * @param converter the converter to convert string values into their ASN.1 equivalents
456: */
457: public X509Name(boolean reverse, Hashtable lookUp, String dirName,
458: X509NameEntryConverter converter) {
459: this .converter = converter;
460: X509NameTokenizer nTok = new X509NameTokenizer(dirName);
461:
462: while (nTok.hasMoreTokens()) {
463: String token = nTok.nextToken();
464: int index = token.indexOf('=');
465:
466: if (index == -1) {
467: throw new IllegalArgumentException(
468: "badly formated directory string");
469: }
470:
471: String name = token.substring(0, index);
472: String value = token.substring(index + 1);
473: DERObjectIdentifier oid = decodeOID(name, lookUp);
474:
475: if (value.indexOf('+') > 0) {
476: X509NameTokenizer vTok = new X509NameTokenizer(value,
477: '+');
478:
479: this .ordering.addElement(oid);
480: this .values.addElement(vTok.nextToken());
481: this .added.addElement(new Boolean(false));
482:
483: while (vTok.hasMoreTokens()) {
484: String sv = vTok.nextToken();
485: int ndx = sv.indexOf('=');
486:
487: String nm = sv.substring(0, ndx);
488: String vl = sv.substring(ndx + 1);
489: this .ordering.addElement(decodeOID(nm, lookUp));
490: this .values.addElement(vl);
491: this .added.addElement(new Boolean(true));
492: }
493: } else {
494: this .ordering.addElement(oid);
495: this .values.addElement(value);
496: this .added.addElement(new Boolean(false));
497: }
498: }
499:
500: if (reverse) {
501: Vector o = new Vector();
502: Vector v = new Vector();
503: Vector a = new Vector();
504:
505: for (int i = this .ordering.size() - 1; i >= 0; i--) {
506: o.addElement(this .ordering.elementAt(i));
507: v.addElement(this .values.elementAt(i));
508: a.addElement(this .added.elementAt(i));
509: }
510:
511: this .ordering = o;
512: this .values = v;
513: this .added = a;
514: }
515: }
516:
517: /**
518: * return a vector of the oids in the name, in the order they were found.
519: */
520: public Vector getOIDs() {
521: Vector v = new Vector();
522:
523: for (int i = 0; i != ordering.size(); i++) {
524: v.addElement(ordering.elementAt(i));
525: }
526:
527: return v;
528: }
529:
530: /**
531: * return a vector of the values found in the name, in the order they
532: * were found.
533: */
534: public Vector getValues() {
535: Vector v = new Vector();
536:
537: for (int i = 0; i != values.size(); i++) {
538: v.addElement(values.elementAt(i));
539: }
540:
541: return v;
542: }
543:
544: public DERObject toASN1Object() {
545: if (seq == null) {
546: ASN1EncodableVector vec = new ASN1EncodableVector();
547: ASN1EncodableVector sVec = new ASN1EncodableVector();
548: DERObjectIdentifier lstOid = null;
549:
550: for (int i = 0; i != ordering.size(); i++) {
551: ASN1EncodableVector v = new ASN1EncodableVector();
552: DERObjectIdentifier oid = (DERObjectIdentifier) ordering
553: .elementAt(i);
554:
555: v.add(oid);
556:
557: String str = (String) values.elementAt(i);
558:
559: v.add(converter.getConvertedValue(oid, str));
560:
561: if (lstOid == null
562: || ((Boolean) this .added.elementAt(i))
563: .booleanValue()) {
564: sVec.add(new DERSequence(v));
565: } else {
566: vec.add(new DERSet(sVec));
567: sVec = new ASN1EncodableVector();
568:
569: sVec.add(new DERSequence(v));
570: }
571:
572: lstOid = oid;
573: }
574:
575: vec.add(new DERSet(sVec));
576:
577: seq = new DERSequence(vec);
578: }
579:
580: return seq;
581: }
582:
583: /**
584: * @param inOrder if true the order of both X509 names must be the same,
585: * as well as the values associated with each element.
586: */
587: public boolean equals(Object _obj, boolean inOrder) {
588: if (_obj == this ) {
589: return true;
590: }
591:
592: if (!inOrder) {
593: return this .equals(_obj);
594: }
595:
596: if (_obj == null || !(_obj instanceof X509Name)) {
597: return false;
598: }
599:
600: X509Name _oxn = (X509Name) _obj;
601: int _orderingSize = ordering.size();
602:
603: if (_orderingSize != _oxn.ordering.size()) {
604: return false;
605: }
606:
607: for (int i = 0; i < _orderingSize; i++) {
608: String _oid = ((DERObjectIdentifier) ordering.elementAt(i))
609: .getId();
610: String _val = (String) values.elementAt(i);
611:
612: String _oOID = ((DERObjectIdentifier) _oxn.ordering
613: .elementAt(i)).getId();
614: String _oVal = (String) _oxn.values.elementAt(i);
615:
616: if (_oid.equals(_oOID)) {
617: _val = _val.trim().toLowerCase();
618: _oVal = _oVal.trim().toLowerCase();
619: if (_val.equals(_oVal)) {
620: continue;
621: } else {
622: StringBuffer v1 = new StringBuffer();
623: StringBuffer v2 = new StringBuffer();
624:
625: if (_val.length() != 0) {
626: char c1 = _val.charAt(0);
627:
628: v1.append(c1);
629:
630: for (int k = 1; k < _val.length(); k++) {
631: char c2 = _val.charAt(k);
632: if (!(c1 == ' ' && c2 == ' ')) {
633: v1.append(c2);
634: }
635: c1 = c2;
636: }
637: }
638:
639: if (_oVal.length() != 0) {
640: char c1 = _oVal.charAt(0);
641:
642: v2.append(c1);
643:
644: for (int k = 1; k < _oVal.length(); k++) {
645: char c2 = _oVal.charAt(k);
646: if (!(c1 == ' ' && c2 == ' ')) {
647: v2.append(c2);
648: }
649: c1 = c2;
650: }
651: }
652:
653: if (!v1.toString().equals(v2.toString())) {
654: return false;
655: }
656: }
657: } else {
658: return false;
659: }
660: }
661:
662: return true;
663: }
664:
665: /**
666: * test for equality - note: case is ignored.
667: */
668: public boolean equals(Object _obj) {
669: if (_obj == this ) {
670: return true;
671: }
672:
673: if (_obj == null || !(_obj instanceof X509Name)) {
674: return false;
675: }
676:
677: X509Name _oxn = (X509Name) _obj;
678:
679: if (this .getDERObject().equals(_oxn.getDERObject())) {
680: return true;
681: }
682:
683: int _orderingSize = ordering.size();
684:
685: if (_orderingSize != _oxn.ordering.size()) {
686: return false;
687: }
688:
689: boolean[] _indexes = new boolean[_orderingSize];
690:
691: for (int i = 0; i < _orderingSize; i++) {
692: boolean _found = false;
693: String _oid = ((DERObjectIdentifier) ordering.elementAt(i))
694: .getId();
695: String _val = (String) values.elementAt(i);
696:
697: for (int j = 0; j < _orderingSize; j++) {
698: if (_indexes[j] == true) {
699: continue;
700: }
701:
702: String _oOID = ((DERObjectIdentifier) _oxn.ordering
703: .elementAt(j)).getId();
704: String _oVal = (String) _oxn.values.elementAt(j);
705:
706: if (_oid.equals(_oOID)) {
707: _val = _val.trim().toLowerCase();
708: _oVal = _oVal.trim().toLowerCase();
709: if (_val.equals(_oVal)) {
710: _indexes[j] = true;
711: _found = true;
712: break;
713: } else {
714: StringBuffer v1 = new StringBuffer();
715: StringBuffer v2 = new StringBuffer();
716:
717: if (_val.length() != 0) {
718: char c1 = _val.charAt(0);
719:
720: v1.append(c1);
721:
722: for (int k = 1; k < _val.length(); k++) {
723: char c2 = _val.charAt(k);
724: if (!(c1 == ' ' && c2 == ' ')) {
725: v1.append(c2);
726: }
727: c1 = c2;
728: }
729: }
730:
731: if (_oVal.length() != 0) {
732: char c1 = _oVal.charAt(0);
733:
734: v2.append(c1);
735:
736: for (int k = 1; k < _oVal.length(); k++) {
737: char c2 = _oVal.charAt(k);
738: if (!(c1 == ' ' && c2 == ' ')) {
739: v2.append(c2);
740: }
741: c1 = c2;
742: }
743: }
744:
745: if (v1.toString().equals(v2.toString())) {
746: _indexes[j] = true;
747: _found = true;
748: break;
749: }
750: }
751: }
752: }
753:
754: if (!_found) {
755: return false;
756: }
757: }
758:
759: return true;
760: }
761:
762: public int hashCode() {
763: ASN1Sequence seq = (ASN1Sequence) this .getDERObject();
764: Enumeration e = seq.getObjects();
765: int hashCode = 0;
766:
767: while (e.hasMoreElements()) {
768: hashCode ^= e.nextElement().hashCode();
769: }
770:
771: return hashCode;
772: }
773:
774: private void appendValue(StringBuffer buf, Hashtable oidSymbols,
775: DERObjectIdentifier oid, String value) {
776: String sym = (String) oidSymbols.get(oid);
777:
778: if (sym != null) {
779: buf.append(sym);
780: } else {
781: buf.append(oid.getId());
782: }
783:
784: buf.append("=");
785:
786: int index = buf.length();
787:
788: buf.append(value);
789:
790: int end = buf.length();
791:
792: while (index != end) {
793: if ((buf.charAt(index) == ',')
794: || (buf.charAt(index) == '"')
795: || (buf.charAt(index) == '\\')
796: || (buf.charAt(index) == '+')
797: || (buf.charAt(index) == '<')
798: || (buf.charAt(index) == '>')
799: || (buf.charAt(index) == ';')) {
800: buf.insert(index, "\\");
801: index++;
802: end++;
803: }
804:
805: index++;
806: }
807: }
808:
809: /**
810: * convert the structure to a string - if reverse is true the
811: * oids and values are listed out starting with the last element
812: * in the sequence (ala RFC 2253), otherwise the string will begin
813: * with the first element of the structure. If no string definition
814: * for the oid is found in oidSymbols the string value of the oid is
815: * added. Two standard symbol tables are provided DefaultSymbols, and
816: * RFC2253Symbols as part of this class.
817: *
818: * @param reverse if true start at the end of the sequence and work back.
819: * @param oidSymbols look up table strings for oids.
820: */
821: public String toString(boolean reverse, Hashtable oidSymbols) {
822: StringBuffer buf = new StringBuffer();
823: boolean first = true;
824:
825: if (reverse) {
826: for (int i = ordering.size() - 1; i >= 0; i--) {
827: if (first) {
828: first = false;
829: } else {
830: if (((Boolean) added.elementAt(i + 1))
831: .booleanValue()) {
832: buf.append("+");
833: } else {
834: buf.append(",");
835: }
836: }
837:
838: appendValue(buf, oidSymbols,
839: (DERObjectIdentifier) ordering.elementAt(i),
840: (String) values.elementAt(i));
841: }
842: } else {
843: for (int i = 0; i < ordering.size(); i++) {
844: if (first) {
845: first = false;
846: } else {
847: if (((Boolean) added.elementAt(i)).booleanValue()) {
848: buf.append("+");
849: } else {
850: buf.append(",");
851: }
852: }
853:
854: appendValue(buf, oidSymbols,
855: (DERObjectIdentifier) ordering.elementAt(i),
856: (String) values.elementAt(i));
857: }
858: }
859:
860: return buf.toString();
861: }
862:
863: public String toString() {
864: return toString(DefaultReverse, DefaultSymbols);
865: }
866: }
|