X509Name.java revision e1142c149e244797ce73b0e7fad40816e447a817
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
259    private static final Boolean TRUE = Boolean.TRUE;
260    private static final Boolean FALSE = Boolean.FALSE;
261    // END android-changed
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     */
404    public X509Name(
405        ASN1Sequence  seq)
406    {
407        this.seq = seq;
408
409        Enumeration e = seq.getObjects();
410
411        while (e.hasMoreElements())
412        {
413            ASN1Set         set = ASN1Set.getInstance(((ASN1Encodable)e.nextElement()).toASN1Primitive());
414
415            for (int i = 0; i < set.size(); i++)
416            {
417                   ASN1Sequence s = ASN1Sequence.getInstance(set.getObjectAt(i).toASN1Primitive());
418
419                   if (s.size() != 2)
420                   {
421                       throw new IllegalArgumentException("badly sized pair");
422                   }
423
424                   ordering.addElement(ASN1ObjectIdentifier.getInstance(s.getObjectAt(0)));
425
426                   ASN1Encodable value = s.getObjectAt(1);
427                   if (value instanceof ASN1String && !(value instanceof DERUniversalString))
428                   {
429                       String v = ((ASN1String)value).getString();
430                       if (v.length() > 0 && v.charAt(0) == '#')
431                       {
432                           values.addElement("\\" + v);
433                       }
434                       else
435                       {
436                           values.addElement(v);
437                       }
438                   }
439                   else
440                   {
441                       try
442                       {
443                           values.addElement("#" + bytesToString(Hex.encode(value.toASN1Primitive().getEncoded(ASN1Encoding.DER))));
444                       }
445                       catch (IOException e1)
446                       {
447                           throw new IllegalArgumentException("cannot encode value");
448                       }
449                   }
450                   // BEGIN android-changed
451                   added.addElement(Boolean.valueOf(i != 0));
452                   // END android-changed
453            }
454        }
455    }
456
457    /**
458     * constructor from a table of attributes.
459     * <p>
460     * it's is assumed the table contains OID/String pairs, and the contents
461     * of the table are copied into an internal table as part of the
462     * construction process.
463     * <p>
464     * <b>Note:</b> if the name you are trying to generate should be
465     * following a specific ordering, you should use the constructor
466     * with the ordering specified below.
467     * @deprecated use an ordered constructor! The hashtable ordering is rarely correct
468     */
469    public X509Name(
470        Hashtable  attributes)
471    {
472        this(null, attributes);
473    }
474
475    /**
476     * Constructor from a table of attributes with ordering.
477     * <p>
478     * it's is assumed the table contains OID/String pairs, and the contents
479     * of the table are copied into an internal table as part of the
480     * construction process. The ordering vector should contain the OIDs
481     * in the order they are meant to be encoded or printed in toString.
482     */
483    public X509Name(
484        Vector      ordering,
485        Hashtable   attributes)
486    {
487        this(ordering, attributes, new X509DefaultEntryConverter());
488    }
489
490    /**
491     * Constructor from a table of attributes with ordering.
492     * <p>
493     * it's is assumed the table contains OID/String pairs, and the contents
494     * of the table are copied into an internal table as part of the
495     * construction process. The ordering vector should contain the OIDs
496     * in the order they are meant to be encoded or printed in toString.
497     * <p>
498     * The passed in converter will be used to convert the strings into their
499     * ASN.1 counterparts.
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     */
543    public X509Name(
544        Vector  oids,
545        Vector  values)
546    {
547        this(oids, values, new X509DefaultEntryConverter());
548    }
549
550    /**
551     * Takes two vectors one of the oids and the other of the values.
552     * <p>
553     * The passed in converter will be used to convert the strings into their
554     * ASN.1 counterparts.
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     */
590    public X509Name(
591        String  dirName)
592    {
593        this(DefaultReverse, DefaultLookUp, dirName);
594    }
595
596    /**
597     * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
598     * some such, converting it into an ordered set of name attributes with each
599     * string value being converted to its associated ASN.1 type using the passed
600     * in converter.
601     */
602    public X509Name(
603        String                  dirName,
604        X509NameEntryConverter  converter)
605    {
606        this(DefaultReverse, DefaultLookUp, dirName, converter);
607    }
608
609    /**
610     * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
611     * some such, converting it into an ordered set of name attributes. If reverse
612     * is true, create the encoded version of the sequence starting from the
613     * last element in the string.
614     */
615    public X509Name(
616        boolean reverse,
617        String  dirName)
618    {
619        this(reverse, DefaultLookUp, dirName);
620    }
621
622    /**
623     * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
624     * some such, converting it into an ordered set of name attributes with each
625     * string value being converted to its associated ASN.1 type using the passed
626     * in converter. If reverse is true the ASN.1 sequence representing the DN will
627     * be built by starting at the end of the string, rather than the start.
628     */
629    public X509Name(
630        boolean                 reverse,
631        String                  dirName,
632        X509NameEntryConverter  converter)
633    {
634        this(reverse, DefaultLookUp, dirName, converter);
635    }
636
637    /**
638     * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
639     * some such, converting it into an ordered set of name attributes. lookUp
640     * should provide a table of lookups, indexed by lowercase only strings and
641     * yielding a ASN1ObjectIdentifier, other than that OID. and numeric oids
642     * will be processed automatically.
643     * <br>
644     * If reverse is true, create the encoded version of the sequence
645     * starting from the last element in the string.
646     * @param reverse true if we should start scanning from the end (RFC 2553).
647     * @param lookUp table of names and their oids.
648     * @param dirName the X.500 string to be parsed.
649     */
650    public X509Name(
651        boolean     reverse,
652        Hashtable   lookUp,
653        String      dirName)
654    {
655        this(reverse, lookUp, dirName, new X509DefaultEntryConverter());
656    }
657
658    private ASN1ObjectIdentifier decodeOID(
659        String      name,
660        Hashtable   lookUp)
661    {
662        if (Strings.toUpperCase(name).startsWith("OID."))
663        {
664            return new ASN1ObjectIdentifier(name.substring(4));
665        }
666        else if (name.charAt(0) >= '0' && name.charAt(0) <= '9')
667        {
668            return new ASN1ObjectIdentifier(name);
669        }
670
671        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)lookUp.get(Strings.toLowerCase(name));
672        if (oid == null)
673        {
674            throw new IllegalArgumentException("Unknown object id - " + name + " - passed to distinguished name");
675        }
676
677        return oid;
678    }
679
680    /**
681     * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
682     * some such, converting it into an ordered set of name attributes. lookUp
683     * should provide a table of lookups, indexed by lowercase only strings and
684     * yielding a ASN1ObjectIdentifier, other than that OID. and numeric oids
685     * will be processed automatically. The passed in converter is used to convert the
686     * string values to the right of each equals sign to their ASN.1 counterparts.
687     * <br>
688     * @param reverse true if we should start scanning from the end, false otherwise.
689     * @param lookUp table of names and oids.
690     * @param dirName the string dirName
691     * @param converter the converter to convert string values into their ASN.1 equivalents
692     */
693    public X509Name(
694        boolean                 reverse,
695        Hashtable               lookUp,
696        String                  dirName,
697        X509NameEntryConverter  converter)
698    {
699        this.converter = converter;
700        X509NameTokenizer   nTok = new X509NameTokenizer(dirName);
701
702        while (nTok.hasMoreTokens())
703        {
704            String  token = nTok.nextToken();
705            int     index = token.indexOf('=');
706
707            if (index == -1)
708            {
709                throw new IllegalArgumentException("badly formatted directory string");
710            }
711
712            String              name = token.substring(0, index);
713            String              value = token.substring(index + 1);
714            ASN1ObjectIdentifier oid = decodeOID(name, lookUp);
715
716            if (value.indexOf('+') > 0)
717            {
718                X509NameTokenizer   vTok = new X509NameTokenizer(value, '+');
719                String  v = vTok.nextToken();
720
721                this.ordering.addElement(oid);
722                this.values.addElement(v);
723                this.added.addElement(FALSE);
724
725                while (vTok.hasMoreTokens())
726                {
727                    String  sv = vTok.nextToken();
728                    int     ndx = sv.indexOf('=');
729
730                    String  nm = sv.substring(0, ndx);
731                    String  vl = sv.substring(ndx + 1);
732                    this.ordering.addElement(decodeOID(nm, lookUp));
733                    this.values.addElement(vl);
734                    this.added.addElement(TRUE);
735                }
736            }
737            else
738            {
739                this.ordering.addElement(oid);
740                this.values.addElement(value);
741                this.added.addElement(FALSE);
742            }
743        }
744
745        if (reverse)
746        {
747            Vector  o = new Vector();
748            Vector  v = new Vector();
749            Vector  a = new Vector();
750
751            int count = 1;
752
753            for (int i = 0; i < this.ordering.size(); i++)
754            {
755                if (((Boolean)this.added.elementAt(i)).booleanValue())
756                {
757                    o.insertElementAt(this.ordering.elementAt(i), count);
758                    v.insertElementAt(this.values.elementAt(i), count);
759                    a.insertElementAt(this.added.elementAt(i), count);
760                    count++;
761                }
762                else
763                {
764                    o.insertElementAt(this.ordering.elementAt(i), 0);
765                    v.insertElementAt(this.values.elementAt(i), 0);
766                    a.insertElementAt(this.added.elementAt(i), 0);
767                    count = 1;
768                }
769            }
770
771            this.ordering = o;
772            this.values = v;
773            this.added = a;
774        }
775    }
776
777    /**
778     * return a vector of the oids in the name, in the order they were found.
779     */
780    public Vector getOIDs()
781    {
782        Vector  v = new Vector();
783
784        for (int i = 0; i != ordering.size(); i++)
785        {
786            v.addElement(ordering.elementAt(i));
787        }
788
789        return v;
790    }
791
792    /**
793     * return a vector of the values found in the name, in the order they
794     * were found.
795     */
796    public Vector getValues()
797    {
798        Vector  v = new Vector();
799
800        for (int i = 0; i != values.size(); i++)
801        {
802            v.addElement(values.elementAt(i));
803        }
804
805        return v;
806    }
807
808    /**
809     * return a vector of the values found in the name, in the order they
810     * were found, with the DN label corresponding to passed in oid.
811     */
812    public Vector getValues(
813        ASN1ObjectIdentifier oid)
814    {
815        Vector  v = new Vector();
816
817        for (int i = 0; i != values.size(); i++)
818        {
819            if (ordering.elementAt(i).equals(oid))
820            {
821                String val = (String)values.elementAt(i);
822
823                if (val.length() > 2 && val.charAt(0) == '\\' && val.charAt(1) == '#')
824                {
825                    v.addElement(val.substring(1));
826                }
827                else
828                {
829                    v.addElement(val);
830                }
831            }
832        }
833
834        return v;
835    }
836
837    public ASN1Primitive toASN1Primitive()
838    {
839        if (seq == null)
840        {
841            ASN1EncodableVector  vec = new ASN1EncodableVector();
842            ASN1EncodableVector  sVec = new ASN1EncodableVector();
843            ASN1ObjectIdentifier  lstOid = null;
844
845            for (int i = 0; i != ordering.size(); i++)
846            {
847                ASN1EncodableVector     v = new ASN1EncodableVector();
848                ASN1ObjectIdentifier     oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
849
850                v.add(oid);
851
852                String  str = (String)values.elementAt(i);
853
854                v.add(converter.getConvertedValue(oid, str));
855
856                if (lstOid == null
857                    || ((Boolean)this.added.elementAt(i)).booleanValue())
858                {
859                    sVec.add(new DERSequence(v));
860                }
861                else
862                {
863                    vec.add(new DERSet(sVec));
864                    sVec = new ASN1EncodableVector();
865
866                    sVec.add(new DERSequence(v));
867                }
868
869                lstOid = oid;
870            }
871
872            vec.add(new DERSet(sVec));
873
874            seq = new DERSequence(vec);
875        }
876
877        return seq;
878    }
879
880    /**
881     * @param inOrder if true the order of both X509 names must be the same,
882     * as well as the values associated with each element.
883     */
884    public boolean equals(Object obj, boolean inOrder)
885    {
886        if (!inOrder)
887        {
888            return this.equals(obj);
889        }
890
891        if (obj == this)
892        {
893            return true;
894        }
895
896        if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
897        {
898            return false;
899        }
900
901        ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
902
903        if (this.toASN1Primitive().equals(derO))
904        {
905            return true;
906        }
907
908        X509Name other;
909
910        try
911        {
912            other = X509Name.getInstance(obj);
913        }
914        catch (IllegalArgumentException e)
915        {
916            return false;
917        }
918
919        int      orderingSize = ordering.size();
920
921        if (orderingSize != other.ordering.size())
922        {
923            return false;
924        }
925
926        for (int i = 0; i < orderingSize; i++)
927        {
928            ASN1ObjectIdentifier  oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
929            ASN1ObjectIdentifier  oOid = (ASN1ObjectIdentifier)other.ordering.elementAt(i);
930
931            if (oid.equals(oOid))
932            {
933                String value = (String)values.elementAt(i);
934                String oValue = (String)other.values.elementAt(i);
935
936                if (!equivalentStrings(value, oValue))
937                {
938                    return false;
939                }
940            }
941            else
942            {
943                return false;
944            }
945        }
946
947        return true;
948    }
949
950    public int hashCode()
951    {
952        if (isHashCodeCalculated)
953        {
954            return hashCodeValue;
955        }
956
957        isHashCodeCalculated = true;
958
959        // this needs to be order independent, like equals
960        for (int i = 0; i != ordering.size(); i += 1)
961        {
962            String value = (String)values.elementAt(i);
963
964            value = canonicalize(value);
965            value = stripInternalSpaces(value);
966
967            hashCodeValue ^= ordering.elementAt(i).hashCode();
968            hashCodeValue ^= value.hashCode();
969        }
970
971        return hashCodeValue;
972    }
973
974    /**
975     * test for equality - note: case is ignored.
976     */
977    public boolean equals(Object obj)
978    {
979        if (obj == this)
980        {
981            return true;
982        }
983
984        if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
985        {
986            return false;
987        }
988
989        ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
990
991        if (this.toASN1Primitive().equals(derO))
992        {
993            return true;
994        }
995
996        X509Name other;
997
998        try
999        {
1000            other = X509Name.getInstance(obj);
1001        }
1002        catch (IllegalArgumentException e)
1003        {
1004            return false;
1005        }
1006
1007        int      orderingSize = ordering.size();
1008
1009        if (orderingSize != other.ordering.size())
1010        {
1011            return false;
1012        }
1013
1014        boolean[] indexes = new boolean[orderingSize];
1015        int       start, end, delta;
1016
1017        if (ordering.elementAt(0).equals(other.ordering.elementAt(0)))   // guess forward
1018        {
1019            start = 0;
1020            end = orderingSize;
1021            delta = 1;
1022        }
1023        else  // guess reversed - most common problem
1024        {
1025            start = orderingSize - 1;
1026            end = -1;
1027            delta = -1;
1028        }
1029
1030        for (int i = start; i != end; i += delta)
1031        {
1032            boolean              found = false;
1033            ASN1ObjectIdentifier  oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
1034            String               value = (String)values.elementAt(i);
1035
1036            for (int j = 0; j < orderingSize; j++)
1037            {
1038                if (indexes[j])
1039                {
1040                    continue;
1041                }
1042
1043                ASN1ObjectIdentifier oOid = (ASN1ObjectIdentifier)other.ordering.elementAt(j);
1044
1045                if (oid.equals(oOid))
1046                {
1047                    String oValue = (String)other.values.elementAt(j);
1048
1049                    if (equivalentStrings(value, oValue))
1050                    {
1051                        indexes[j] = true;
1052                        found      = true;
1053                        break;
1054                    }
1055                }
1056            }
1057
1058            if (!found)
1059            {
1060                return false;
1061            }
1062        }
1063
1064        return true;
1065    }
1066
1067    private boolean equivalentStrings(String s1, String s2)
1068    {
1069        String value = canonicalize(s1);
1070        String oValue = canonicalize(s2);
1071
1072        if (!value.equals(oValue))
1073        {
1074            value = stripInternalSpaces(value);
1075            oValue = stripInternalSpaces(oValue);
1076
1077            if (!value.equals(oValue))
1078            {
1079                return false;
1080            }
1081        }
1082
1083        return true;
1084    }
1085
1086    private String canonicalize(String s)
1087    {
1088        String value = Strings.toLowerCase(s.trim());
1089
1090        if (value.length() > 0 && value.charAt(0) == '#')
1091        {
1092            ASN1Primitive obj = decodeObject(value);
1093
1094            if (obj instanceof ASN1String)
1095            {
1096                value = Strings.toLowerCase(((ASN1String)obj).getString().trim());
1097            }
1098        }
1099
1100        return value;
1101    }
1102
1103    private ASN1Primitive decodeObject(String oValue)
1104    {
1105        try
1106        {
1107            return ASN1Primitive.fromByteArray(Hex.decode(oValue.substring(1)));
1108        }
1109        catch (IOException e)
1110        {
1111            throw new IllegalStateException("unknown encoding in name: " + e);
1112        }
1113    }
1114
1115    private String stripInternalSpaces(
1116        String str)
1117    {
1118        StringBuffer res = new StringBuffer();
1119
1120        if (str.length() != 0)
1121        {
1122            char    c1 = str.charAt(0);
1123
1124            res.append(c1);
1125
1126            for (int k = 1; k < str.length(); k++)
1127            {
1128                char    c2 = str.charAt(k);
1129                if (!(c1 == ' ' && c2 == ' '))
1130                {
1131                    res.append(c2);
1132                }
1133                c1 = c2;
1134            }
1135        }
1136
1137        return res.toString();
1138    }
1139
1140    private void appendValue(
1141        StringBuffer        buf,
1142        Hashtable           oidSymbols,
1143        ASN1ObjectIdentifier oid,
1144        String              value)
1145    {
1146        String  sym = (String)oidSymbols.get(oid);
1147
1148        if (sym != null)
1149        {
1150            buf.append(sym);
1151        }
1152        else
1153        {
1154            buf.append(oid.getId());
1155        }
1156
1157        buf.append('=');
1158
1159        int     index = buf.length();
1160
1161        buf.append(value);
1162
1163        int     end = buf.length();
1164
1165        if (value.length() >= 2 && value.charAt(0) == '\\' && value.charAt(1) == '#')
1166        {
1167            index += 2;
1168        }
1169
1170        while (index != end)
1171        {
1172            if ((buf.charAt(index) == ',')
1173               || (buf.charAt(index) == '"')
1174               || (buf.charAt(index) == '\\')
1175               || (buf.charAt(index) == '+')
1176               || (buf.charAt(index) == '=')
1177               || (buf.charAt(index) == '<')
1178               || (buf.charAt(index) == '>')
1179               || (buf.charAt(index) == ';'))
1180            {
1181                buf.insert(index, "\\");
1182                index++;
1183                end++;
1184            }
1185
1186            index++;
1187        }
1188    }
1189
1190    /**
1191     * convert the structure to a string - if reverse is true the
1192     * oids and values are listed out starting with the last element
1193     * in the sequence (ala RFC 2253), otherwise the string will begin
1194     * with the first element of the structure. If no string definition
1195     * for the oid is found in oidSymbols the string value of the oid is
1196     * added. Two standard symbol tables are provided DefaultSymbols, and
1197     * RFC2253Symbols as part of this class.
1198     *
1199     * @param reverse if true start at the end of the sequence and work back.
1200     * @param oidSymbols look up table strings for oids.
1201     */
1202    public String toString(
1203        boolean     reverse,
1204        Hashtable   oidSymbols)
1205    {
1206        StringBuffer            buf = new StringBuffer();
1207        Vector                  components = new Vector();
1208        boolean                 first = true;
1209
1210        StringBuffer ava = null;
1211
1212        for (int i = 0; i < ordering.size(); i++)
1213        {
1214            if (((Boolean)added.elementAt(i)).booleanValue())
1215            {
1216                ava.append('+');
1217                appendValue(ava, oidSymbols,
1218                    (ASN1ObjectIdentifier)ordering.elementAt(i),
1219                    (String)values.elementAt(i));
1220            }
1221            else
1222            {
1223                ava = new StringBuffer();
1224                appendValue(ava, oidSymbols,
1225                    (ASN1ObjectIdentifier)ordering.elementAt(i),
1226                    (String)values.elementAt(i));
1227                components.addElement(ava);
1228            }
1229        }
1230
1231        if (reverse)
1232        {
1233            for (int i = components.size() - 1; i >= 0; i--)
1234            {
1235                if (first)
1236                {
1237                    first = false;
1238                }
1239                else
1240                {
1241                    buf.append(',');
1242                }
1243
1244                buf.append(components.elementAt(i).toString());
1245            }
1246        }
1247        else
1248        {
1249            for (int i = 0; i < components.size(); i++)
1250            {
1251                if (first)
1252                {
1253                    first = false;
1254                }
1255                else
1256                {
1257                    buf.append(',');
1258                }
1259
1260                buf.append(components.elementAt(i).toString());
1261            }
1262        }
1263
1264        return buf.toString();
1265    }
1266
1267    private String bytesToString(
1268        byte[] data)
1269    {
1270        char[]  cs = new char[data.length];
1271
1272        for (int i = 0; i != cs.length; i++)
1273        {
1274            cs[i] = (char)(data[i] & 0xff);
1275        }
1276
1277        return new String(cs);
1278    }
1279
1280    public String toString()
1281    {
1282        return toString(DefaultReverse, DefaultSymbols);
1283    }
1284}
1285