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