001    /**
002     *
003     *  Licensed to the Apache Software Foundation (ASF) under one or more
004     *  contributor license agreements.  See the NOTICE file distributed with
005     *  this work for additional information regarding copyright ownership.
006     *  The ASF licenses this file to You under the Apache License, Version 2.0
007     *  (the "License"); you may not use this file except in compliance with
008     *  the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     *  Unless required by applicable law or agreed to in writing, software
013     *  distributed under the License is distributed on an "AS IS" BASIS,
014     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     *  See the License for the specific language governing permissions and
016     *  limitations under the License.
017     */
018    
019    package org.apache.geronimo.util.asn1.x509;
020    
021    import java.util.Enumeration;
022    import java.util.Hashtable;
023    import java.util.Vector;
024    
025    import org.apache.geronimo.util.asn1.*;
026    import org.apache.geronimo.util.asn1.pkcs.PKCSObjectIdentifiers;
027    
028    /**
029     * <pre>
030     *     RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
031     *
032     *     RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
033     *
034     *     AttributeTypeAndValue ::= SEQUENCE {
035     *                                   type  OBJECT IDENTIFIER,
036     *                                   value ANY }
037     * </pre>
038     */
039    public class X509Name
040        extends ASN1Encodable
041    {
042        /**
043         * country code - StringType(SIZE(2))
044         */
045        public static final DERObjectIdentifier C = new DERObjectIdentifier("2.5.4.6");
046    
047        /**
048         * organization - StringType(SIZE(1..64))
049         */
050        public static final DERObjectIdentifier O = new DERObjectIdentifier("2.5.4.10");
051    
052        /**
053         * organizational unit name - StringType(SIZE(1..64))
054         */
055        public static final DERObjectIdentifier OU = new DERObjectIdentifier("2.5.4.11");
056    
057        /**
058         * Title
059         */
060        public static final DERObjectIdentifier T = new DERObjectIdentifier("2.5.4.12");
061    
062        /**
063         * common name - StringType(SIZE(1..64))
064         */
065        public static final DERObjectIdentifier CN = new DERObjectIdentifier("2.5.4.3");
066    
067        /**
068         * device serial number name - StringType(SIZE(1..64))
069         */
070        public static final DERObjectIdentifier SN = new DERObjectIdentifier("2.5.4.5");
071    
072        /**
073         * locality name - StringType(SIZE(1..64))
074         */
075        public static final DERObjectIdentifier L = new DERObjectIdentifier("2.5.4.7");
076    
077        /**
078         * state, or province name - StringType(SIZE(1..64))
079         */
080        public static final DERObjectIdentifier ST = new DERObjectIdentifier("2.5.4.8");
081    
082        /**
083         * Naming attributes of type X520name
084         */
085        public static final DERObjectIdentifier SURNAME = new DERObjectIdentifier("2.5.4.4");
086        public static final DERObjectIdentifier GIVENNAME = new DERObjectIdentifier("2.5.4.42");
087        public static final DERObjectIdentifier INITIALS = new DERObjectIdentifier("2.5.4.43");
088        public static final DERObjectIdentifier GENERATION = new DERObjectIdentifier("2.5.4.44");
089        public static final DERObjectIdentifier UNIQUE_IDENTIFIER = new DERObjectIdentifier("2.5.4.45");
090    
091        /**
092         * Email address (RSA PKCS#9 extension) - IA5String.
093         * <p>Note: if you're trying to be ultra orthodox, don't use this! It shouldn't be in here.
094         */
095        public static final DERObjectIdentifier EmailAddress = PKCSObjectIdentifiers.pkcs_9_at_emailAddress;
096    
097        /**
098         * more from PKCS#9
099         */
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         * others...
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)); // copy the hash table
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    }