001package org.apache.commons.ssl.org.bouncycastle.asn1.x509;
002
003import java.io.IOException;
004import java.util.Enumeration;
005import java.util.Hashtable;
006import java.util.Vector;
007
008import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Encodable;
009import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1EncodableVector;
010import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Encoding;
011import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Object;
012import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1ObjectIdentifier;
013import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Primitive;
014import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Sequence;
015import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Set;
016import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1String;
017import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1TaggedObject;
018import org.apache.commons.ssl.org.bouncycastle.asn1.DERSequence;
019import org.apache.commons.ssl.org.bouncycastle.asn1.DERSet;
020import org.apache.commons.ssl.org.bouncycastle.asn1.DERUniversalString;
021import org.apache.commons.ssl.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
022import org.apache.commons.ssl.org.bouncycastle.asn1.x500.X500Name;
023import org.bouncycastle.util.Strings;
024import org.bouncycastle.util.encoders.Hex;
025
026/**
027 * <pre>
028 *     RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
029 *
030 *     RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
031 *
032 *     AttributeTypeAndValue ::= SEQUENCE {
033 *                                   type  OBJECT IDENTIFIER,
034 *                                   value ANY }
035 * </pre>
036 * @deprecated use org.bouncycastle.asn1.x500.X500Name.
037 */
038public class X509Name
039    extends ASN1Object
040{
041    /**
042     * country code - StringType(SIZE(2))
043     * @deprecated use a X500NameStyle
044     */
045    public static final ASN1ObjectIdentifier C = new ASN1ObjectIdentifier("2.5.4.6");
046
047    /**
048     * organization - StringType(SIZE(1..64))
049     * @deprecated use a X500NameStyle
050     */
051    public static final ASN1ObjectIdentifier O = new ASN1ObjectIdentifier("2.5.4.10");
052
053    /**
054     * organizational unit name - StringType(SIZE(1..64))
055     * @deprecated use a X500NameStyle
056     */
057    public static final ASN1ObjectIdentifier OU = new ASN1ObjectIdentifier("2.5.4.11");
058
059    /**
060     * Title
061     * @deprecated use a X500NameStyle
062     */
063    public static final ASN1ObjectIdentifier T = new ASN1ObjectIdentifier("2.5.4.12");
064
065    /**
066     * common name - StringType(SIZE(1..64))
067     * @deprecated use a X500NameStyle
068     */
069    public static final ASN1ObjectIdentifier CN = new ASN1ObjectIdentifier("2.5.4.3");
070
071    /**
072     * device serial number name - StringType(SIZE(1..64))
073     */
074    public static final ASN1ObjectIdentifier SN = new ASN1ObjectIdentifier("2.5.4.5");
075
076    /**
077     * street - StringType(SIZE(1..64))
078     */
079    public static final ASN1ObjectIdentifier STREET = new ASN1ObjectIdentifier("2.5.4.9");
080    
081    /**
082     * device serial number name - StringType(SIZE(1..64))
083     */
084    public static final ASN1ObjectIdentifier SERIALNUMBER = SN;
085
086    /**
087     * locality name - StringType(SIZE(1..64))
088     */
089    public static final ASN1ObjectIdentifier L = new ASN1ObjectIdentifier("2.5.4.7");
090
091    /**
092     * state, or province name - StringType(SIZE(1..64))
093     */
094    public static final ASN1ObjectIdentifier ST = new ASN1ObjectIdentifier("2.5.4.8");
095
096    /**
097     * Naming attributes of type X520name
098     */
099    public static final ASN1ObjectIdentifier SURNAME = new ASN1ObjectIdentifier("2.5.4.4");
100    public static final ASN1ObjectIdentifier GIVENNAME = new ASN1ObjectIdentifier("2.5.4.42");
101    public static final ASN1ObjectIdentifier INITIALS = new ASN1ObjectIdentifier("2.5.4.43");
102    public static final ASN1ObjectIdentifier GENERATION = new ASN1ObjectIdentifier("2.5.4.44");
103    public static final ASN1ObjectIdentifier UNIQUE_IDENTIFIER = new ASN1ObjectIdentifier("2.5.4.45");
104
105    /**
106     * businessCategory - DirectoryString(SIZE(1..128)
107     */
108    public static final ASN1ObjectIdentifier BUSINESS_CATEGORY = new ASN1ObjectIdentifier(
109                    "2.5.4.15");
110
111    /**
112     * postalCode - DirectoryString(SIZE(1..40)
113     */
114    public static final ASN1ObjectIdentifier POSTAL_CODE = new ASN1ObjectIdentifier(
115                    "2.5.4.17");
116    
117    /**
118     * dnQualifier - DirectoryString(SIZE(1..64)
119     */
120    public static final ASN1ObjectIdentifier DN_QUALIFIER = new ASN1ObjectIdentifier(
121                    "2.5.4.46");
122
123    /**
124     * RFC 3039 Pseudonym - DirectoryString(SIZE(1..64)
125     */
126    public static final ASN1ObjectIdentifier PSEUDONYM = new ASN1ObjectIdentifier(
127                    "2.5.4.65");
128
129
130    /**
131     * RFC 3039 DateOfBirth - GeneralizedTime - YYYYMMDD000000Z
132     */
133    public static final ASN1ObjectIdentifier DATE_OF_BIRTH = new ASN1ObjectIdentifier(
134                    "1.3.6.1.5.5.7.9.1");
135
136    /**
137     * RFC 3039 PlaceOfBirth - DirectoryString(SIZE(1..128)
138     */
139    public static final ASN1ObjectIdentifier PLACE_OF_BIRTH = new ASN1ObjectIdentifier(
140                    "1.3.6.1.5.5.7.9.2");
141
142    /**
143     * RFC 3039 Gender - PrintableString (SIZE(1)) -- "M", "F", "m" or "f"
144     */
145    public static final ASN1ObjectIdentifier GENDER = new ASN1ObjectIdentifier(
146                    "1.3.6.1.5.5.7.9.3");
147
148    /**
149     * RFC 3039 CountryOfCitizenship - PrintableString (SIZE (2)) -- ISO 3166
150     * codes only
151     */
152    public static final ASN1ObjectIdentifier COUNTRY_OF_CITIZENSHIP = new ASN1ObjectIdentifier(
153                    "1.3.6.1.5.5.7.9.4");
154
155    /**
156     * RFC 3039 CountryOfResidence - PrintableString (SIZE (2)) -- ISO 3166
157     * codes only
158     */
159    public static final ASN1ObjectIdentifier COUNTRY_OF_RESIDENCE = new ASN1ObjectIdentifier(
160                    "1.3.6.1.5.5.7.9.5");
161
162
163    /**
164     * ISIS-MTT NameAtBirth - DirectoryString(SIZE(1..64)
165     */
166    public static final ASN1ObjectIdentifier NAME_AT_BIRTH =  new ASN1ObjectIdentifier("1.3.36.8.3.14");
167
168    /**
169     * RFC 3039 PostalAddress - SEQUENCE SIZE (1..6) OF
170     * DirectoryString(SIZE(1..30))
171     */
172    public static final ASN1ObjectIdentifier POSTAL_ADDRESS = new ASN1ObjectIdentifier("2.5.4.16");
173
174    /**
175     * RFC 2256 dmdName
176     */
177    public static final ASN1ObjectIdentifier DMD_NAME = new ASN1ObjectIdentifier("2.5.4.54");
178
179    /**
180     * id-at-telephoneNumber
181     */
182    public static final ASN1ObjectIdentifier TELEPHONE_NUMBER = X509ObjectIdentifiers.id_at_telephoneNumber;
183
184    /**
185     * id-at-name
186     */
187    public static final ASN1ObjectIdentifier NAME = X509ObjectIdentifiers.id_at_name;
188
189    /**
190     * Email address (RSA PKCS#9 extension) - IA5String.
191     * <p>Note: if you're trying to be ultra orthodox, don't use this! It shouldn't be in here.
192     * @deprecated use a X500NameStyle
193     */
194    public static final ASN1ObjectIdentifier EmailAddress = PKCSObjectIdentifiers.pkcs_9_at_emailAddress;
195    
196    /**
197     * more from PKCS#9
198     */
199    public static final ASN1ObjectIdentifier UnstructuredName = PKCSObjectIdentifiers.pkcs_9_at_unstructuredName;
200    public static final ASN1ObjectIdentifier UnstructuredAddress = PKCSObjectIdentifiers.pkcs_9_at_unstructuredAddress;
201    
202    /**
203     * email address in Verisign certificates
204     */
205    public static final ASN1ObjectIdentifier E = EmailAddress;
206    
207    /*
208     * others...
209     */
210    public static final ASN1ObjectIdentifier DC = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.25");
211
212    /**
213     * LDAP User id.
214     */
215    public static final ASN1ObjectIdentifier UID = new ASN1ObjectIdentifier("0.9.2342.19200300.100.1.1");
216
217    /**
218     * determines whether or not strings should be processed and printed
219     * from back to front.
220     */
221    public static boolean DefaultReverse = false;
222
223    /**
224     * default look up table translating OID values into their common symbols following
225     * the convention in RFC 2253 with a few extras
226     */
227    public static final Hashtable DefaultSymbols = new Hashtable();
228
229    /**
230     * look up table translating OID values into their common symbols following the convention in RFC 2253
231     * 
232     */
233    public static final Hashtable RFC2253Symbols = new Hashtable();
234
235    /**
236     * look up table translating OID values into their common symbols following the convention in RFC 1779
237     * 
238     */
239    public static final Hashtable RFC1779Symbols = new Hashtable();
240
241    /**
242     * look up table translating common symbols into their OIDS.
243     */
244    public static final Hashtable DefaultLookUp = new Hashtable();
245
246    /**
247     * look up table translating OID values into their common symbols
248     * @deprecated use DefaultSymbols
249     */
250    public static final Hashtable OIDLookUp = DefaultSymbols;
251
252    /**
253     * look up table translating string values into their OIDS -
254     * @deprecated use DefaultLookUp
255     */
256    public static final Hashtable SymbolLookUp = DefaultLookUp;
257
258    private static final Boolean TRUE = new Boolean(true); // for J2ME compatibility
259    private static final Boolean FALSE = new Boolean(false);
260
261    static
262    {
263        DefaultSymbols.put(C, "C");
264        DefaultSymbols.put(O, "O");
265        DefaultSymbols.put(T, "T");
266        DefaultSymbols.put(OU, "OU");
267        DefaultSymbols.put(CN, "CN");
268        DefaultSymbols.put(L, "L");
269        DefaultSymbols.put(ST, "ST");
270        DefaultSymbols.put(SN, "SERIALNUMBER");
271        DefaultSymbols.put(EmailAddress, "E");
272        DefaultSymbols.put(DC, "DC");
273        DefaultSymbols.put(UID, "UID");
274        DefaultSymbols.put(STREET, "STREET");
275        DefaultSymbols.put(SURNAME, "SURNAME");
276        DefaultSymbols.put(GIVENNAME, "GIVENNAME");
277        DefaultSymbols.put(INITIALS, "INITIALS");
278        DefaultSymbols.put(GENERATION, "GENERATION");
279        DefaultSymbols.put(UnstructuredAddress, "unstructuredAddress");
280        DefaultSymbols.put(UnstructuredName, "unstructuredName");
281        DefaultSymbols.put(UNIQUE_IDENTIFIER, "UniqueIdentifier");
282        DefaultSymbols.put(DN_QUALIFIER, "DN");
283        DefaultSymbols.put(PSEUDONYM, "Pseudonym");
284        DefaultSymbols.put(POSTAL_ADDRESS, "PostalAddress");
285        DefaultSymbols.put(NAME_AT_BIRTH, "NameAtBirth");
286        DefaultSymbols.put(COUNTRY_OF_CITIZENSHIP, "CountryOfCitizenship");
287        DefaultSymbols.put(COUNTRY_OF_RESIDENCE, "CountryOfResidence");
288        DefaultSymbols.put(GENDER, "Gender");
289        DefaultSymbols.put(PLACE_OF_BIRTH, "PlaceOfBirth");
290        DefaultSymbols.put(DATE_OF_BIRTH, "DateOfBirth");
291        DefaultSymbols.put(POSTAL_CODE, "PostalCode");
292        DefaultSymbols.put(BUSINESS_CATEGORY, "BusinessCategory");
293        DefaultSymbols.put(TELEPHONE_NUMBER, "TelephoneNumber");
294        DefaultSymbols.put(NAME, "Name");
295
296        RFC2253Symbols.put(C, "C");
297        RFC2253Symbols.put(O, "O");
298        RFC2253Symbols.put(OU, "OU");
299        RFC2253Symbols.put(CN, "CN");
300        RFC2253Symbols.put(L, "L");
301        RFC2253Symbols.put(ST, "ST");
302        RFC2253Symbols.put(STREET, "STREET");
303        RFC2253Symbols.put(DC, "DC");
304        RFC2253Symbols.put(UID, "UID");
305
306        RFC1779Symbols.put(C, "C");
307        RFC1779Symbols.put(O, "O");
308        RFC1779Symbols.put(OU, "OU");
309        RFC1779Symbols.put(CN, "CN");
310        RFC1779Symbols.put(L, "L");
311        RFC1779Symbols.put(ST, "ST");
312        RFC1779Symbols.put(STREET, "STREET");
313
314        DefaultLookUp.put("c", C);
315        DefaultLookUp.put("o", O);
316        DefaultLookUp.put("t", T);
317        DefaultLookUp.put("ou", OU);
318        DefaultLookUp.put("cn", CN);
319        DefaultLookUp.put("l", L);
320        DefaultLookUp.put("st", ST);
321        DefaultLookUp.put("sn", SN);
322        DefaultLookUp.put("serialnumber", SN);
323        DefaultLookUp.put("street", STREET);
324        DefaultLookUp.put("emailaddress", E);
325        DefaultLookUp.put("dc", DC);
326        DefaultLookUp.put("e", E);
327        DefaultLookUp.put("uid", UID);
328        DefaultLookUp.put("surname", SURNAME);
329        DefaultLookUp.put("givenname", GIVENNAME);
330        DefaultLookUp.put("initials", INITIALS);
331        DefaultLookUp.put("generation", GENERATION);
332        DefaultLookUp.put("unstructuredaddress", UnstructuredAddress);
333        DefaultLookUp.put("unstructuredname", UnstructuredName);
334        DefaultLookUp.put("uniqueidentifier", UNIQUE_IDENTIFIER);
335        DefaultLookUp.put("dn", DN_QUALIFIER);
336        DefaultLookUp.put("pseudonym", PSEUDONYM);
337        DefaultLookUp.put("postaladdress", POSTAL_ADDRESS);
338        DefaultLookUp.put("nameofbirth", NAME_AT_BIRTH);
339        DefaultLookUp.put("countryofcitizenship", COUNTRY_OF_CITIZENSHIP);
340        DefaultLookUp.put("countryofresidence", COUNTRY_OF_RESIDENCE);
341        DefaultLookUp.put("gender", GENDER);
342        DefaultLookUp.put("placeofbirth", PLACE_OF_BIRTH);
343        DefaultLookUp.put("dateofbirth", DATE_OF_BIRTH);
344        DefaultLookUp.put("postalcode", POSTAL_CODE);
345        DefaultLookUp.put("businesscategory", BUSINESS_CATEGORY);
346        DefaultLookUp.put("telephonenumber", TELEPHONE_NUMBER);
347        DefaultLookUp.put("name", NAME);
348    }
349
350    private X509NameEntryConverter  converter = null;
351    private Vector                  ordering = new Vector();
352    private Vector                  values = new Vector();
353    private Vector                  added = new Vector();
354
355    private ASN1Sequence            seq;
356
357    private boolean                 isHashCodeCalculated;
358    private int                     hashCodeValue;
359
360    /**
361     * Return a X509Name based on the passed in tagged object.
362     * 
363     * @param obj tag object holding name.
364     * @param explicit true if explicitly tagged false otherwise.
365     * @return the X509Name
366     */
367    public static X509Name getInstance(
368        ASN1TaggedObject obj,
369        boolean          explicit)
370    {
371        return getInstance(ASN1Sequence.getInstance(obj, explicit));
372    }
373
374    public static X509Name getInstance(
375        Object  obj)
376    {
377        if (obj == null || obj instanceof X509Name)
378        {
379            return (X509Name)obj;
380        }
381        else if (obj instanceof X500Name)
382        {
383            return new X509Name(ASN1Sequence.getInstance(((X500Name)obj).toASN1Primitive()));
384        }
385        else if (obj != null)
386        {
387            return new X509Name(ASN1Sequence.getInstance(obj));
388        }
389
390        return null;
391    }
392
393    protected X509Name()
394    {
395        // constructure use by new X500 Name class
396    }
397    /**
398     * Constructor from ASN1Sequence
399     *
400     * the principal will be a list of constructed sets, each containing an (OID, String) pair.
401     * @deprecated use X500Name.getInstance()
402     */
403    public X509Name(
404        ASN1Sequence  seq)
405    {
406        this.seq = seq;
407
408        Enumeration e = seq.getObjects();
409
410        while (e.hasMoreElements())
411        {
412            ASN1Set         set = ASN1Set.getInstance(((ASN1Encodable)e.nextElement()).toASN1Primitive());
413
414            for (int i = 0; i < set.size(); i++) 
415            {
416                   ASN1Sequence s = ASN1Sequence.getInstance(set.getObjectAt(i).toASN1Primitive());
417
418                   if (s.size() != 2)
419                   {
420                       throw new IllegalArgumentException("badly sized pair");
421                   }
422
423                   ordering.addElement(ASN1ObjectIdentifier.getInstance(s.getObjectAt(0)));
424                   
425                   ASN1Encodable value = s.getObjectAt(1);
426                   if (value instanceof ASN1String && !(value instanceof DERUniversalString))
427                   {
428                       String v = ((ASN1String)value).getString();
429                       if (v.length() > 0 && v.charAt(0) == '#')
430                       {
431                           values.addElement("\\" + v);
432                       }
433                       else
434                       {
435                           values.addElement(v);
436                       }
437                   }
438                   else
439                   {
440                       try
441                       {
442                           values.addElement("#" + bytesToString(Hex.encode(value.toASN1Primitive().getEncoded(ASN1Encoding.DER))));
443                       }
444                       catch (IOException e1)
445                       {
446                           throw new IllegalArgumentException("cannot encode value");
447                       }
448                   }
449                   added.addElement((i != 0) ? TRUE : FALSE);  // to allow earlier JDK compatibility
450            }
451        }
452    }
453
454    /**
455     * constructor from a table of attributes.
456     * <p>
457     * it's is assumed the table contains OID/String pairs, and the contents
458     * of the table are copied into an internal table as part of the
459     * construction process.
460     * <p>
461     * <b>Note:</b> if the name you are trying to generate should be
462     * following a specific ordering, you should use the constructor
463     * with the ordering specified below.
464     * @deprecated use an ordered constructor! The hashtable ordering is rarely correct
465     */
466    public X509Name(
467        Hashtable  attributes)
468    {
469        this(null, attributes);
470    }
471
472    /**
473     * Constructor from a table of attributes with ordering.
474     * <p>
475     * it's is assumed the table contains OID/String pairs, and the contents
476     * of the table are copied into an internal table as part of the
477     * construction process. The ordering vector should contain the OIDs
478     * in the order they are meant to be encoded or printed in toString.
479     */
480    public X509Name(
481        Vector      ordering,
482        Hashtable   attributes)
483    {
484        this(ordering, attributes, new X509DefaultEntryConverter());
485    }
486
487    /**
488     * Constructor from a table of attributes with ordering.
489     * <p>
490     * it's is assumed the table contains OID/String pairs, and the contents
491     * of the table are copied into an internal table as part of the
492     * construction process. The ordering vector should contain the OIDs
493     * in the order they are meant to be encoded or printed in toString.
494     * <p>
495     * The passed in converter will be used to convert the strings into their
496     * ASN.1 counterparts.
497     * @deprecated use X500Name, X500NameBuilder
498     */
499    public X509Name(
500        Vector                   ordering,
501        Hashtable                attributes,
502        X509NameEntryConverter   converter)
503    {
504        this.converter = converter;
505
506        if (ordering != null)
507        {
508            for (int i = 0; i != ordering.size(); i++)
509            {
510                this.ordering.addElement(ordering.elementAt(i));
511                this.added.addElement(FALSE);
512            }
513        }
514        else
515        {
516            Enumeration     e = attributes.keys();
517
518            while (e.hasMoreElements())
519            {
520                this.ordering.addElement(e.nextElement());
521                this.added.addElement(FALSE);
522            }
523        }
524
525        for (int i = 0; i != this.ordering.size(); i++)
526        {
527            ASN1ObjectIdentifier     oid = (ASN1ObjectIdentifier)this.ordering.elementAt(i);
528
529            if (attributes.get(oid) == null)
530            {
531                throw new IllegalArgumentException("No attribute for object id - " + oid.getId() + " - passed to distinguished name");
532            }
533
534            this.values.addElement(attributes.get(oid)); // copy the hash table
535        }
536    }
537
538    /**
539     * Takes two vectors one of the oids and the other of the values.
540     * @deprecated use X500Name, X500NameBuilder
541     */
542    public X509Name(
543        Vector  oids,
544        Vector  values)
545    {
546        this(oids, values, new X509DefaultEntryConverter());
547    }
548
549    /**
550     * Takes two vectors one of the oids and the other of the values.
551     * <p>
552     * The passed in converter will be used to convert the strings into their
553     * ASN.1 counterparts.
554     * @deprecated use X500Name, X500NameBuilder
555     */
556    public X509Name(
557        Vector                  oids,
558        Vector                  values,
559        X509NameEntryConverter  converter)
560    {
561        this.converter = converter;
562
563        if (oids.size() != values.size())
564        {
565            throw new IllegalArgumentException("oids vector must be same length as values.");
566        }
567
568        for (int i = 0; i < oids.size(); i++)
569        {
570            this.ordering.addElement(oids.elementAt(i));
571            this.values.addElement(values.elementAt(i));
572            this.added.addElement(FALSE);
573        }
574    }
575
576//    private Boolean isEncoded(String s)
577//    {
578//        if (s.charAt(0) == '#')
579//        {
580//            return TRUE;
581//        }
582//
583//        return FALSE;
584//    }
585
586    /**
587     * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
588     * some such, converting it into an ordered set of name attributes.
589     * @deprecated use X500Name, X500NameBuilder
590     */
591    public X509Name(
592        String  dirName)
593    {
594        this(DefaultReverse, DefaultLookUp, dirName);
595    }
596
597    /**
598     * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
599     * some such, converting it into an ordered set of name attributes with each
600     * string value being converted to its associated ASN.1 type using the passed
601     * in converter.
602     * @deprecated use X500Name, X500NameBuilder
603     */
604    public X509Name(
605        String                  dirName,
606        X509NameEntryConverter  converter)
607    {
608        this(DefaultReverse, DefaultLookUp, dirName, converter);
609    }
610
611    /**
612     * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
613     * some such, converting it into an ordered set of name attributes. If reverse
614     * is true, create the encoded version of the sequence starting from the
615     * last element in the string.
616     * @deprecated use X500Name, X500NameBuilder
617     */
618    public X509Name(
619        boolean reverse,
620        String  dirName)
621    {
622        this(reverse, DefaultLookUp, dirName);
623    }
624
625    /**
626     * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
627     * some such, converting it into an ordered set of name attributes with each
628     * string value being converted to its associated ASN.1 type using the passed
629     * in converter. If reverse is true the ASN.1 sequence representing the DN will
630     * be built by starting at the end of the string, rather than the start.
631     * @deprecated use X500Name, X500NameBuilder
632     */
633    public X509Name(
634        boolean                 reverse,
635        String                  dirName,
636        X509NameEntryConverter  converter)
637    {
638        this(reverse, DefaultLookUp, dirName, converter);
639    }
640
641    /**
642     * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
643     * some such, converting it into an ordered set of name attributes. lookUp
644     * should provide a table of lookups, indexed by lowercase only strings and
645     * yielding a ASN1ObjectIdentifier, other than that OID. and numeric oids
646     * will be processed automatically.
647     * <br>
648     * If reverse is true, create the encoded version of the sequence
649     * starting from the last element in the string.
650     * @param reverse true if we should start scanning from the end (RFC 2553).
651     * @param lookUp table of names and their oids.
652     * @param dirName the X.500 string to be parsed.
653     * @deprecated use X500Name, X500NameBuilder
654     */
655    public X509Name(
656        boolean     reverse,
657        Hashtable   lookUp,
658        String      dirName)
659    {
660        this(reverse, lookUp, dirName, new X509DefaultEntryConverter());
661    }
662
663    private ASN1ObjectIdentifier decodeOID(
664        String      name,
665        Hashtable   lookUp)
666    {
667        name = name.trim();
668        if (Strings.toUpperCase(name).startsWith("OID."))
669        {
670            return new ASN1ObjectIdentifier(name.substring(4));
671        }
672        else if (name.charAt(0) >= '0' && name.charAt(0) <= '9')
673        {
674            return new ASN1ObjectIdentifier(name);
675        }
676
677        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)lookUp.get(Strings.toLowerCase(name));
678        if (oid == null)
679        {
680            throw new IllegalArgumentException("Unknown object id - " + name + " - passed to distinguished name");
681        }
682
683        return oid;
684    }
685
686    private String unescape(String elt)
687    {
688        if (elt.length() == 0 || (elt.indexOf('\\') < 0 && elt.indexOf('"') < 0))
689        {
690            return elt.trim();
691        }
692
693        char[] elts = elt.toCharArray();
694        boolean escaped = false;
695        boolean quoted = false;
696        StringBuffer buf = new StringBuffer(elt.length());
697        int start = 0;
698
699        // if it's an escaped hash string and not an actual encoding in string form
700        // we need to leave it escaped.
701        if (elts[0] == '\\')
702        {
703            if (elts[1] == '#')
704            {
705                start = 2;
706                buf.append("\\#");
707            }
708        }
709
710        boolean nonWhiteSpaceEncountered = false;
711        int     lastEscaped = 0;
712
713        for (int i = start; i != elts.length; i++)
714        {
715            char c = elts[i];
716
717            if (c != ' ')
718            {
719                nonWhiteSpaceEncountered = true;
720            }
721
722            if (c == '"')
723            {
724                if (!escaped)
725                {
726                    quoted = !quoted;
727                }
728                else
729                {
730                    buf.append(c);
731                }
732                escaped = false;
733            }
734            else if (c == '\\' && !(escaped || quoted))
735            {
736                escaped = true;
737                lastEscaped = buf.length();
738            }
739            else
740            {
741                if (c == ' ' && !escaped && !nonWhiteSpaceEncountered)
742                {
743                    continue;
744                }
745                buf.append(c);
746                escaped = false;
747            }
748        }
749
750        if (buf.length() > 0)
751        {
752            while (buf.charAt(buf.length() - 1) == ' ' && lastEscaped != (buf.length() - 1))
753            {
754                buf.setLength(buf.length() - 1);
755            }
756        }
757
758        return buf.toString();
759    }
760
761    /**
762     * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
763     * some such, converting it into an ordered set of name attributes. lookUp
764     * should provide a table of lookups, indexed by lowercase only strings and
765     * yielding a ASN1ObjectIdentifier, other than that OID. and numeric oids
766     * will be processed automatically. The passed in converter is used to convert the
767     * string values to the right of each equals sign to their ASN.1 counterparts.
768     * <br>
769     * @param reverse true if we should start scanning from the end, false otherwise.
770     * @param lookUp table of names and oids.
771     * @param dirName the string dirName
772     * @param converter the converter to convert string values into their ASN.1 equivalents
773     */
774    public X509Name(
775        boolean                 reverse,
776        Hashtable               lookUp,
777        String                  dirName,
778        X509NameEntryConverter  converter)
779    {
780        this.converter = converter;
781        X509NameTokenizer   nTok = new X509NameTokenizer(dirName);
782
783        while (nTok.hasMoreTokens())
784        {
785            String  token = nTok.nextToken();
786
787            if (token.indexOf('+') > 0)
788            {
789                X509NameTokenizer   pTok = new X509NameTokenizer(token, '+');
790
791                addEntry(lookUp, pTok.nextToken(), FALSE);
792
793                while (pTok.hasMoreTokens())
794                {
795                    addEntry(lookUp, pTok.nextToken(), TRUE);
796                }
797            }
798            else
799            {
800                addEntry(lookUp, token, FALSE);
801            }
802        }
803
804        if (reverse)
805        {
806            Vector  o = new Vector();
807            Vector  v = new Vector();
808            Vector  a = new Vector();
809
810            int count = 1;
811
812            for (int i = 0; i < this.ordering.size(); i++)
813            {
814                if (((Boolean)this.added.elementAt(i)).booleanValue())
815                {
816                    o.insertElementAt(this.ordering.elementAt(i), count);
817                    v.insertElementAt(this.values.elementAt(i), count);
818                    a.insertElementAt(this.added.elementAt(i), count);
819                    count++;
820                }
821                else
822                {
823                    o.insertElementAt(this.ordering.elementAt(i), 0);
824                    v.insertElementAt(this.values.elementAt(i), 0);
825                    a.insertElementAt(this.added.elementAt(i), 0);
826                    count = 1;
827                }
828            }
829
830            this.ordering = o;
831            this.values = v;
832            this.added = a;
833        }
834    }
835
836    private void addEntry(Hashtable lookUp, String token, Boolean isAdded)
837    {
838        X509NameTokenizer vTok;
839        String name;
840        String value;ASN1ObjectIdentifier oid;
841        vTok = new X509NameTokenizer(token, '=');
842
843        name = vTok.nextToken();
844
845        if (!vTok.hasMoreTokens())
846        {
847           throw new IllegalArgumentException("badly formatted directory string");
848        }
849
850        value = vTok.nextToken();
851
852        oid = decodeOID(name, lookUp);
853
854        this.ordering.addElement(oid);
855        this.values.addElement(unescape(value));
856        this.added.addElement(isAdded);
857    }
858
859    /**
860     * return a vector of the oids in the name, in the order they were found.
861     */
862    public Vector getOIDs()
863    {
864        Vector  v = new Vector();
865
866        for (int i = 0; i != ordering.size(); i++)
867        {
868            v.addElement(ordering.elementAt(i));
869        }
870
871        return v;
872    }
873
874    /**
875     * return a vector of the values found in the name, in the order they
876     * were found.
877     */
878    public Vector getValues()
879    {
880        Vector  v = new Vector();
881
882        for (int i = 0; i != values.size(); i++)
883        {
884            v.addElement(values.elementAt(i));
885        }
886
887        return v;
888    }
889
890    /**
891     * return a vector of the values found in the name, in the order they
892     * were found, with the DN label corresponding to passed in oid.
893     */
894    public Vector getValues(
895        ASN1ObjectIdentifier oid)
896    {
897        Vector  v = new Vector();
898
899        for (int i = 0; i != values.size(); i++)
900        {
901            if (ordering.elementAt(i).equals(oid))
902            {
903                String val = (String)values.elementAt(i);
904
905                if (val.length() > 2 && val.charAt(0) == '\\' && val.charAt(1) == '#')
906                {
907                    v.addElement(val.substring(1));
908                }
909                else
910                {
911                    v.addElement(val);
912                }
913            }
914        }
915
916        return v;
917    }
918
919    public ASN1Primitive toASN1Primitive()
920    {
921        if (seq == null)
922        {
923            ASN1EncodableVector  vec = new ASN1EncodableVector();
924            ASN1EncodableVector  sVec = new ASN1EncodableVector();
925            ASN1ObjectIdentifier  lstOid = null;
926            
927            for (int i = 0; i != ordering.size(); i++)
928            {
929                ASN1EncodableVector     v = new ASN1EncodableVector();
930                ASN1ObjectIdentifier     oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
931
932                v.add(oid);
933
934                String  str = (String)values.elementAt(i);
935
936                v.add(converter.getConvertedValue(oid, str));
937 
938                if (lstOid == null 
939                    || ((Boolean)this.added.elementAt(i)).booleanValue())
940                {
941                    sVec.add(new DERSequence(v));
942                }
943                else
944                {
945                    vec.add(new DERSet(sVec));
946                    sVec = new ASN1EncodableVector();
947                    
948                    sVec.add(new DERSequence(v));
949                }
950                
951                lstOid = oid;
952            }
953            
954            vec.add(new DERSet(sVec));
955            
956            seq = new DERSequence(vec);
957        }
958
959        return seq;
960    }
961
962    /**
963     * @param inOrder if true the order of both X509 names must be the same,
964     * as well as the values associated with each element.
965     */
966    public boolean equals(Object obj, boolean inOrder)
967    {
968        if (!inOrder)
969        {
970            return this.equals(obj);
971        }
972
973        if (obj == this)
974        {
975            return true;
976        }
977
978        if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
979        {
980            return false;
981        }
982
983        ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
984
985        if (this.toASN1Primitive().equals(derO))
986        {
987            return true;
988        }
989
990        X509Name other;
991
992        try
993        {
994            other = X509Name.getInstance(obj);
995        }
996        catch (IllegalArgumentException e)
997        {
998            return false;
999        }
1000
1001        int      orderingSize = ordering.size();
1002
1003        if (orderingSize != other.ordering.size())
1004        {
1005            return false;
1006        }
1007
1008        for (int i = 0; i < orderingSize; i++)
1009        {
1010            ASN1ObjectIdentifier  oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
1011            ASN1ObjectIdentifier  oOid = (ASN1ObjectIdentifier)other.ordering.elementAt(i);
1012
1013            if (oid.equals(oOid))
1014            {
1015                String value = (String)values.elementAt(i);
1016                String oValue = (String)other.values.elementAt(i);
1017
1018                if (!equivalentStrings(value, oValue))
1019                {
1020                    return false;
1021                }
1022            }
1023            else
1024            {
1025                return false;
1026            }
1027        }
1028
1029        return true;
1030    }
1031
1032    public int hashCode()
1033    {
1034        if (isHashCodeCalculated)
1035        {
1036            return hashCodeValue;
1037        }
1038
1039        isHashCodeCalculated = true;
1040
1041        // this needs to be order independent, like equals
1042        for (int i = 0; i != ordering.size(); i += 1)
1043        {
1044            String value = (String)values.elementAt(i);
1045
1046            value = canonicalize(value);
1047            value = stripInternalSpaces(value);
1048
1049            hashCodeValue ^= ordering.elementAt(i).hashCode();
1050            hashCodeValue ^= value.hashCode();
1051        }
1052
1053        return hashCodeValue;
1054    }
1055
1056    /**
1057     * test for equality - note: case is ignored.
1058     */
1059    public boolean equals(Object obj)
1060    {
1061        if (obj == this)
1062        {
1063            return true;
1064        }
1065
1066        if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
1067        {
1068            return false;
1069        }
1070        
1071        ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
1072        
1073        if (this.toASN1Primitive().equals(derO))
1074        {
1075            return true;
1076        }
1077
1078        X509Name other;
1079
1080        try
1081        {
1082            other = X509Name.getInstance(obj);
1083        }
1084        catch (IllegalArgumentException e)
1085        { 
1086            return false;
1087        }
1088
1089        int      orderingSize = ordering.size();
1090
1091        if (orderingSize != other.ordering.size())
1092        {
1093            return false;
1094        }
1095        
1096        boolean[] indexes = new boolean[orderingSize];
1097        int       start, end, delta;
1098
1099        if (ordering.elementAt(0).equals(other.ordering.elementAt(0)))   // guess forward
1100        {
1101            start = 0;
1102            end = orderingSize;
1103            delta = 1;
1104        }
1105        else  // guess reversed - most common problem
1106        {
1107            start = orderingSize - 1;
1108            end = -1;
1109            delta = -1;
1110        }
1111
1112        for (int i = start; i != end; i += delta)
1113        {
1114            boolean              found = false;
1115            ASN1ObjectIdentifier  oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
1116            String               value = (String)values.elementAt(i);
1117
1118            for (int j = 0; j < orderingSize; j++)
1119            {
1120                if (indexes[j])
1121                {
1122                    continue;
1123                }
1124
1125                ASN1ObjectIdentifier oOid = (ASN1ObjectIdentifier)other.ordering.elementAt(j);
1126
1127                if (oid.equals(oOid))
1128                {
1129                    String oValue = (String)other.values.elementAt(j);
1130
1131                    if (equivalentStrings(value, oValue))
1132                    {
1133                        indexes[j] = true;
1134                        found      = true;
1135                        break;
1136                    }
1137                }
1138            }
1139
1140            if (!found)
1141            {
1142                return false;
1143            }
1144        }
1145        
1146        return true;
1147    }
1148
1149    private boolean equivalentStrings(String s1, String s2)
1150    {
1151        String value = canonicalize(s1);
1152        String oValue = canonicalize(s2);
1153        
1154        if (!value.equals(oValue))
1155        {
1156            value = stripInternalSpaces(value);
1157            oValue = stripInternalSpaces(oValue);
1158
1159            if (!value.equals(oValue))
1160            {
1161                return false;
1162            }
1163        }
1164
1165        return true;
1166    }
1167
1168    private String canonicalize(String s)
1169    {
1170        String value = Strings.toLowerCase(s.trim());
1171        
1172        if (value.length() > 0 && value.charAt(0) == '#')
1173        {
1174            ASN1Primitive obj = decodeObject(value);
1175
1176            if (obj instanceof ASN1String)
1177            {
1178                value = Strings.toLowerCase(((ASN1String)obj).getString().trim());
1179            }
1180        }
1181
1182        return value;
1183    }
1184
1185    private ASN1Primitive decodeObject(String oValue)
1186    {
1187        try
1188        {
1189            return ASN1Primitive.fromByteArray(Hex.decode(oValue.substring(1)));
1190        }
1191        catch (IOException e)
1192        {
1193            throw new IllegalStateException("unknown encoding in name: " + e);
1194        }
1195    }
1196
1197    private String stripInternalSpaces(
1198        String str)
1199    {
1200        StringBuffer res = new StringBuffer();
1201
1202        if (str.length() != 0)
1203        {
1204            char    c1 = str.charAt(0);
1205
1206            res.append(c1);
1207
1208            for (int k = 1; k < str.length(); k++)
1209            {
1210                char    c2 = str.charAt(k);
1211                if (!(c1 == ' ' && c2 == ' '))
1212                {
1213                    res.append(c2);
1214                }
1215                c1 = c2;
1216            }
1217        }
1218
1219        return res.toString();
1220    }
1221
1222    private void appendValue(
1223        StringBuffer        buf,
1224        Hashtable           oidSymbols,
1225        ASN1ObjectIdentifier oid,
1226        String              value)
1227    {
1228        String  sym = (String)oidSymbols.get(oid);
1229
1230        if (sym != null)
1231        {
1232            buf.append(sym);
1233        }
1234        else
1235        {
1236            buf.append(oid.getId());
1237        }
1238
1239        buf.append('=');
1240
1241        int start = buf.length();
1242        buf.append(value);
1243        int end = buf.length();
1244
1245        if (value.length() >= 2 && value.charAt(0) == '\\' && value.charAt(1) == '#')
1246        {
1247            start += 2;
1248        }
1249
1250        while (start < end && buf.charAt(start) == ' ')
1251        {
1252            buf.insert(start, "\\");
1253            start += 2;
1254            ++end;
1255        }
1256
1257        while (--end > start && buf.charAt(end) == ' ')
1258        {
1259            buf.insert(end, '\\');
1260        }
1261
1262        while (start <= end)
1263        {
1264            switch (buf.charAt(start))
1265            {
1266            case ',':
1267            case '"':
1268            case '\\':
1269            case '+':
1270            case '=':
1271            case '<':
1272            case '>':
1273            case ';':
1274                buf.insert(start, "\\");
1275                start += 2;
1276                ++end;
1277                break;
1278            default:
1279                ++start;
1280                break;
1281            }
1282        }
1283    }
1284
1285    /**
1286     * convert the structure to a string - if reverse is true the
1287     * oids and values are listed out starting with the last element
1288     * in the sequence (ala RFC 2253), otherwise the string will begin
1289     * with the first element of the structure. If no string definition
1290     * for the oid is found in oidSymbols the string value of the oid is
1291     * added. Two standard symbol tables are provided DefaultSymbols, and
1292     * RFC2253Symbols as part of this class.
1293     *
1294     * @param reverse if true start at the end of the sequence and work back.
1295     * @param oidSymbols look up table strings for oids.
1296     */
1297    public String toString(
1298        boolean     reverse,
1299        Hashtable   oidSymbols)
1300    {
1301        StringBuffer            buf = new StringBuffer();
1302        Vector                  components = new Vector();
1303        boolean                 first = true;
1304
1305        StringBuffer ava = null;
1306
1307        for (int i = 0; i < ordering.size(); i++)
1308        {
1309            if (((Boolean)added.elementAt(i)).booleanValue())
1310            {
1311                ava.append('+');
1312                appendValue(ava, oidSymbols,
1313                    (ASN1ObjectIdentifier)ordering.elementAt(i),
1314                    (String)values.elementAt(i));
1315            }
1316            else
1317            {
1318                ava = new StringBuffer();
1319                appendValue(ava, oidSymbols,
1320                    (ASN1ObjectIdentifier)ordering.elementAt(i),
1321                    (String)values.elementAt(i));
1322                components.addElement(ava);
1323            }
1324        }
1325
1326        if (reverse)
1327        {
1328            for (int i = components.size() - 1; i >= 0; i--)
1329            {
1330                if (first)
1331                {
1332                    first = false;
1333                }
1334                else
1335                {
1336                    buf.append(',');
1337                }
1338
1339                buf.append(components.elementAt(i).toString());
1340            }
1341        }
1342        else
1343        {
1344            for (int i = 0; i < components.size(); i++)
1345            {
1346                if (first)
1347                {
1348                    first = false;
1349                }
1350                else
1351                {
1352                    buf.append(',');
1353                }
1354
1355                buf.append(components.elementAt(i).toString());
1356            }
1357        }
1358
1359        return buf.toString();
1360    }
1361
1362    private String bytesToString(
1363        byte[] data)
1364    {
1365        char[]  cs = new char[data.length];
1366
1367        for (int i = 0; i != cs.length; i++)
1368        {
1369            cs[i] = (char)(data[i] & 0xff);
1370        }
1371
1372        return new String(cs);
1373    }
1374    
1375    public String toString()
1376    {
1377        return toString(DefaultReverse, DefaultSymbols);
1378    }
1379}