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