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