1package org.bouncycastle.asn1.x509;
2
3import java.io.IOException;
4import java.util.Enumeration;
5import java.util.Hashtable;
6import java.util.Vector;
7
8import org.bouncycastle.asn1.ASN1Encodable;
9import org.bouncycastle.asn1.ASN1EncodableVector;
10import org.bouncycastle.asn1.ASN1Encoding;
11import org.bouncycastle.asn1.ASN1Object;
12import org.bouncycastle.asn1.ASN1ObjectIdentifier;
13import org.bouncycastle.asn1.ASN1Primitive;
14import org.bouncycastle.asn1.ASN1Sequence;
15import org.bouncycastle.asn1.ASN1Set;
16import org.bouncycastle.asn1.ASN1String;
17import org.bouncycastle.asn1.ASN1TaggedObject;
18import org.bouncycastle.asn1.DERSequence;
19import org.bouncycastle.asn1.DERSet;
20import org.bouncycastle.asn1.DERUniversalString;
21import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
22import org.bouncycastle.asn1.x500.X500Name;
23import org.bouncycastle.util.Strings;
24import org.bouncycastle.util.encoders.Hex;
25
26/**
27 * <pre>
28 *     RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
29 *
30 *     RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
31 *
32 *     AttributeTypeAndValue ::= SEQUENCE {
33 *                                   type  OBJECT IDENTIFIER,
34 *                                   value ANY }
35 * </pre>
36 * @deprecated use org.bouncycastle.asn1.x500.X500Name.
37 */
38public class X509Name
39    extends ASN1Object
40{
41    /**
42     * country code - StringType(SIZE(2))
43     * @deprecated use a X500NameStyle
44     */
45    public static final ASN1ObjectIdentifier C = new ASN1ObjectIdentifier("2.5.4.6");
46
47    /**
48     * organization - StringType(SIZE(1..64))
49     * @deprecated use a X500NameStyle
50     */
51    public static final ASN1ObjectIdentifier O = new ASN1ObjectIdentifier("2.5.4.10");
52
53    /**
54     * organizational unit name - StringType(SIZE(1..64))
55     * @deprecated use a X500NameStyle
56     */
57    public static final ASN1ObjectIdentifier OU = new ASN1ObjectIdentifier("2.5.4.11");
58
59    /**
60     * Title
61     * @deprecated use a X500NameStyle
62     */
63    public static final ASN1ObjectIdentifier T = new ASN1ObjectIdentifier("2.5.4.12");
64
65    /**
66     * common name - StringType(SIZE(1..64))
67     * @deprecated use a X500NameStyle
68     */
69    public static final ASN1ObjectIdentifier CN = new ASN1ObjectIdentifier("2.5.4.3");
70
71    /**
72     * device serial number name - StringType(SIZE(1..64))
73     */
74    public static final ASN1ObjectIdentifier SN = new ASN1ObjectIdentifier("2.5.4.5");
75
76    /**
77     * street - StringType(SIZE(1..64))
78     */
79    public static final ASN1ObjectIdentifier STREET = new ASN1ObjectIdentifier("2.5.4.9");
80
81    /**
82     * device serial number name - StringType(SIZE(1..64))
83     */
84    public static final ASN1ObjectIdentifier SERIALNUMBER = SN;
85
86    /**
87     * locality name - StringType(SIZE(1..64))
88     */
89    public static final ASN1ObjectIdentifier L = new ASN1ObjectIdentifier("2.5.4.7");
90
91    /**
92     * state, or province name - StringType(SIZE(1..64))
93     */
94    public static final ASN1ObjectIdentifier ST = new ASN1ObjectIdentifier("2.5.4.8");
95
96    /**
97     * Naming attributes of type X520name
98     */
99    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    // BEGIN Android-changed: Use Boolean class constants instead of Boolean constructor
259    private static final Boolean TRUE = Boolean.TRUE;
260    private static final Boolean FALSE = Boolean.FALSE;
261    // END Android-changed: Use Boolean class constants instead of Boolean constructor
262
263    static
264    {
265        DefaultSymbols.put(C, "C");
266        DefaultSymbols.put(O, "O");
267        DefaultSymbols.put(T, "T");
268        DefaultSymbols.put(OU, "OU");
269        DefaultSymbols.put(CN, "CN");
270        DefaultSymbols.put(L, "L");
271        DefaultSymbols.put(ST, "ST");
272        DefaultSymbols.put(SN, "SERIALNUMBER");
273        DefaultSymbols.put(EmailAddress, "E");
274        DefaultSymbols.put(DC, "DC");
275        DefaultSymbols.put(UID, "UID");
276        DefaultSymbols.put(STREET, "STREET");
277        DefaultSymbols.put(SURNAME, "SURNAME");
278        DefaultSymbols.put(GIVENNAME, "GIVENNAME");
279        DefaultSymbols.put(INITIALS, "INITIALS");
280        DefaultSymbols.put(GENERATION, "GENERATION");
281        DefaultSymbols.put(UnstructuredAddress, "unstructuredAddress");
282        DefaultSymbols.put(UnstructuredName, "unstructuredName");
283        DefaultSymbols.put(UNIQUE_IDENTIFIER, "UniqueIdentifier");
284        DefaultSymbols.put(DN_QUALIFIER, "DN");
285        DefaultSymbols.put(PSEUDONYM, "Pseudonym");
286        DefaultSymbols.put(POSTAL_ADDRESS, "PostalAddress");
287        DefaultSymbols.put(NAME_AT_BIRTH, "NameAtBirth");
288        DefaultSymbols.put(COUNTRY_OF_CITIZENSHIP, "CountryOfCitizenship");
289        DefaultSymbols.put(COUNTRY_OF_RESIDENCE, "CountryOfResidence");
290        DefaultSymbols.put(GENDER, "Gender");
291        DefaultSymbols.put(PLACE_OF_BIRTH, "PlaceOfBirth");
292        DefaultSymbols.put(DATE_OF_BIRTH, "DateOfBirth");
293        DefaultSymbols.put(POSTAL_CODE, "PostalCode");
294        DefaultSymbols.put(BUSINESS_CATEGORY, "BusinessCategory");
295        DefaultSymbols.put(TELEPHONE_NUMBER, "TelephoneNumber");
296        DefaultSymbols.put(NAME, "Name");
297
298        RFC2253Symbols.put(C, "C");
299        RFC2253Symbols.put(O, "O");
300        RFC2253Symbols.put(OU, "OU");
301        RFC2253Symbols.put(CN, "CN");
302        RFC2253Symbols.put(L, "L");
303        RFC2253Symbols.put(ST, "ST");
304        RFC2253Symbols.put(STREET, "STREET");
305        RFC2253Symbols.put(DC, "DC");
306        RFC2253Symbols.put(UID, "UID");
307
308        RFC1779Symbols.put(C, "C");
309        RFC1779Symbols.put(O, "O");
310        RFC1779Symbols.put(OU, "OU");
311        RFC1779Symbols.put(CN, "CN");
312        RFC1779Symbols.put(L, "L");
313        RFC1779Symbols.put(ST, "ST");
314        RFC1779Symbols.put(STREET, "STREET");
315
316        DefaultLookUp.put("c", C);
317        DefaultLookUp.put("o", O);
318        DefaultLookUp.put("t", T);
319        DefaultLookUp.put("ou", OU);
320        DefaultLookUp.put("cn", CN);
321        DefaultLookUp.put("l", L);
322        DefaultLookUp.put("st", ST);
323        DefaultLookUp.put("sn", SN);
324        DefaultLookUp.put("serialnumber", SN);
325        DefaultLookUp.put("street", STREET);
326        DefaultLookUp.put("emailaddress", E);
327        DefaultLookUp.put("dc", DC);
328        DefaultLookUp.put("e", E);
329        DefaultLookUp.put("uid", UID);
330        DefaultLookUp.put("surname", SURNAME);
331        DefaultLookUp.put("givenname", GIVENNAME);
332        DefaultLookUp.put("initials", INITIALS);
333        DefaultLookUp.put("generation", GENERATION);
334        DefaultLookUp.put("unstructuredaddress", UnstructuredAddress);
335        DefaultLookUp.put("unstructuredname", UnstructuredName);
336        DefaultLookUp.put("uniqueidentifier", UNIQUE_IDENTIFIER);
337        DefaultLookUp.put("dn", DN_QUALIFIER);
338        DefaultLookUp.put("pseudonym", PSEUDONYM);
339        DefaultLookUp.put("postaladdress", POSTAL_ADDRESS);
340        DefaultLookUp.put("nameofbirth", NAME_AT_BIRTH);
341        DefaultLookUp.put("countryofcitizenship", COUNTRY_OF_CITIZENSHIP);
342        DefaultLookUp.put("countryofresidence", COUNTRY_OF_RESIDENCE);
343        DefaultLookUp.put("gender", GENDER);
344        DefaultLookUp.put("placeofbirth", PLACE_OF_BIRTH);
345        DefaultLookUp.put("dateofbirth", DATE_OF_BIRTH);
346        DefaultLookUp.put("postalcode", POSTAL_CODE);
347        DefaultLookUp.put("businesscategory", BUSINESS_CATEGORY);
348        DefaultLookUp.put("telephonenumber", TELEPHONE_NUMBER);
349        DefaultLookUp.put("name", NAME);
350    }
351
352    private X509NameEntryConverter  converter = null;
353    private Vector                  ordering = new Vector();
354    private Vector                  values = new Vector();
355    private Vector                  added = new Vector();
356
357    private ASN1Sequence            seq;
358
359    private boolean                 isHashCodeCalculated;
360    private int                     hashCodeValue;
361
362    /**
363     * Return a X509Name based on the passed in tagged object.
364     *
365     * @param obj tag object holding name.
366     * @param explicit true if explicitly tagged false otherwise.
367     * @return the X509Name
368     */
369    public static X509Name getInstance(
370        ASN1TaggedObject obj,
371        boolean          explicit)
372    {
373        return getInstance(ASN1Sequence.getInstance(obj, explicit));
374    }
375
376    public static X509Name getInstance(
377        Object  obj)
378    {
379        if (obj == null || obj instanceof X509Name)
380        {
381            return (X509Name)obj;
382        }
383        else if (obj instanceof X500Name)
384        {
385            return new X509Name(ASN1Sequence.getInstance(((X500Name)obj).toASN1Primitive()));
386        }
387        else if (obj != null)
388        {
389            return new X509Name(ASN1Sequence.getInstance(obj));
390        }
391
392        return null;
393    }
394
395    protected X509Name()
396    {
397        // constructure use by new X500 Name class
398    }
399    /**
400     * Constructor from ASN1Sequence
401     *
402     * the principal will be a list of constructed sets, each containing an (OID, String) pair.
403     * @deprecated use X500Name.getInstance()
404     */
405    public X509Name(
406        ASN1Sequence  seq)
407    {
408        this.seq = seq;
409
410        Enumeration e = seq.getObjects();
411
412        while (e.hasMoreElements())
413        {
414            ASN1Set         set = ASN1Set.getInstance(((ASN1Encodable)e.nextElement()).toASN1Primitive());
415
416            for (int i = 0; i < set.size(); i++)
417            {
418                   ASN1Sequence s = ASN1Sequence.getInstance(set.getObjectAt(i).toASN1Primitive());
419
420                   if (s.size() != 2)
421                   {
422                       throw new IllegalArgumentException("badly sized pair");
423                   }
424
425                   ordering.addElement(ASN1ObjectIdentifier.getInstance(s.getObjectAt(0)));
426
427                   ASN1Encodable value = s.getObjectAt(1);
428                   if (value instanceof ASN1String && !(value instanceof DERUniversalString))
429                   {
430                       String v = ((ASN1String)value).getString();
431                       if (v.length() > 0 && v.charAt(0) == '#')
432                       {
433                           values.addElement("\\" + v);
434                       }
435                       else
436                       {
437                           values.addElement(v);
438                       }
439                   }
440                   else
441                   {
442                       try
443                       {
444                           values.addElement("#" + bytesToString(Hex.encode(value.toASN1Primitive().getEncoded(ASN1Encoding.DER))));
445                       }
446                       catch (IOException e1)
447                       {
448                           throw new IllegalArgumentException("cannot encode value");
449                       }
450                   }
451                   added.addElement((i != 0) ? TRUE : FALSE);  // to allow earlier JDK compatibility
452            }
453        }
454    }
455
456    /**
457     * constructor from a table of attributes.
458     * <p>
459     * it's is assumed the table contains OID/String pairs, and the contents
460     * of the table are copied into an internal table as part of the
461     * construction process.
462     * <p>
463     * <b>Note:</b> if the name you are trying to generate should be
464     * following a specific ordering, you should use the constructor
465     * with the ordering specified below.
466     * @deprecated use an ordered constructor! The hashtable ordering is rarely correct
467     */
468    public X509Name(
469        Hashtable  attributes)
470    {
471        this(null, attributes);
472    }
473
474    /**
475     * Constructor from a table of attributes with ordering.
476     * <p>
477     * it's is assumed the table contains OID/String pairs, and the contents
478     * of the table are copied into an internal table as part of the
479     * construction process. The ordering vector should contain the OIDs
480     * in the order they are meant to be encoded or printed in toString.
481     */
482    public X509Name(
483        Vector      ordering,
484        Hashtable   attributes)
485    {
486        this(ordering, attributes, new X509DefaultEntryConverter());
487    }
488
489    /**
490     * Constructor from a table of attributes with ordering.
491     * <p>
492     * it's is assumed the table contains OID/String pairs, and the contents
493     * of the table are copied into an internal table as part of the
494     * construction process. The ordering vector should contain the OIDs
495     * in the order they are meant to be encoded or printed in toString.
496     * <p>
497     * The passed in converter will be used to convert the strings into their
498     * ASN.1 counterparts.
499     * @deprecated use X500Name, X500NameBuilder
500     */
501    public X509Name(
502        Vector                   ordering,
503        Hashtable                attributes,
504        X509NameEntryConverter   converter)
505    {
506        this.converter = converter;
507
508        if (ordering != null)
509        {
510            for (int i = 0; i != ordering.size(); i++)
511            {
512                this.ordering.addElement(ordering.elementAt(i));
513                this.added.addElement(FALSE);
514            }
515        }
516        else
517        {
518            Enumeration     e = attributes.keys();
519
520            while (e.hasMoreElements())
521            {
522                this.ordering.addElement(e.nextElement());
523                this.added.addElement(FALSE);
524            }
525        }
526
527        for (int i = 0; i != this.ordering.size(); i++)
528        {
529            ASN1ObjectIdentifier     oid = (ASN1ObjectIdentifier)this.ordering.elementAt(i);
530
531            if (attributes.get(oid) == null)
532            {
533                throw new IllegalArgumentException("No attribute for object id - " + oid.getId() + " - passed to distinguished name");
534            }
535
536            this.values.addElement(attributes.get(oid)); // copy the hash table
537        }
538    }
539
540    /**
541     * Takes two vectors one of the oids and the other of the values.
542     * @deprecated use X500Name, X500NameBuilder
543     */
544    public X509Name(
545        Vector  oids,
546        Vector  values)
547    {
548        this(oids, values, new X509DefaultEntryConverter());
549    }
550
551    /**
552     * Takes two vectors one of the oids and the other of the values.
553     * <p>
554     * The passed in converter will be used to convert the strings into their
555     * ASN.1 counterparts.
556     * @deprecated use X500Name, X500NameBuilder
557     */
558    public X509Name(
559        Vector                  oids,
560        Vector                  values,
561        X509NameEntryConverter  converter)
562    {
563        this.converter = converter;
564
565        if (oids.size() != values.size())
566        {
567            throw new IllegalArgumentException("oids vector must be same length as values.");
568        }
569
570        for (int i = 0; i < oids.size(); i++)
571        {
572            this.ordering.addElement(oids.elementAt(i));
573            this.values.addElement(values.elementAt(i));
574            this.added.addElement(FALSE);
575        }
576    }
577
578//    private Boolean isEncoded(String s)
579//    {
580//        if (s.charAt(0) == '#')
581//        {
582//            return TRUE;
583//        }
584//
585//        return FALSE;
586//    }
587
588    /**
589     * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
590     * some such, converting it into an ordered set of name attributes.
591     * @deprecated use X500Name, X500NameBuilder
592     */
593    public X509Name(
594        String  dirName)
595    {
596        this(DefaultReverse, DefaultLookUp, dirName);
597    }
598
599    /**
600     * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
601     * some such, converting it into an ordered set of name attributes with each
602     * string value being converted to its associated ASN.1 type using the passed
603     * in converter.
604     * @deprecated use X500Name, X500NameBuilder
605     */
606    public X509Name(
607        String                  dirName,
608        X509NameEntryConverter  converter)
609    {
610        this(DefaultReverse, DefaultLookUp, dirName, converter);
611    }
612
613    /**
614     * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
615     * some such, converting it into an ordered set of name attributes. If reverse
616     * is true, create the encoded version of the sequence starting from the
617     * last element in the string.
618     * @deprecated use X500Name, X500NameBuilder
619     */
620    public X509Name(
621        boolean reverse,
622        String  dirName)
623    {
624        this(reverse, DefaultLookUp, dirName);
625    }
626
627    /**
628     * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
629     * some such, converting it into an ordered set of name attributes with each
630     * string value being converted to its associated ASN.1 type using the passed
631     * in converter. If reverse is true the ASN.1 sequence representing the DN will
632     * be built by starting at the end of the string, rather than the start.
633     * @deprecated use X500Name, X500NameBuilder
634     */
635    public X509Name(
636        boolean                 reverse,
637        String                  dirName,
638        X509NameEntryConverter  converter)
639    {
640        this(reverse, DefaultLookUp, dirName, converter);
641    }
642
643    /**
644     * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
645     * some such, converting it into an ordered set of name attributes. lookUp
646     * should provide a table of lookups, indexed by lowercase only strings and
647     * yielding a ASN1ObjectIdentifier, other than that OID. and numeric oids
648     * will be processed automatically.
649     * <br>
650     * If reverse is true, create the encoded version of the sequence
651     * starting from the last element in the string.
652     * @param reverse true if we should start scanning from the end (RFC 2553).
653     * @param lookUp table of names and their oids.
654     * @param dirName the X.500 string to be parsed.
655     * @deprecated use X500Name, X500NameBuilder
656     */
657    public X509Name(
658        boolean     reverse,
659        Hashtable   lookUp,
660        String      dirName)
661    {
662        this(reverse, lookUp, dirName, new X509DefaultEntryConverter());
663    }
664
665    private ASN1ObjectIdentifier decodeOID(
666        String      name,
667        Hashtable   lookUp)
668    {
669        name = name.trim();
670        if (Strings.toUpperCase(name).startsWith("OID."))
671        {
672            return new ASN1ObjectIdentifier(name.substring(4));
673        }
674        else if (name.charAt(0) >= '0' && name.charAt(0) <= '9')
675        {
676            return new ASN1ObjectIdentifier(name);
677        }
678
679        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)lookUp.get(Strings.toLowerCase(name));
680        if (oid == null)
681        {
682            throw new IllegalArgumentException("Unknown object id - " + name + " - passed to distinguished name");
683        }
684
685        return oid;
686    }
687
688    private String unescape(String elt)
689    {
690        if (elt.length() == 0 || (elt.indexOf('\\') < 0 && elt.indexOf('"') < 0))
691        {
692            return elt.trim();
693        }
694
695        char[] elts = elt.toCharArray();
696        boolean escaped = false;
697        boolean quoted = false;
698        StringBuffer buf = new StringBuffer(elt.length());
699        int start = 0;
700
701        // if it's an escaped hash string and not an actual encoding in string form
702        // we need to leave it escaped.
703        if (elts[0] == '\\')
704        {
705            if (elts[1] == '#')
706            {
707                start = 2;
708                buf.append("\\#");
709            }
710        }
711
712        boolean nonWhiteSpaceEncountered = false;
713        int     lastEscaped = 0;
714
715        for (int i = start; i != elts.length; i++)
716        {
717            char c = elts[i];
718
719            if (c != ' ')
720            {
721                nonWhiteSpaceEncountered = true;
722            }
723
724            if (c == '"')
725            {
726                if (!escaped)
727                {
728                    quoted = !quoted;
729                }
730                else
731                {
732                    buf.append(c);
733                }
734                escaped = false;
735            }
736            else if (c == '\\' && !(escaped || quoted))
737            {
738                escaped = true;
739                lastEscaped = buf.length();
740            }
741            else
742            {
743                if (c == ' ' && !escaped && !nonWhiteSpaceEncountered)
744                {
745                    continue;
746                }
747                buf.append(c);
748                escaped = false;
749            }
750        }
751
752        if (buf.length() > 0)
753        {
754            while (buf.charAt(buf.length() - 1) == ' ' && lastEscaped != (buf.length() - 1))
755            {
756                buf.setLength(buf.length() - 1);
757            }
758        }
759
760        return buf.toString();
761    }
762
763    /**
764     * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
765     * some such, converting it into an ordered set of name attributes. lookUp
766     * should provide a table of lookups, indexed by lowercase only strings and
767     * yielding a ASN1ObjectIdentifier, other than that OID. and numeric oids
768     * will be processed automatically. The passed in converter is used to convert the
769     * string values to the right of each equals sign to their ASN.1 counterparts.
770     * <br>
771     * @param reverse true if we should start scanning from the end, false otherwise.
772     * @param lookUp table of names and oids.
773     * @param dirName the string dirName
774     * @param converter the converter to convert string values into their ASN.1 equivalents
775     */
776    public X509Name(
777        boolean                 reverse,
778        Hashtable               lookUp,
779        String                  dirName,
780        X509NameEntryConverter  converter)
781    {
782        this.converter = converter;
783        X509NameTokenizer   nTok = new X509NameTokenizer(dirName);
784
785        while (nTok.hasMoreTokens())
786        {
787            String  token = nTok.nextToken();
788
789            if (token.indexOf('+') > 0)
790            {
791                X509NameTokenizer   pTok = new X509NameTokenizer(token, '+');
792
793                addEntry(lookUp, pTok.nextToken(), FALSE);
794
795                while (pTok.hasMoreTokens())
796                {
797                    addEntry(lookUp, pTok.nextToken(), TRUE);
798                }
799            }
800            else
801            {
802                addEntry(lookUp, token, FALSE);
803            }
804        }
805
806        if (reverse)
807        {
808            Vector  o = new Vector();
809            Vector  v = new Vector();
810            Vector  a = new Vector();
811
812            int count = 1;
813
814            for (int i = 0; i < this.ordering.size(); i++)
815            {
816                if (((Boolean)this.added.elementAt(i)).booleanValue())
817                {
818                    o.insertElementAt(this.ordering.elementAt(i), count);
819                    v.insertElementAt(this.values.elementAt(i), count);
820                    a.insertElementAt(this.added.elementAt(i), count);
821                    count++;
822                }
823                else
824                {
825                    o.insertElementAt(this.ordering.elementAt(i), 0);
826                    v.insertElementAt(this.values.elementAt(i), 0);
827                    a.insertElementAt(this.added.elementAt(i), 0);
828                    count = 1;
829                }
830            }
831
832            this.ordering = o;
833            this.values = v;
834            this.added = a;
835        }
836    }
837
838    private void addEntry(Hashtable lookUp, String token, Boolean isAdded)
839    {
840        X509NameTokenizer vTok;
841        String name;
842        String value;ASN1ObjectIdentifier oid;
843        vTok = new X509NameTokenizer(token, '=');
844
845        name = vTok.nextToken();
846
847        if (!vTok.hasMoreTokens())
848        {
849           throw new IllegalArgumentException("badly formatted directory string");
850        }
851
852        value = vTok.nextToken();
853
854        oid = decodeOID(name, lookUp);
855
856        this.ordering.addElement(oid);
857        this.values.addElement(unescape(value));
858        this.added.addElement(isAdded);
859    }
860
861    /**
862     * return a vector of the oids in the name, in the order they were found.
863     */
864    public Vector getOIDs()
865    {
866        Vector  v = new Vector();
867
868        for (int i = 0; i != ordering.size(); i++)
869        {
870            v.addElement(ordering.elementAt(i));
871        }
872
873        return v;
874    }
875
876    /**
877     * return a vector of the values found in the name, in the order they
878     * were found.
879     */
880    public Vector getValues()
881    {
882        Vector  v = new Vector();
883
884        for (int i = 0; i != values.size(); i++)
885        {
886            v.addElement(values.elementAt(i));
887        }
888
889        return v;
890    }
891
892    /**
893     * return a vector of the values found in the name, in the order they
894     * were found, with the DN label corresponding to passed in oid.
895     */
896    public Vector getValues(
897        ASN1ObjectIdentifier oid)
898    {
899        Vector  v = new Vector();
900
901        for (int i = 0; i != values.size(); i++)
902        {
903            if (ordering.elementAt(i).equals(oid))
904            {
905                String val = (String)values.elementAt(i);
906
907                if (val.length() > 2 && val.charAt(0) == '\\' && val.charAt(1) == '#')
908                {
909                    v.addElement(val.substring(1));
910                }
911                else
912                {
913                    v.addElement(val);
914                }
915            }
916        }
917
918        return v;
919    }
920
921    public ASN1Primitive toASN1Primitive()
922    {
923        if (seq == null)
924        {
925            ASN1EncodableVector  vec = new ASN1EncodableVector();
926            ASN1EncodableVector  sVec = new ASN1EncodableVector();
927            ASN1ObjectIdentifier  lstOid = null;
928
929            for (int i = 0; i != ordering.size(); i++)
930            {
931                ASN1EncodableVector     v = new ASN1EncodableVector();
932                ASN1ObjectIdentifier     oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
933
934                v.add(oid);
935
936                String  str = (String)values.elementAt(i);
937
938                v.add(converter.getConvertedValue(oid, str));
939
940                if (lstOid == null
941                    || ((Boolean)this.added.elementAt(i)).booleanValue())
942                {
943                    sVec.add(new DERSequence(v));
944                }
945                else
946                {
947                    vec.add(new DERSet(sVec));
948                    sVec = new ASN1EncodableVector();
949
950                    sVec.add(new DERSequence(v));
951                }
952
953                lstOid = oid;
954            }
955
956            vec.add(new DERSet(sVec));
957
958            seq = new DERSequence(vec);
959        }
960
961        return seq;
962    }
963
964    /**
965     * @param inOrder if true the order of both X509 names must be the same,
966     * as well as the values associated with each element.
967     */
968    public boolean equals(Object obj, boolean inOrder)
969    {
970        if (!inOrder)
971        {
972            return this.equals(obj);
973        }
974
975        if (obj == this)
976        {
977            return true;
978        }
979
980        if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
981        {
982            return false;
983        }
984
985        ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
986
987        if (this.toASN1Primitive().equals(derO))
988        {
989            return true;
990        }
991
992        X509Name other;
993
994        try
995        {
996            other = X509Name.getInstance(obj);
997        }
998        catch (IllegalArgumentException e)
999        {
1000            return false;
1001        }
1002
1003        int      orderingSize = ordering.size();
1004
1005        if (orderingSize != other.ordering.size())
1006        {
1007            return false;
1008        }
1009
1010        for (int i = 0; i < orderingSize; i++)
1011        {
1012            ASN1ObjectIdentifier  oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
1013            ASN1ObjectIdentifier  oOid = (ASN1ObjectIdentifier)other.ordering.elementAt(i);
1014
1015            if (oid.equals(oOid))
1016            {
1017                String value = (String)values.elementAt(i);
1018                String oValue = (String)other.values.elementAt(i);
1019
1020                if (!equivalentStrings(value, oValue))
1021                {
1022                    return false;
1023                }
1024            }
1025            else
1026            {
1027                return false;
1028            }
1029        }
1030
1031        return true;
1032    }
1033
1034    public int hashCode()
1035    {
1036        if (isHashCodeCalculated)
1037        {
1038            return hashCodeValue;
1039        }
1040
1041        isHashCodeCalculated = true;
1042
1043        // this needs to be order independent, like equals
1044        for (int i = 0; i != ordering.size(); i += 1)
1045        {
1046            String value = (String)values.elementAt(i);
1047
1048            value = canonicalize(value);
1049            value = stripInternalSpaces(value);
1050
1051            hashCodeValue ^= ordering.elementAt(i).hashCode();
1052            hashCodeValue ^= value.hashCode();
1053        }
1054
1055        return hashCodeValue;
1056    }
1057
1058    /**
1059     * test for equality - note: case is ignored.
1060     */
1061    public boolean equals(Object obj)
1062    {
1063        if (obj == this)
1064        {
1065            return true;
1066        }
1067
1068        if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
1069        {
1070            return false;
1071        }
1072
1073        ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
1074
1075        if (this.toASN1Primitive().equals(derO))
1076        {
1077            return true;
1078        }
1079
1080        X509Name other;
1081
1082        try
1083        {
1084            other = X509Name.getInstance(obj);
1085        }
1086        catch (IllegalArgumentException e)
1087        {
1088            return false;
1089        }
1090
1091        int      orderingSize = ordering.size();
1092
1093        if (orderingSize != other.ordering.size())
1094        {
1095            return false;
1096        }
1097
1098        boolean[] indexes = new boolean[orderingSize];
1099        int       start, end, delta;
1100
1101        if (ordering.elementAt(0).equals(other.ordering.elementAt(0)))   // guess forward
1102        {
1103            start = 0;
1104            end = orderingSize;
1105            delta = 1;
1106        }
1107        else  // guess reversed - most common problem
1108        {
1109            start = orderingSize - 1;
1110            end = -1;
1111            delta = -1;
1112        }
1113
1114        for (int i = start; i != end; i += delta)
1115        {
1116            boolean              found = false;
1117            ASN1ObjectIdentifier  oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
1118            String               value = (String)values.elementAt(i);
1119
1120            for (int j = 0; j < orderingSize; j++)
1121            {
1122                if (indexes[j])
1123                {
1124                    continue;
1125                }
1126
1127                ASN1ObjectIdentifier oOid = (ASN1ObjectIdentifier)other.ordering.elementAt(j);
1128
1129                if (oid.equals(oOid))
1130                {
1131                    String oValue = (String)other.values.elementAt(j);
1132
1133                    if (equivalentStrings(value, oValue))
1134                    {
1135                        indexes[j] = true;
1136                        found      = true;
1137                        break;
1138                    }
1139                }
1140            }
1141
1142            if (!found)
1143            {
1144                return false;
1145            }
1146        }
1147
1148        return true;
1149    }
1150
1151    private boolean equivalentStrings(String s1, String s2)
1152    {
1153        String value = canonicalize(s1);
1154        String oValue = canonicalize(s2);
1155
1156        if (!value.equals(oValue))
1157        {
1158            value = stripInternalSpaces(value);
1159            oValue = stripInternalSpaces(oValue);
1160
1161            if (!value.equals(oValue))
1162            {
1163                return false;
1164            }
1165        }
1166
1167        return true;
1168    }
1169
1170    private String canonicalize(String s)
1171    {
1172        String value = Strings.toLowerCase(s.trim());
1173
1174        if (value.length() > 0 && value.charAt(0) == '#')
1175        {
1176            ASN1Primitive obj = decodeObject(value);
1177
1178            if (obj instanceof ASN1String)
1179            {
1180                value = Strings.toLowerCase(((ASN1String)obj).getString().trim());
1181            }
1182        }
1183
1184        return value;
1185    }
1186
1187    private ASN1Primitive decodeObject(String oValue)
1188    {
1189        try
1190        {
1191            return ASN1Primitive.fromByteArray(Hex.decode(oValue.substring(1)));
1192        }
1193        catch (IOException e)
1194        {
1195            throw new IllegalStateException("unknown encoding in name: " + e);
1196        }
1197    }
1198
1199    private String stripInternalSpaces(
1200        String str)
1201    {
1202        StringBuffer res = new StringBuffer();
1203
1204        if (str.length() != 0)
1205        {
1206            char    c1 = str.charAt(0);
1207
1208            res.append(c1);
1209
1210            for (int k = 1; k < str.length(); k++)
1211            {
1212                char    c2 = str.charAt(k);
1213                if (!(c1 == ' ' && c2 == ' '))
1214                {
1215                    res.append(c2);
1216                }
1217                c1 = c2;
1218            }
1219        }
1220
1221        return res.toString();
1222    }
1223
1224    private void appendValue(
1225        StringBuffer        buf,
1226        Hashtable           oidSymbols,
1227        ASN1ObjectIdentifier oid,
1228        String              value)
1229    {
1230        String  sym = (String)oidSymbols.get(oid);
1231
1232        if (sym != null)
1233        {
1234            buf.append(sym);
1235        }
1236        else
1237        {
1238            buf.append(oid.getId());
1239        }
1240
1241        buf.append('=');
1242
1243        int start = buf.length();
1244        buf.append(value);
1245        int end = buf.length();
1246
1247        if (value.length() >= 2 && value.charAt(0) == '\\' && value.charAt(1) == '#')
1248        {
1249            start += 2;
1250        }
1251
1252        while (start < end && buf.charAt(start) == ' ')
1253        {
1254            buf.insert(start, "\\");
1255            start += 2;
1256            ++end;
1257        }
1258
1259        while (--end > start && buf.charAt(end) == ' ')
1260        {
1261            buf.insert(end, '\\');
1262        }
1263
1264        while (start <= end)
1265        {
1266            switch (buf.charAt(start))
1267            {
1268            case ',':
1269            case '"':
1270            case '\\':
1271            case '+':
1272            case '=':
1273            case '<':
1274            case '>':
1275            case ';':
1276                buf.insert(start, "\\");
1277                start += 2;
1278                ++end;
1279                break;
1280            default:
1281                ++start;
1282                break;
1283            }
1284        }
1285    }
1286
1287    /**
1288     * convert the structure to a string - if reverse is true the
1289     * oids and values are listed out starting with the last element
1290     * in the sequence (ala RFC 2253), otherwise the string will begin
1291     * with the first element of the structure. If no string definition
1292     * for the oid is found in oidSymbols the string value of the oid is
1293     * added. Two standard symbol tables are provided DefaultSymbols, and
1294     * RFC2253Symbols as part of this class.
1295     *
1296     * @param reverse if true start at the end of the sequence and work back.
1297     * @param oidSymbols look up table strings for oids.
1298     */
1299    public String toString(
1300        boolean     reverse,
1301        Hashtable   oidSymbols)
1302    {
1303        StringBuffer            buf = new StringBuffer();
1304        Vector                  components = new Vector();
1305        boolean                 first = true;
1306
1307        StringBuffer ava = null;
1308
1309        for (int i = 0; i < ordering.size(); i++)
1310        {
1311            if (((Boolean)added.elementAt(i)).booleanValue())
1312            {
1313                ava.append('+');
1314                appendValue(ava, oidSymbols,
1315                    (ASN1ObjectIdentifier)ordering.elementAt(i),
1316                    (String)values.elementAt(i));
1317            }
1318            else
1319            {
1320                ava = new StringBuffer();
1321                appendValue(ava, oidSymbols,
1322                    (ASN1ObjectIdentifier)ordering.elementAt(i),
1323                    (String)values.elementAt(i));
1324                components.addElement(ava);
1325            }
1326        }
1327
1328        if (reverse)
1329        {
1330            for (int i = components.size() - 1; i >= 0; i--)
1331            {
1332                if (first)
1333                {
1334                    first = false;
1335                }
1336                else
1337                {
1338                    buf.append(',');
1339                }
1340
1341                buf.append(components.elementAt(i).toString());
1342            }
1343        }
1344        else
1345        {
1346            for (int i = 0; i < components.size(); i++)
1347            {
1348                if (first)
1349                {
1350                    first = false;
1351                }
1352                else
1353                {
1354                    buf.append(',');
1355                }
1356
1357                buf.append(components.elementAt(i).toString());
1358            }
1359        }
1360
1361        return buf.toString();
1362    }
1363
1364    private String bytesToString(
1365        byte[] data)
1366    {
1367        char[]  cs = new char[data.length];
1368
1369        for (int i = 0; i != cs.length; i++)
1370        {
1371            cs[i] = (char)(data[i] & 0xff);
1372        }
1373
1374        return new String(cs);
1375    }
1376
1377    public String toString()
1378    {
1379        return toString(DefaultReverse, DefaultSymbols);
1380    }
1381}
1382