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    }