Home | History | Annotate | Download | only in x509
      1 package org.bouncycastle.asn1.x509;
      2 
      3 import java.io.IOException;
      4 import java.util.Enumeration;
      5 import java.util.Hashtable;
      6 import java.util.Vector;
      7 
      8 import org.bouncycastle.asn1.ASN1Encodable;
      9 import org.bouncycastle.asn1.ASN1EncodableVector;
     10 import org.bouncycastle.asn1.ASN1Encoding;
     11 import org.bouncycastle.asn1.ASN1Object;
     12 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
     13 import org.bouncycastle.asn1.ASN1Primitive;
     14 import org.bouncycastle.asn1.ASN1Sequence;
     15 import org.bouncycastle.asn1.ASN1Set;
     16 import org.bouncycastle.asn1.ASN1String;
     17 import org.bouncycastle.asn1.ASN1TaggedObject;
     18 import org.bouncycastle.asn1.DERSequence;
     19 import org.bouncycastle.asn1.DERSet;
     20 import org.bouncycastle.asn1.DERUniversalString;
     21 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
     22 import org.bouncycastle.asn1.x500.X500Name;
     23 import org.bouncycastle.util.Strings;
     24 import 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  */
     38 public 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