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.ASN1Object;
     11 import org.bouncycastle.asn1.ASN1Sequence;
     12 import org.bouncycastle.asn1.ASN1Set;
     13 import org.bouncycastle.asn1.ASN1TaggedObject;
     14 import org.bouncycastle.asn1.DEREncodable;
     15 import org.bouncycastle.asn1.DERObject;
     16 import org.bouncycastle.asn1.DERObjectIdentifier;
     17 import org.bouncycastle.asn1.DERSequence;
     18 import org.bouncycastle.asn1.DERSet;
     19 import org.bouncycastle.asn1.DERString;
     20 import org.bouncycastle.asn1.DERUniversalString;
     21 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
     22 import org.bouncycastle.util.Strings;
     23 import org.bouncycastle.util.encoders.Hex;
     24 
     25 /**
     26  * <pre>
     27  *     RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
     28  *
     29  *     RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
     30  *
     31  *     AttributeTypeAndValue ::= SEQUENCE {
     32  *                                   type  OBJECT IDENTIFIER,
     33  *                                   value ANY }
     34  * </pre>
     35  */
     36 public class X509Name
     37     extends ASN1Encodable
     38 {
     39     /**
     40      * country code - StringType(SIZE(2))
     41      */
     42     public static final DERObjectIdentifier C = new DERObjectIdentifier("2.5.4.6");
     43 
     44     /**
     45      * organization - StringType(SIZE(1..64))
     46      */
     47     public static final DERObjectIdentifier O = new DERObjectIdentifier("2.5.4.10");
     48 
     49     /**
     50      * organizational unit name - StringType(SIZE(1..64))
     51      */
     52     public static final DERObjectIdentifier OU = new DERObjectIdentifier("2.5.4.11");
     53 
     54     /**
     55      * Title
     56      */
     57     public static final DERObjectIdentifier T = new DERObjectIdentifier("2.5.4.12");
     58 
     59     /**
     60      * common name - StringType(SIZE(1..64))
     61      */
     62     public static final DERObjectIdentifier CN = new DERObjectIdentifier("2.5.4.3");
     63 
     64     /**
     65      * device serial number name - StringType(SIZE(1..64))
     66      */
     67     public static final DERObjectIdentifier SN = new DERObjectIdentifier("2.5.4.5");
     68 
     69     /**
     70      * street - StringType(SIZE(1..64))
     71      */
     72     public static final DERObjectIdentifier STREET = new DERObjectIdentifier("2.5.4.9");
     73 
     74     /**
     75      * device serial number name - StringType(SIZE(1..64))
     76      */
     77     public static final DERObjectIdentifier SERIALNUMBER = SN;
     78 
     79     /**
     80      * locality name - StringType(SIZE(1..64))
     81      */
     82     public static final DERObjectIdentifier L = new DERObjectIdentifier("2.5.4.7");
     83 
     84     /**
     85      * state, or province name - StringType(SIZE(1..64))
     86      */
     87     public static final DERObjectIdentifier ST = new DERObjectIdentifier("2.5.4.8");
     88 
     89     /**
     90      * Naming attributes of type X520name
     91      */
     92     public static final DERObjectIdentifier SURNAME = new DERObjectIdentifier("2.5.4.4");
     93     public static final DERObjectIdentifier GIVENNAME = new DERObjectIdentifier("2.5.4.42");
     94     public static final DERObjectIdentifier INITIALS = new DERObjectIdentifier("2.5.4.43");
     95     public static final DERObjectIdentifier GENERATION = new DERObjectIdentifier("2.5.4.44");
     96     public static final DERObjectIdentifier UNIQUE_IDENTIFIER = new DERObjectIdentifier("2.5.4.45");
     97 
     98     /**
     99      * businessCategory - DirectoryString(SIZE(1..128)
    100      */
    101     public static final DERObjectIdentifier BUSINESS_CATEGORY = new DERObjectIdentifier(
    102                     "2.5.4.15");
    103 
    104     /**
    105      * postalCode - DirectoryString(SIZE(1..40)
    106      */
    107     public static final DERObjectIdentifier POSTAL_CODE = new DERObjectIdentifier(
    108                     "2.5.4.17");
    109 
    110     /**
    111      * dnQualifier - DirectoryString(SIZE(1..64)
    112      */
    113     public static final DERObjectIdentifier DN_QUALIFIER = new DERObjectIdentifier(
    114                     "2.5.4.46");
    115 
    116     /**
    117      * RFC 3039 Pseudonym - DirectoryString(SIZE(1..64)
    118      */
    119     public static final DERObjectIdentifier PSEUDONYM = new DERObjectIdentifier(
    120                     "2.5.4.65");
    121 
    122 
    123     /**
    124      * RFC 3039 DateOfBirth - GeneralizedTime - YYYYMMDD000000Z
    125      */
    126     public static final DERObjectIdentifier DATE_OF_BIRTH = new DERObjectIdentifier(
    127                     "1.3.6.1.5.5.7.9.1");
    128 
    129     /**
    130      * RFC 3039 PlaceOfBirth - DirectoryString(SIZE(1..128)
    131      */
    132     public static final DERObjectIdentifier PLACE_OF_BIRTH = new DERObjectIdentifier(
    133                     "1.3.6.1.5.5.7.9.2");
    134 
    135     /**
    136      * RFC 3039 Gender - PrintableString (SIZE(1)) -- "M", "F", "m" or "f"
    137      */
    138     public static final DERObjectIdentifier GENDER = new DERObjectIdentifier(
    139                     "1.3.6.1.5.5.7.9.3");
    140 
    141     /**
    142      * RFC 3039 CountryOfCitizenship - PrintableString (SIZE (2)) -- ISO 3166
    143      * codes only
    144      */
    145     public static final DERObjectIdentifier COUNTRY_OF_CITIZENSHIP = new DERObjectIdentifier(
    146                     "1.3.6.1.5.5.7.9.4");
    147 
    148     /**
    149      * RFC 3039 CountryOfResidence - PrintableString (SIZE (2)) -- ISO 3166
    150      * codes only
    151      */
    152     public static final DERObjectIdentifier COUNTRY_OF_RESIDENCE = new DERObjectIdentifier(
    153                     "1.3.6.1.5.5.7.9.5");
    154 
    155 
    156     /**
    157      * ISIS-MTT NameAtBirth - DirectoryString(SIZE(1..64)
    158      */
    159     public static final DERObjectIdentifier NAME_AT_BIRTH =  new DERObjectIdentifier("1.3.36.8.3.14");
    160 
    161     /**
    162      * RFC 3039 PostalAddress - SEQUENCE SIZE (1..6) OF
    163      * DirectoryString(SIZE(1..30))
    164      */
    165     public static final DERObjectIdentifier POSTAL_ADDRESS = new DERObjectIdentifier("2.5.4.16");
    166 
    167     /**
    168      * RFC 2256 dmdName
    169      */
    170     public static final DERObjectIdentifier DMD_NAME = new DERObjectIdentifier("2.5.4.54");
    171 
    172     /**
    173      * id-at-telephoneNumber
    174      */
    175     public static final DERObjectIdentifier TELEPHONE_NUMBER = X509ObjectIdentifiers.id_at_telephoneNumber;
    176 
    177     /**
    178      * id-at-name
    179      */
    180     public static final DERObjectIdentifier NAME = X509ObjectIdentifiers.id_at_name;
    181 
    182     /**
    183      * Email address (RSA PKCS#9 extension) - IA5String.
    184      * <p>Note: if you're trying to be ultra orthodox, don't use this! It shouldn't be in here.
    185      */
    186     public static final DERObjectIdentifier EmailAddress = PKCSObjectIdentifiers.pkcs_9_at_emailAddress;
    187 
    188     /**
    189      * more from PKCS#9
    190      */
    191     public static final DERObjectIdentifier UnstructuredName = PKCSObjectIdentifiers.pkcs_9_at_unstructuredName;
    192     public static final DERObjectIdentifier UnstructuredAddress = PKCSObjectIdentifiers.pkcs_9_at_unstructuredAddress;
    193 
    194     /**
    195      * email address in Verisign certificates
    196      */
    197     public static final DERObjectIdentifier E = EmailAddress;
    198 
    199     /*
    200      * others...
    201      */
    202     public static final DERObjectIdentifier DC = new DERObjectIdentifier("0.9.2342.19200300.100.1.25");
    203 
    204     /**
    205      * LDAP User id.
    206      */
    207     public static final DERObjectIdentifier UID = new DERObjectIdentifier("0.9.2342.19200300.100.1.1");
    208 
    209     /**
    210      * determines whether or not strings should be processed and printed
    211      * from back to front.
    212      */
    213     public static boolean DefaultReverse = false;
    214 
    215     /**
    216      * default look up table translating OID values into their common symbols following
    217      * the convention in RFC 2253 with a few extras
    218      */
    219     public static final Hashtable DefaultSymbols = new Hashtable();
    220 
    221     /**
    222      * look up table translating OID values into their common symbols following the convention in RFC 2253
    223      *
    224      */
    225     public static final Hashtable RFC2253Symbols = new Hashtable();
    226 
    227     /**
    228      * look up table translating OID values into their common symbols following the convention in RFC 1779
    229      *
    230      */
    231     public static final Hashtable RFC1779Symbols = new Hashtable();
    232 
    233     /**
    234      * look up table translating common symbols into their OIDS.
    235      */
    236     public static final Hashtable DefaultLookUp = new Hashtable();
    237 
    238     /**
    239      * look up table translating OID values into their common symbols
    240      * @deprecated use DefaultSymbols
    241      */
    242     public static final Hashtable OIDLookUp = DefaultSymbols;
    243 
    244     /**
    245      * look up table translating string values into their OIDS -
    246      * @deprecated use DefaultLookUp
    247      */
    248     public static final Hashtable SymbolLookUp = DefaultLookUp;
    249 
    250     // BEGIN android-removed
    251     //private static final Boolean TRUE = new Boolean(true); // for J2ME compatibility
    252     //private static final Boolean FALSE = new Boolean(false);
    253     // END android-removed
    254 
    255     static
    256     {
    257         DefaultSymbols.put(C, "C");
    258         DefaultSymbols.put(O, "O");
    259         DefaultSymbols.put(T, "T");
    260         DefaultSymbols.put(OU, "OU");
    261         DefaultSymbols.put(CN, "CN");
    262         DefaultSymbols.put(L, "L");
    263         DefaultSymbols.put(ST, "ST");
    264         DefaultSymbols.put(SN, "SERIALNUMBER");
    265         DefaultSymbols.put(EmailAddress, "E");
    266         DefaultSymbols.put(DC, "DC");
    267         DefaultSymbols.put(UID, "UID");
    268         DefaultSymbols.put(STREET, "STREET");
    269         DefaultSymbols.put(SURNAME, "SURNAME");
    270         DefaultSymbols.put(GIVENNAME, "GIVENNAME");
    271         DefaultSymbols.put(INITIALS, "INITIALS");
    272         DefaultSymbols.put(GENERATION, "GENERATION");
    273         DefaultSymbols.put(UnstructuredAddress, "unstructuredAddress");
    274         DefaultSymbols.put(UnstructuredName, "unstructuredName");
    275         DefaultSymbols.put(UNIQUE_IDENTIFIER, "UniqueIdentifier");
    276         DefaultSymbols.put(DN_QUALIFIER, "DN");
    277         DefaultSymbols.put(PSEUDONYM, "Pseudonym");
    278         DefaultSymbols.put(POSTAL_ADDRESS, "PostalAddress");
    279         DefaultSymbols.put(NAME_AT_BIRTH, "NameAtBirth");
    280         DefaultSymbols.put(COUNTRY_OF_CITIZENSHIP, "CountryOfCitizenship");
    281         DefaultSymbols.put(COUNTRY_OF_RESIDENCE, "CountryOfResidence");
    282         DefaultSymbols.put(GENDER, "Gender");
    283         DefaultSymbols.put(PLACE_OF_BIRTH, "PlaceOfBirth");
    284         DefaultSymbols.put(DATE_OF_BIRTH, "DateOfBirth");
    285         DefaultSymbols.put(POSTAL_CODE, "PostalCode");
    286         DefaultSymbols.put(BUSINESS_CATEGORY, "BusinessCategory");
    287         DefaultSymbols.put(TELEPHONE_NUMBER, "TelephoneNumber");
    288         DefaultSymbols.put(NAME, "Name");
    289 
    290         RFC2253Symbols.put(C, "C");
    291         RFC2253Symbols.put(O, "O");
    292         RFC2253Symbols.put(OU, "OU");
    293         RFC2253Symbols.put(CN, "CN");
    294         RFC2253Symbols.put(L, "L");
    295         RFC2253Symbols.put(ST, "ST");
    296         RFC2253Symbols.put(STREET, "STREET");
    297         RFC2253Symbols.put(DC, "DC");
    298         RFC2253Symbols.put(UID, "UID");
    299 
    300         RFC1779Symbols.put(C, "C");
    301         RFC1779Symbols.put(O, "O");
    302         RFC1779Symbols.put(OU, "OU");
    303         RFC1779Symbols.put(CN, "CN");
    304         RFC1779Symbols.put(L, "L");
    305         RFC1779Symbols.put(ST, "ST");
    306         RFC1779Symbols.put(STREET, "STREET");
    307 
    308         DefaultLookUp.put("c", C);
    309         DefaultLookUp.put("o", O);
    310         DefaultLookUp.put("t", T);
    311         DefaultLookUp.put("ou", OU);
    312         DefaultLookUp.put("cn", CN);
    313         DefaultLookUp.put("l", L);
    314         DefaultLookUp.put("st", ST);
    315         DefaultLookUp.put("sn", SN);
    316         DefaultLookUp.put("serialnumber", SN);
    317         DefaultLookUp.put("street", STREET);
    318         DefaultLookUp.put("emailaddress", E);
    319         DefaultLookUp.put("dc", DC);
    320         DefaultLookUp.put("e", E);
    321         DefaultLookUp.put("uid", UID);
    322         DefaultLookUp.put("surname", SURNAME);
    323         DefaultLookUp.put("givenname", GIVENNAME);
    324         DefaultLookUp.put("initials", INITIALS);
    325         DefaultLookUp.put("generation", GENERATION);
    326         DefaultLookUp.put("unstructuredaddress", UnstructuredAddress);
    327         DefaultLookUp.put("unstructuredname", UnstructuredName);
    328         DefaultLookUp.put("uniqueidentifier", UNIQUE_IDENTIFIER);
    329         DefaultLookUp.put("dn", DN_QUALIFIER);
    330         DefaultLookUp.put("pseudonym", PSEUDONYM);
    331         DefaultLookUp.put("postaladdress", POSTAL_ADDRESS);
    332         DefaultLookUp.put("nameofbirth", NAME_AT_BIRTH);
    333         DefaultLookUp.put("countryofcitizenship", COUNTRY_OF_CITIZENSHIP);
    334         DefaultLookUp.put("countryofresidence", COUNTRY_OF_RESIDENCE);
    335         DefaultLookUp.put("gender", GENDER);
    336         DefaultLookUp.put("placeofbirth", PLACE_OF_BIRTH);
    337         DefaultLookUp.put("dateofbirth", DATE_OF_BIRTH);
    338         DefaultLookUp.put("postalcode", POSTAL_CODE);
    339         DefaultLookUp.put("businesscategory", BUSINESS_CATEGORY);
    340         DefaultLookUp.put("telephonenumber", TELEPHONE_NUMBER);
    341         DefaultLookUp.put("name", NAME);
    342     }
    343 
    344     private X509NameEntryConverter  converter = null;
    345     // BEGIN android-changed
    346     private X509NameElementList     elems = new X509NameElementList();
    347     // END android-changed
    348 
    349     private ASN1Sequence            seq;
    350 
    351     private boolean                 isHashCodeCalculated;
    352     private int                     hashCodeValue;
    353 
    354     /**
    355      * Return a X509Name based on the passed in tagged object.
    356      *
    357      * @param obj tag object holding name.
    358      * @param explicit true if explicitly tagged false otherwise.
    359      * @return the X509Name
    360      */
    361     public static X509Name getInstance(
    362         ASN1TaggedObject obj,
    363         boolean          explicit)
    364     {
    365         return getInstance(ASN1Sequence.getInstance(obj, explicit));
    366     }
    367 
    368     public static X509Name getInstance(
    369         Object  obj)
    370     {
    371         if (obj == null || obj instanceof X509Name)
    372         {
    373             return (X509Name)obj;
    374         }
    375         else if (obj instanceof ASN1Sequence)
    376         {
    377             return new X509Name((ASN1Sequence)obj);
    378         }
    379 
    380         throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
    381     }
    382 
    383     /**
    384      * Constructor from ASN1Sequence
    385      *
    386      * the principal will be a list of constructed sets, each containing an (OID, String) pair.
    387      */
    388     public X509Name(
    389         ASN1Sequence  seq)
    390     {
    391         this.seq = seq;
    392 
    393         Enumeration e = seq.getObjects();
    394 
    395         while (e.hasMoreElements())
    396         {
    397             ASN1Set         set = ASN1Set.getInstance(e.nextElement());
    398 
    399             for (int i = 0; i < set.size(); i++)
    400             {
    401                    ASN1Sequence s = ASN1Sequence.getInstance(set.getObjectAt(i));
    402 
    403                    if (s.size() != 2)
    404                    {
    405                        throw new IllegalArgumentException("badly sized pair");
    406                    }
    407 
    408                    // BEGIN android-changed
    409                    DERObjectIdentifier key = DERObjectIdentifier.getInstance(s.getObjectAt(0));
    410 
    411                    DEREncodable value = s.getObjectAt(1);
    412                    String valueStr;
    413                    if (value instanceof DERString && !(value instanceof DERUniversalString))
    414                    {
    415                        String v = ((DERString)value).getString();
    416                        if (v.length() > 0 && v.charAt(0) == '#')
    417                        {
    418                            valueStr = "\\" + v;
    419                        }
    420                        else
    421                        {
    422                            valueStr = v;
    423                        }
    424                    }
    425                    else
    426                    {
    427                        valueStr = "#" + bytesToString(Hex.encode(value.getDERObject().getDEREncoded()));
    428                    }
    429                    boolean added = (i != 0);  // to allow earlier JDK compatibility
    430                    elems.add(key, valueStr, added);
    431                    // END android-changed
    432             }
    433         }
    434     }
    435 
    436     /**
    437      * constructor from a table of attributes.
    438      * <p>
    439      * it's is assumed the table contains OID/String pairs, and the contents
    440      * of the table are copied into an internal table as part of the
    441      * construction process.
    442      * <p>
    443      * <b>Note:</b> if the name you are trying to generate should be
    444      * following a specific ordering, you should use the constructor
    445      * with the ordering specified below.
    446      * @deprecated use an ordered constructor! The hashtable ordering is rarely correct
    447      */
    448     public X509Name(
    449         Hashtable  attributes)
    450     {
    451         this(null, attributes);
    452     }
    453 
    454     /**
    455      * Constructor from a table of attributes with ordering.
    456      * <p>
    457      * it's is assumed the table contains OID/String pairs, and the contents
    458      * of the table are copied into an internal table as part of the
    459      * construction process. The ordering vector should contain the OIDs
    460      * in the order they are meant to be encoded or printed in toString.
    461      */
    462     public X509Name(
    463         Vector      ordering,
    464         Hashtable   attributes)
    465     {
    466         this(ordering, attributes, new X509DefaultEntryConverter());
    467     }
    468 
    469     /**
    470      * Constructor from a table of attributes with ordering.
    471      * <p>
    472      * it's is assumed the table contains OID/String pairs, and the contents
    473      * of the table are copied into an internal table as part of the
    474      * construction process. The ordering vector should contain the OIDs
    475      * in the order they are meant to be encoded or printed in toString.
    476      * <p>
    477      * The passed in converter will be used to convert the strings into their
    478      * ASN.1 counterparts.
    479      */
    480     public X509Name(
    481         Vector                   ordering,
    482         Hashtable                attributes,
    483         X509NameEntryConverter   converter)
    484     {
    485         // BEGIN android-changed
    486         DERObjectIdentifier problem = null;
    487         this.converter = converter;
    488 
    489         if (ordering != null)
    490         {
    491             for (int i = 0; i != ordering.size(); i++)
    492             {
    493                 DERObjectIdentifier key =
    494                     (DERObjectIdentifier) ordering.elementAt(i);
    495                 String value = (String) attributes.get(key);
    496                 if (value == null)
    497                 {
    498                     problem = key;
    499                     break;
    500                 }
    501                 elems.add(key, value);
    502             }
    503         }
    504         else
    505         {
    506             Enumeration     e = attributes.keys();
    507 
    508             while (e.hasMoreElements())
    509             {
    510                 DERObjectIdentifier key =
    511                     (DERObjectIdentifier) e.nextElement();
    512                 String value = (String) attributes.get(key);
    513                 if (value == null)
    514                 {
    515                     problem = key;
    516                     break;
    517                 }
    518                 elems.add(key, value);
    519             }
    520         }
    521 
    522         if (problem != null)
    523         {
    524             throw new IllegalArgumentException("No attribute for object id - " + problem.getId() + " - passed to distinguished name");
    525         }
    526         // END android-changed
    527     }
    528 
    529     /**
    530      * Takes two vectors one of the oids and the other of the values.
    531      */
    532     public X509Name(
    533         Vector  oids,
    534         Vector  values)
    535     {
    536         this(oids, values, new X509DefaultEntryConverter());
    537     }
    538 
    539     /**
    540      * Takes two vectors one of the oids and the other of the values.
    541      * <p>
    542      * The passed in converter will be used to convert the strings into their
    543      * ASN.1 counterparts.
    544      */
    545     public X509Name(
    546         Vector                  oids,
    547         Vector                  values,
    548         X509NameEntryConverter  converter)
    549     {
    550         this.converter = converter;
    551 
    552         if (oids.size() != values.size())
    553         {
    554             throw new IllegalArgumentException("oids vector must be same length as values.");
    555         }
    556 
    557         for (int i = 0; i < oids.size(); i++)
    558         {
    559             // BEGIN android-changed
    560             elems.add((DERObjectIdentifier) oids.elementAt(i),
    561                     (String) values.elementAt(i));
    562             // END android-changed
    563         }
    564     }
    565 
    566 //    private Boolean isEncoded(String s)
    567 //    {
    568 //        if (s.charAt(0) == '#')
    569 //        {
    570 //            return TRUE;
    571 //        }
    572 //
    573 //        return FALSE;
    574 //    }
    575 
    576     /**
    577      * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
    578      * some such, converting it into an ordered set of name attributes.
    579      */
    580     public X509Name(
    581         String  dirName)
    582     {
    583         this(DefaultReverse, DefaultLookUp, dirName);
    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 with each
    589      * string value being converted to its associated ASN.1 type using the passed
    590      * in converter.
    591      */
    592     public X509Name(
    593         String                  dirName,
    594         X509NameEntryConverter  converter)
    595     {
    596         this(DefaultReverse, DefaultLookUp, dirName, converter);
    597     }
    598 
    599     /**
    600      * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
    601      * some such, converting it into an ordered set of name attributes. If reverse
    602      * is true, create the encoded version of the sequence starting from the
    603      * last element in the string.
    604      */
    605     public X509Name(
    606         boolean reverse,
    607         String  dirName)
    608     {
    609         this(reverse, DefaultLookUp, dirName);
    610     }
    611 
    612     /**
    613      * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
    614      * some such, converting it into an ordered set of name attributes with each
    615      * string value being converted to its associated ASN.1 type using the passed
    616      * in converter. If reverse is true the ASN.1 sequence representing the DN will
    617      * be built by starting at the end of the string, rather than the start.
    618      */
    619     public X509Name(
    620         boolean                 reverse,
    621         String                  dirName,
    622         X509NameEntryConverter  converter)
    623     {
    624         this(reverse, DefaultLookUp, dirName, converter);
    625     }
    626 
    627     /**
    628      * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
    629      * some such, converting it into an ordered set of name attributes. lookUp
    630      * should provide a table of lookups, indexed by lowercase only strings and
    631      * yielding a DERObjectIdentifier, other than that OID. and numeric oids
    632      * will be processed automatically.
    633      * <br>
    634      * If reverse is true, create the encoded version of the sequence
    635      * starting from the last element in the string.
    636      * @param reverse true if we should start scanning from the end (RFC 2553).
    637      * @param lookUp table of names and their oids.
    638      * @param dirName the X.500 string to be parsed.
    639      */
    640     public X509Name(
    641         boolean     reverse,
    642         Hashtable   lookUp,
    643         String      dirName)
    644     {
    645         this(reverse, lookUp, dirName, new X509DefaultEntryConverter());
    646     }
    647 
    648     private DERObjectIdentifier decodeOID(
    649         String      name,
    650         Hashtable   lookUp)
    651     {
    652         if (Strings.toUpperCase(name).startsWith("OID."))
    653         {
    654             return new DERObjectIdentifier(name.substring(4));
    655         }
    656         else if (name.charAt(0) >= '0' && name.charAt(0) <= '9')
    657         {
    658             return new DERObjectIdentifier(name);
    659         }
    660 
    661         DERObjectIdentifier oid = (DERObjectIdentifier)lookUp.get(Strings.toLowerCase(name));
    662         if (oid == null)
    663         {
    664             throw new IllegalArgumentException("Unknown object id - " + name + " - passed to distinguished name");
    665         }
    666 
    667         return oid;
    668     }
    669 
    670     /**
    671      * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
    672      * some such, converting it into an ordered set of name attributes. lookUp
    673      * should provide a table of lookups, indexed by lowercase only strings and
    674      * yielding a DERObjectIdentifier, other than that OID. and numeric oids
    675      * will be processed automatically. The passed in converter is used to convert the
    676      * string values to the right of each equals sign to their ASN.1 counterparts.
    677      * <br>
    678      * @param reverse true if we should start scanning from the end, false otherwise.
    679      * @param lookUp table of names and oids.
    680      * @param dirName the string dirName
    681      * @param converter the converter to convert string values into their ASN.1 equivalents
    682      */
    683     public X509Name(
    684         boolean                 reverse,
    685         Hashtable               lookUp,
    686         String                  dirName,
    687         X509NameEntryConverter  converter)
    688     {
    689         this.converter = converter;
    690         X509NameTokenizer   nTok = new X509NameTokenizer(dirName);
    691 
    692         while (nTok.hasMoreTokens())
    693         {
    694             String  token = nTok.nextToken();
    695             int     index = token.indexOf('=');
    696 
    697             if (index == -1)
    698             {
    699                 throw new IllegalArgumentException("badly formatted directory string");
    700             }
    701 
    702             String              name = token.substring(0, index);
    703             String              value = token.substring(index + 1);
    704             DERObjectIdentifier oid = decodeOID(name, lookUp);
    705 
    706             if (value.indexOf('+') > 0)
    707             {
    708                 X509NameTokenizer   vTok = new X509NameTokenizer(value, '+');
    709                 String  v = vTok.nextToken();
    710 
    711                 // BEGIN android-changed
    712                 this.elems.add(oid, v);
    713                 // END android-changed
    714 
    715                 while (vTok.hasMoreTokens())
    716                 {
    717                     String  sv = vTok.nextToken();
    718                     int     ndx = sv.indexOf('=');
    719 
    720                     String  nm = sv.substring(0, ndx);
    721                     String  vl = sv.substring(ndx + 1);
    722                     // BEGIN android-changed
    723                     this.elems.add(decodeOID(nm, lookUp), vl, true);
    724                     // END android-changed
    725                 }
    726             }
    727             else
    728             {
    729                 // BEGIN android-changed
    730                 this.elems.add(oid, value);
    731                 // END android-changed
    732             }
    733         }
    734 
    735         if (reverse)
    736         {
    737             // BEGIN android-changed
    738             this.elems = this.elems.reverse();
    739             // END android-changed
    740         }
    741     }
    742 
    743     /**
    744      * return a vector of the oids in the name, in the order they were found.
    745      */
    746     public Vector getOIDs()
    747     {
    748         // BEGIN android-changed
    749         Vector  v = new Vector();
    750         int     size = elems.size();
    751 
    752         for (int i = 0; i < size; i++)
    753         {
    754             v.addElement(elems.getKey(i));
    755         }
    756 
    757         return v;
    758         // END android-changed
    759     }
    760 
    761     /**
    762      * return a vector of the values found in the name, in the order they
    763      * were found.
    764      */
    765     public Vector getValues()
    766     {
    767         Vector  v = new Vector();
    768         // BEGIN android-changed
    769         int     size = elems.size();
    770 
    771         for (int i = 0; i != size; i++)
    772         {
    773             v.addElement(elems.getValue(i));
    774         }
    775         // END android-changed
    776 
    777         return v;
    778     }
    779 
    780     /**
    781      * return a vector of the values found in the name, in the order they
    782      * were found, with the DN label corresponding to passed in oid.
    783      */
    784     public Vector getValues(
    785         DERObjectIdentifier oid)
    786     {
    787         Vector  v = new Vector();
    788         int     size = elems.size();
    789         // BEGIN android-changed
    790 
    791         for (int i = 0; i != size; i++)
    792         {
    793             if (elems.getKey(i).equals(oid))
    794             {
    795                 String val = elems.getValue(i);
    796 
    797                 if (val.length() > 2 && val.charAt(0) == '\\' && val.charAt(1) == '#')
    798                 {
    799                     v.addElement(val.substring(1));
    800                 }
    801                 else
    802                 {
    803                     v.addElement(val);
    804                 }
    805             }
    806         }
    807         // END android-changed
    808 
    809         return v;
    810     }
    811 
    812     public DERObject toASN1Object()
    813     {
    814         if (seq == null)
    815         {
    816             ASN1EncodableVector  vec = new ASN1EncodableVector();
    817             ASN1EncodableVector  sVec = new ASN1EncodableVector();
    818             DERObjectIdentifier  lstOid = null;
    819             // BEGIN android-changed
    820             int                  size = elems.size();
    821 
    822             for (int i = 0; i != size; i++)
    823             {
    824                 ASN1EncodableVector     v = new ASN1EncodableVector();
    825                 DERObjectIdentifier     oid = elems.getKey(i);
    826 
    827                 v.add(oid);
    828 
    829                 String  str = elems.getValue(i);
    830 
    831                 v.add(converter.getConvertedValue(oid, str));
    832 
    833                 if (lstOid == null
    834                     || this.elems.getAdded(i))
    835                 // END android-changed
    836                 {
    837                     sVec.add(new DERSequence(v));
    838                 }
    839                 else
    840                 {
    841                     vec.add(new DERSet(sVec));
    842                     sVec = new ASN1EncodableVector();
    843 
    844                     sVec.add(new DERSequence(v));
    845                 }
    846 
    847                 lstOid = oid;
    848             }
    849 
    850             vec.add(new DERSet(sVec));
    851 
    852             seq = new DERSequence(vec);
    853             // END android-changed
    854         }
    855 
    856         return seq;
    857     }
    858 
    859     /**
    860      * @param inOrder if true the order of both X509 names must be the same,
    861      * as well as the values associated with each element.
    862      */
    863     public boolean equals(Object obj, boolean inOrder)
    864     {
    865         if (!inOrder)
    866         {
    867             return this.equals(obj);
    868         }
    869 
    870         if (obj == this)
    871         {
    872             return true;
    873         }
    874 
    875         if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
    876         {
    877             return false;
    878         }
    879 
    880         DERObject derO = ((DEREncodable)obj).getDERObject();
    881 
    882         if (this.getDERObject().equals(derO))
    883         {
    884             return true;
    885         }
    886 
    887         X509Name other;
    888 
    889         try
    890         {
    891             other = X509Name.getInstance(obj);
    892         }
    893         catch (IllegalArgumentException e)
    894         {
    895             return false;
    896         }
    897 
    898         // BEGIN android-changed
    899         int      orderingSize = elems.size();
    900 
    901         if (orderingSize != other.elems.size())
    902         // END android-changed
    903         {
    904             return false;
    905         }
    906 
    907         for (int i = 0; i < orderingSize; i++)
    908         {
    909             // BEGIN android-changed
    910             DERObjectIdentifier  oid = elems.getKey(i);
    911             DERObjectIdentifier  oOid = other.elems.getKey(i);
    912             // END android-changed
    913 
    914             if (oid.equals(oOid))
    915             {
    916                 // BEGIN android-changed
    917                 String value = elems.getValue(i);
    918                 String oValue = other.elems.getValue(i);
    919                 // END android-changed
    920 
    921                 if (!equivalentStrings(value, oValue))
    922                 {
    923                     return false;
    924                 }
    925             }
    926             else
    927             {
    928                 return false;
    929             }
    930         }
    931 
    932         return true;
    933     }
    934 
    935     public int hashCode()
    936     {
    937         if (isHashCodeCalculated)
    938         {
    939             return hashCodeValue;
    940         }
    941 
    942         isHashCodeCalculated = true;
    943 
    944         // this needs to be order independent, like equals
    945         for (int i = 0; i != elems.size(); i += 1)
    946         {
    947             String value = (String)elems.getValue(i);
    948 
    949             value = canonicalize(value);
    950             value = stripInternalSpaces(value);
    951 
    952             hashCodeValue ^= value.hashCode();
    953         }
    954 
    955         return hashCodeValue;
    956     }
    957 
    958     /**
    959      * test for equality - note: case is ignored.
    960      */
    961     public boolean equals(Object obj)
    962     {
    963         if (obj == this)
    964         {
    965             return true;
    966         }
    967 
    968         if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
    969         {
    970             return false;
    971         }
    972 
    973         DERObject derO = ((DEREncodable)obj).getDERObject();
    974 
    975         if (this.getDERObject().equals(derO))
    976         {
    977             return true;
    978         }
    979 
    980         X509Name other;
    981 
    982         try
    983         {
    984             other = X509Name.getInstance(obj);
    985         }
    986         catch (IllegalArgumentException e)
    987         {
    988             return false;
    989         }
    990 
    991         // BEGIN android-changed
    992         int      orderingSize = elems.size();
    993 
    994         if (orderingSize != other.elems.size())
    995             // END android-changed
    996         {
    997             return false;
    998         }
    999 
   1000         boolean[] indexes = new boolean[orderingSize];
   1001         int       start, end, delta;
   1002 
   1003         // BEGIN android-changed
   1004         if (elems.getKey(0).equals(other.elems.getKey(0)))   // guess forward
   1005         // END android-changed
   1006         {
   1007             start = 0;
   1008             end = orderingSize;
   1009             delta = 1;
   1010         }
   1011         else  // guess reversed - most common problem
   1012         {
   1013             start = orderingSize - 1;
   1014             end = -1;
   1015             delta = -1;
   1016         }
   1017 
   1018         for (int i = start; i != end; i += delta)
   1019         {
   1020             boolean              found = false;
   1021             // BEGIN android-changed
   1022             DERObjectIdentifier  oid = elems.getKey(i);
   1023             String               value = elems.getValue(i);
   1024             // END android-changed
   1025 
   1026             for (int j = 0; j < orderingSize; j++)
   1027             {
   1028                 if (indexes[j])
   1029                 {
   1030                     continue;
   1031                 }
   1032 
   1033                 // BEGIN android-changed
   1034                 DERObjectIdentifier oOid = other.elems.getKey(j);
   1035                 // END android-changed
   1036 
   1037                 if (oid.equals(oOid))
   1038                 {
   1039                     // BEGIN android-changed
   1040                     String oValue = other.elems.getValue(j);
   1041                     // END android-changed
   1042 
   1043                     if (equivalentStrings(value, oValue))
   1044                     {
   1045                         indexes[j] = true;
   1046                         found      = true;
   1047                         break;
   1048                     }
   1049                 }
   1050             }
   1051 
   1052             if (!found)
   1053             {
   1054                 return false;
   1055             }
   1056         }
   1057 
   1058         return true;
   1059     }
   1060 
   1061     private boolean equivalentStrings(String s1, String s2)
   1062     {
   1063         String value = canonicalize(s1);
   1064         String oValue = canonicalize(s2);
   1065 
   1066         if (!value.equals(oValue))
   1067         {
   1068             value = stripInternalSpaces(value);
   1069             oValue = stripInternalSpaces(oValue);
   1070 
   1071             if (!value.equals(oValue))
   1072             {
   1073                 return false;
   1074             }
   1075         }
   1076 
   1077         return true;
   1078     }
   1079 
   1080     private String canonicalize(String s)
   1081     {
   1082         String value = Strings.toLowerCase(s.trim());
   1083 
   1084         if (value.length() > 0 && value.charAt(0) == '#')
   1085         {
   1086             DERObject obj = decodeObject(value);
   1087 
   1088             if (obj instanceof DERString)
   1089             {
   1090                 value = Strings.toLowerCase(((DERString)obj).getString().trim());
   1091             }
   1092         }
   1093 
   1094         return value;
   1095     }
   1096 
   1097     private ASN1Object decodeObject(String oValue)
   1098     {
   1099         try
   1100         {
   1101             return ASN1Object.fromByteArray(Hex.decode(oValue.substring(1)));
   1102         }
   1103         catch (IOException e)
   1104         {
   1105             throw new IllegalStateException("unknown encoding in name: " + e);
   1106         }
   1107     }
   1108 
   1109     private String stripInternalSpaces(
   1110         String str)
   1111     {
   1112         StringBuffer res = new StringBuffer();
   1113 
   1114         if (str.length() != 0)
   1115         {
   1116             char    c1 = str.charAt(0);
   1117 
   1118             res.append(c1);
   1119 
   1120             for (int k = 1; k < str.length(); k++)
   1121             {
   1122                 char    c2 = str.charAt(k);
   1123                 if (!(c1 == ' ' && c2 == ' '))
   1124                 {
   1125                     res.append(c2);
   1126                 }
   1127                 c1 = c2;
   1128             }
   1129         }
   1130 
   1131         return res.toString();
   1132     }
   1133 
   1134     private void appendValue(
   1135         StringBuffer        buf,
   1136         Hashtable           oidSymbols,
   1137         DERObjectIdentifier oid,
   1138         String              value)
   1139     {
   1140         String  sym = (String)oidSymbols.get(oid);
   1141 
   1142         if (sym != null)
   1143         {
   1144             buf.append(sym);
   1145         }
   1146         else
   1147         {
   1148             buf.append(oid.getId());
   1149         }
   1150 
   1151         buf.append('=');
   1152 
   1153         int     index = buf.length();
   1154 
   1155         buf.append(value);
   1156 
   1157         int     end = buf.length();
   1158 
   1159         if (value.length() >= 2 && value.charAt(0) == '\\' && value.charAt(1) == '#')
   1160         {
   1161             index += 2;
   1162         }
   1163 
   1164         while (index != end)
   1165         {
   1166             if ((buf.charAt(index) == ',')
   1167                || (buf.charAt(index) == '"')
   1168                || (buf.charAt(index) == '\\')
   1169                || (buf.charAt(index) == '+')
   1170                || (buf.charAt(index) == '=')
   1171                || (buf.charAt(index) == '<')
   1172                || (buf.charAt(index) == '>')
   1173                || (buf.charAt(index) == ';'))
   1174             {
   1175                 buf.insert(index, "\\");
   1176                 index++;
   1177                 end++;
   1178             }
   1179 
   1180             index++;
   1181         }
   1182     }
   1183 
   1184     /**
   1185      * convert the structure to a string - if reverse is true the
   1186      * oids and values are listed out starting with the last element
   1187      * in the sequence (ala RFC 2253), otherwise the string will begin
   1188      * with the first element of the structure. If no string definition
   1189      * for the oid is found in oidSymbols the string value of the oid is
   1190      * added. Two standard symbol tables are provided DefaultSymbols, and
   1191      * RFC2253Symbols as part of this class.
   1192      *
   1193      * @param reverse if true start at the end of the sequence and work back.
   1194      * @param oidSymbols look up table strings for oids.
   1195      */
   1196     public String toString(
   1197         boolean     reverse,
   1198         Hashtable   oidSymbols)
   1199     {
   1200         StringBuffer            buf = new StringBuffer();
   1201         Vector                  components = new Vector();
   1202         boolean                 first = true;
   1203 
   1204         StringBuffer ava = null;
   1205 
   1206         // BEGIN android-changed
   1207         for (int i = 0; i < elems.size(); i++)
   1208         // END android-changed
   1209         {
   1210             if (elems.getAdded(i))
   1211             {
   1212                 ava.append('+');
   1213                 appendValue(ava, oidSymbols,
   1214                     // BEGIN android-changed
   1215                     elems.getKey(i),
   1216                     elems.getValue(i));
   1217                     // END android-changed
   1218             }
   1219             else
   1220             {
   1221                 ava = new StringBuffer();
   1222                 appendValue(ava, oidSymbols,
   1223                     // BEGIN android-changed
   1224                     elems.getKey(i),
   1225                     elems.getValue(i));
   1226                     // END android-changed
   1227                 components.addElement(ava);
   1228             }
   1229         }
   1230 
   1231         if (reverse)
   1232         {
   1233             // BEGIN android-changed
   1234             for (int i = elems.size() - 1; i >= 0; i--)
   1235             // END android-changed
   1236             {
   1237                 if (first)
   1238                 {
   1239                     first = false;
   1240                 }
   1241                 else
   1242                 {
   1243                     buf.append(',');
   1244                 }
   1245 
   1246                 buf.append(components.elementAt(i).toString());
   1247             }
   1248         }
   1249         else
   1250         {
   1251             for (int i = 0; i < components.size(); i++)
   1252             {
   1253                 if (first)
   1254                 {
   1255                     first = false;
   1256                 }
   1257                 else
   1258                 {
   1259                     buf.append(',');
   1260                 }
   1261 
   1262                 buf.append(components.elementAt(i).toString());
   1263             }
   1264         }
   1265 
   1266         return buf.toString();
   1267     }
   1268 
   1269     private String bytesToString(
   1270         byte[] data)
   1271     {
   1272         char[]  cs = new char[data.length];
   1273 
   1274         for (int i = 0; i != cs.length; i++)
   1275         {
   1276             cs[i] = (char)(data[i] & 0xff);
   1277         }
   1278 
   1279         return new String(cs);
   1280     }
   1281 
   1282     public String toString()
   1283     {
   1284         return toString(DefaultReverse, DefaultSymbols);
   1285     }
   1286 }
   1287