X509Name.java revision e6bf3e8dfa2804891a82075cb469b736321b4827
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                // BEGIN android-changed
710                throw new IllegalArgumentException("badly formatted directory string");
711                // END android-changed
712            }
713
714            String              name = token.substring(0, index);
715            String              value = token.substring(index + 1);
716            ASN1ObjectIdentifier oid = decodeOID(name, lookUp);
717
718            if (value.indexOf('+') > 0)
719            {
720                X509NameTokenizer   vTok = new X509NameTokenizer(value, '+');
721                String  v = vTok.nextToken();
722
723                this.ordering.addElement(oid);
724                this.values.addElement(v);
725                this.added.addElement(FALSE);
726
727                while (vTok.hasMoreTokens())
728                {
729                    String  sv = vTok.nextToken();
730                    int     ndx = sv.indexOf('=');
731
732                    String  nm = sv.substring(0, ndx);
733                    String  vl = sv.substring(ndx + 1);
734                    this.ordering.addElement(decodeOID(nm, lookUp));
735                    this.values.addElement(vl);
736                    this.added.addElement(TRUE);
737                }
738            }
739            else
740            {
741                this.ordering.addElement(oid);
742                this.values.addElement(value);
743                this.added.addElement(FALSE);
744            }
745        }
746
747        if (reverse)
748        {
749            Vector  o = new Vector();
750            Vector  v = new Vector();
751            Vector  a = new Vector();
752
753            int count = 1;
754
755            for (int i = 0; i < this.ordering.size(); i++)
756            {
757                if (((Boolean)this.added.elementAt(i)).booleanValue())
758                {
759                    o.insertElementAt(this.ordering.elementAt(i), count);
760                    v.insertElementAt(this.values.elementAt(i), count);
761                    a.insertElementAt(this.added.elementAt(i), count);
762                    count++;
763                }
764                else
765                {
766                    o.insertElementAt(this.ordering.elementAt(i), 0);
767                    v.insertElementAt(this.values.elementAt(i), 0);
768                    a.insertElementAt(this.added.elementAt(i), 0);
769                    count = 1;
770                }
771            }
772
773            this.ordering = o;
774            this.values = v;
775            this.added = a;
776        }
777    }
778
779    /**
780     * return a vector of the oids in the name, in the order they were found.
781     */
782    public Vector getOIDs()
783    {
784        Vector  v = new Vector();
785
786        for (int i = 0; i != ordering.size(); i++)
787        {
788            v.addElement(ordering.elementAt(i));
789        }
790
791        return v;
792    }
793
794    /**
795     * return a vector of the values found in the name, in the order they
796     * were found.
797     */
798    public Vector getValues()
799    {
800        Vector  v = new Vector();
801
802        for (int i = 0; i != values.size(); i++)
803        {
804            v.addElement(values.elementAt(i));
805        }
806
807        return v;
808    }
809
810    /**
811     * return a vector of the values found in the name, in the order they
812     * were found, with the DN label corresponding to passed in oid.
813     */
814    public Vector getValues(
815        ASN1ObjectIdentifier oid)
816    {
817        Vector  v = new Vector();
818
819        for (int i = 0; i != values.size(); i++)
820        {
821            if (ordering.elementAt(i).equals(oid))
822            {
823                String val = (String)values.elementAt(i);
824
825                if (val.length() > 2 && val.charAt(0) == '\\' && val.charAt(1) == '#')
826                {
827                    v.addElement(val.substring(1));
828                }
829                else
830                {
831                    v.addElement(val);
832                }
833            }
834        }
835
836        return v;
837    }
838
839    public ASN1Primitive toASN1Primitive()
840    {
841        if (seq == null)
842        {
843            ASN1EncodableVector  vec = new ASN1EncodableVector();
844            ASN1EncodableVector  sVec = new ASN1EncodableVector();
845            ASN1ObjectIdentifier  lstOid = null;
846
847            for (int i = 0; i != ordering.size(); i++)
848            {
849                ASN1EncodableVector     v = new ASN1EncodableVector();
850                ASN1ObjectIdentifier     oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
851
852                v.add(oid);
853
854                String  str = (String)values.elementAt(i);
855
856                v.add(converter.getConvertedValue(oid, str));
857
858                if (lstOid == null
859                    || ((Boolean)this.added.elementAt(i)).booleanValue())
860                {
861                    sVec.add(new DERSequence(v));
862                }
863                else
864                {
865                    vec.add(new DERSet(sVec));
866                    sVec = new ASN1EncodableVector();
867
868                    sVec.add(new DERSequence(v));
869                }
870
871                lstOid = oid;
872            }
873
874            vec.add(new DERSet(sVec));
875
876            seq = new DERSequence(vec);
877        }
878
879        return seq;
880    }
881
882    /**
883     * @param inOrder if true the order of both X509 names must be the same,
884     * as well as the values associated with each element.
885     */
886    public boolean equals(Object obj, boolean inOrder)
887    {
888        if (!inOrder)
889        {
890            return this.equals(obj);
891        }
892
893        if (obj == this)
894        {
895            return true;
896        }
897
898        if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
899        {
900            return false;
901        }
902
903        ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
904
905        if (this.toASN1Primitive().equals(derO))
906        {
907            return true;
908        }
909
910        X509Name other;
911
912        try
913        {
914            other = X509Name.getInstance(obj);
915        }
916        catch (IllegalArgumentException e)
917        {
918            return false;
919        }
920
921        int      orderingSize = ordering.size();
922
923        if (orderingSize != other.ordering.size())
924        {
925            return false;
926        }
927
928        for (int i = 0; i < orderingSize; i++)
929        {
930            ASN1ObjectIdentifier  oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
931            ASN1ObjectIdentifier  oOid = (ASN1ObjectIdentifier)other.ordering.elementAt(i);
932
933            if (oid.equals(oOid))
934            {
935                String value = (String)values.elementAt(i);
936                String oValue = (String)other.values.elementAt(i);
937
938                if (!equivalentStrings(value, oValue))
939                {
940                    return false;
941                }
942            }
943            else
944            {
945                return false;
946            }
947        }
948
949        return true;
950    }
951
952    public int hashCode()
953    {
954        if (isHashCodeCalculated)
955        {
956            return hashCodeValue;
957        }
958
959        isHashCodeCalculated = true;
960
961        // this needs to be order independent, like equals
962        for (int i = 0; i != ordering.size(); i += 1)
963        {
964            String value = (String)values.elementAt(i);
965
966            value = canonicalize(value);
967            value = stripInternalSpaces(value);
968
969            hashCodeValue ^= ordering.elementAt(i).hashCode();
970            hashCodeValue ^= value.hashCode();
971        }
972
973        return hashCodeValue;
974    }
975
976    /**
977     * test for equality - note: case is ignored.
978     */
979    public boolean equals(Object obj)
980    {
981        if (obj == this)
982        {
983            return true;
984        }
985
986        if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
987        {
988            return false;
989        }
990
991        ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
992
993        if (this.toASN1Primitive().equals(derO))
994        {
995            return true;
996        }
997
998        X509Name other;
999
1000        try
1001        {
1002            other = X509Name.getInstance(obj);
1003        }
1004        catch (IllegalArgumentException e)
1005        {
1006            return false;
1007        }
1008
1009        int      orderingSize = ordering.size();
1010
1011        if (orderingSize != other.ordering.size())
1012        {
1013            return false;
1014        }
1015
1016        boolean[] indexes = new boolean[orderingSize];
1017        int       start, end, delta;
1018
1019        if (ordering.elementAt(0).equals(other.ordering.elementAt(0)))   // guess forward
1020        {
1021            start = 0;
1022            end = orderingSize;
1023            delta = 1;
1024        }
1025        else  // guess reversed - most common problem
1026        {
1027            start = orderingSize - 1;
1028            end = -1;
1029            delta = -1;
1030        }
1031
1032        for (int i = start; i != end; i += delta)
1033        {
1034            boolean              found = false;
1035            ASN1ObjectIdentifier  oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
1036            String               value = (String)values.elementAt(i);
1037
1038            for (int j = 0; j < orderingSize; j++)
1039            {
1040                if (indexes[j])
1041                {
1042                    continue;
1043                }
1044
1045                ASN1ObjectIdentifier oOid = (ASN1ObjectIdentifier)other.ordering.elementAt(j);
1046
1047                if (oid.equals(oOid))
1048                {
1049                    String oValue = (String)other.values.elementAt(j);
1050
1051                    if (equivalentStrings(value, oValue))
1052                    {
1053                        indexes[j] = true;
1054                        found      = true;
1055                        break;
1056                    }
1057                }
1058            }
1059
1060            if (!found)
1061            {
1062                return false;
1063            }
1064        }
1065
1066        return true;
1067    }
1068
1069    private boolean equivalentStrings(String s1, String s2)
1070    {
1071        String value = canonicalize(s1);
1072        String oValue = canonicalize(s2);
1073
1074        if (!value.equals(oValue))
1075        {
1076            value = stripInternalSpaces(value);
1077            oValue = stripInternalSpaces(oValue);
1078
1079            if (!value.equals(oValue))
1080            {
1081                return false;
1082            }
1083        }
1084
1085        return true;
1086    }
1087
1088    private String canonicalize(String s)
1089    {
1090        String value = Strings.toLowerCase(s.trim());
1091
1092        if (value.length() > 0 && value.charAt(0) == '#')
1093        {
1094            ASN1Primitive obj = decodeObject(value);
1095
1096            if (obj instanceof ASN1String)
1097            {
1098                value = Strings.toLowerCase(((ASN1String)obj).getString().trim());
1099            }
1100        }
1101
1102        return value;
1103    }
1104
1105    private ASN1Primitive decodeObject(String oValue)
1106    {
1107        try
1108        {
1109            return ASN1Primitive.fromByteArray(Hex.decode(oValue.substring(1)));
1110        }
1111        catch (IOException e)
1112        {
1113            throw new IllegalStateException("unknown encoding in name: " + e);
1114        }
1115    }
1116
1117    private String stripInternalSpaces(
1118        String str)
1119    {
1120        StringBuffer res = new StringBuffer();
1121
1122        if (str.length() != 0)
1123        {
1124            char    c1 = str.charAt(0);
1125
1126            res.append(c1);
1127
1128            for (int k = 1; k < str.length(); k++)
1129            {
1130                char    c2 = str.charAt(k);
1131                if (!(c1 == ' ' && c2 == ' '))
1132                {
1133                    res.append(c2);
1134                }
1135                c1 = c2;
1136            }
1137        }
1138
1139        return res.toString();
1140    }
1141
1142    private void appendValue(
1143        StringBuffer        buf,
1144        Hashtable           oidSymbols,
1145        ASN1ObjectIdentifier oid,
1146        String              value)
1147    {
1148        String  sym = (String)oidSymbols.get(oid);
1149
1150        if (sym != null)
1151        {
1152            buf.append(sym);
1153        }
1154        else
1155        {
1156            buf.append(oid.getId());
1157        }
1158
1159        buf.append('=');
1160
1161        int     index = buf.length();
1162
1163        buf.append(value);
1164
1165        int     end = buf.length();
1166
1167        if (value.length() >= 2 && value.charAt(0) == '\\' && value.charAt(1) == '#')
1168        {
1169            index += 2;
1170        }
1171
1172        while (index != end)
1173        {
1174            if ((buf.charAt(index) == ',')
1175               || (buf.charAt(index) == '"')
1176               || (buf.charAt(index) == '\\')
1177               || (buf.charAt(index) == '+')
1178               || (buf.charAt(index) == '=')
1179               || (buf.charAt(index) == '<')
1180               || (buf.charAt(index) == '>')
1181               || (buf.charAt(index) == ';'))
1182            {
1183                buf.insert(index, "\\");
1184                index++;
1185                end++;
1186            }
1187
1188            index++;
1189        }
1190    }
1191
1192    /**
1193     * convert the structure to a string - if reverse is true the
1194     * oids and values are listed out starting with the last element
1195     * in the sequence (ala RFC 2253), otherwise the string will begin
1196     * with the first element of the structure. If no string definition
1197     * for the oid is found in oidSymbols the string value of the oid is
1198     * added. Two standard symbol tables are provided DefaultSymbols, and
1199     * RFC2253Symbols as part of this class.
1200     *
1201     * @param reverse if true start at the end of the sequence and work back.
1202     * @param oidSymbols look up table strings for oids.
1203     */
1204    public String toString(
1205        boolean     reverse,
1206        Hashtable   oidSymbols)
1207    {
1208        StringBuffer            buf = new StringBuffer();
1209        Vector                  components = new Vector();
1210        boolean                 first = true;
1211
1212        StringBuffer ava = null;
1213
1214        for (int i = 0; i < ordering.size(); i++)
1215        {
1216            if (((Boolean)added.elementAt(i)).booleanValue())
1217            {
1218                ava.append('+');
1219                appendValue(ava, oidSymbols,
1220                    (ASN1ObjectIdentifier)ordering.elementAt(i),
1221                    (String)values.elementAt(i));
1222            }
1223            else
1224            {
1225                ava = new StringBuffer();
1226                appendValue(ava, oidSymbols,
1227                    (ASN1ObjectIdentifier)ordering.elementAt(i),
1228                    (String)values.elementAt(i));
1229                components.addElement(ava);
1230            }
1231        }
1232
1233        if (reverse)
1234        {
1235            for (int i = components.size() - 1; i >= 0; i--)
1236            {
1237                if (first)
1238                {
1239                    first = false;
1240                }
1241                else
1242                {
1243                    buf.append(',');
1244                }
1245
1246                buf.append(components.elementAt(i).toString());
1247            }
1248        }
1249        else
1250        {
1251            for (int i = 0; i < components.size(); i++)
1252            {
1253                if (first)
1254                {
1255                    first = false;
1256                }
1257                else
1258                {
1259                    buf.append(',');
1260                }
1261
1262                buf.append(components.elementAt(i).toString());
1263            }
1264        }
1265
1266        return buf.toString();
1267    }
1268
1269    private String bytesToString(
1270        byte[] data)
1271    {
1272        char[]  cs = new char[data.length];
1273
1274        for (int i = 0; i != cs.length; i++)
1275        {
1276            cs[i] = (char)(data[i] & 0xff);
1277        }
1278
1279        return new String(cs);
1280    }
1281
1282    public String toString()
1283    {
1284        return toString(DefaultReverse, DefaultSymbols);
1285    }
1286}
1287