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: Use Boolean class constants instead of Boolean constructor
    259     private static final Boolean TRUE = Boolean.TRUE;
    260     private static final Boolean FALSE = Boolean.FALSE;
    261     // END Android-changed: Use Boolean class constants instead of Boolean constructor
    262 
    263     static
    264     {
    265         DefaultSymbols.put(C, "C");
    266         DefaultSymbols.put(O, "O");
    267         DefaultSymbols.put(T, "T");
    268         DefaultSymbols.put(OU, "OU");
    269         DefaultSymbols.put(CN, "CN");
    270         DefaultSymbols.put(L, "L");
    271         DefaultSymbols.put(ST, "ST");
    272         DefaultSymbols.put(SN, "SERIALNUMBER");
    273         DefaultSymbols.put(EmailAddress, "E");
    274         DefaultSymbols.put(DC, "DC");
    275         DefaultSymbols.put(UID, "UID");
    276         DefaultSymbols.put(STREET, "STREET");
    277         DefaultSymbols.put(SURNAME, "SURNAME");
    278         DefaultSymbols.put(GIVENNAME, "GIVENNAME");
    279         DefaultSymbols.put(INITIALS, "INITIALS");
    280         DefaultSymbols.put(GENERATION, "GENERATION");
    281         DefaultSymbols.put(UnstructuredAddress, "unstructuredAddress");
    282         DefaultSymbols.put(UnstructuredName, "unstructuredName");
    283         DefaultSymbols.put(UNIQUE_IDENTIFIER, "UniqueIdentifier");
    284         DefaultSymbols.put(DN_QUALIFIER, "DN");
    285         DefaultSymbols.put(PSEUDONYM, "Pseudonym");
    286         DefaultSymbols.put(POSTAL_ADDRESS, "PostalAddress");
    287         DefaultSymbols.put(NAME_AT_BIRTH, "NameAtBirth");
    288         DefaultSymbols.put(COUNTRY_OF_CITIZENSHIP, "CountryOfCitizenship");
    289         DefaultSymbols.put(COUNTRY_OF_RESIDENCE, "CountryOfResidence");
    290         DefaultSymbols.put(GENDER, "Gender");
    291         DefaultSymbols.put(PLACE_OF_BIRTH, "PlaceOfBirth");
    292         DefaultSymbols.put(DATE_OF_BIRTH, "DateOfBirth");
    293         DefaultSymbols.put(POSTAL_CODE, "PostalCode");
    294         DefaultSymbols.put(BUSINESS_CATEGORY, "BusinessCategory");
    295         DefaultSymbols.put(TELEPHONE_NUMBER, "TelephoneNumber");
    296         DefaultSymbols.put(NAME, "Name");
    297 
    298         RFC2253Symbols.put(C, "C");
    299         RFC2253Symbols.put(O, "O");
    300         RFC2253Symbols.put(OU, "OU");
    301         RFC2253Symbols.put(CN, "CN");
    302         RFC2253Symbols.put(L, "L");
    303         RFC2253Symbols.put(ST, "ST");
    304         RFC2253Symbols.put(STREET, "STREET");
    305         RFC2253Symbols.put(DC, "DC");
    306         RFC2253Symbols.put(UID, "UID");
    307 
    308         RFC1779Symbols.put(C, "C");
    309         RFC1779Symbols.put(O, "O");
    310         RFC1779Symbols.put(OU, "OU");
    311         RFC1779Symbols.put(CN, "CN");
    312         RFC1779Symbols.put(L, "L");
    313         RFC1779Symbols.put(ST, "ST");
    314         RFC1779Symbols.put(STREET, "STREET");
    315 
    316         DefaultLookUp.put("c", C);
    317         DefaultLookUp.put("o", O);
    318         DefaultLookUp.put("t", T);
    319         DefaultLookUp.put("ou", OU);
    320         DefaultLookUp.put("cn", CN);
    321         DefaultLookUp.put("l", L);
    322         DefaultLookUp.put("st", ST);
    323         DefaultLookUp.put("sn", SN);
    324         DefaultLookUp.put("serialnumber", SN);
    325         DefaultLookUp.put("street", STREET);
    326         DefaultLookUp.put("emailaddress", E);
    327         DefaultLookUp.put("dc", DC);
    328         DefaultLookUp.put("e", E);
    329         DefaultLookUp.put("uid", UID);
    330         DefaultLookUp.put("surname", SURNAME);
    331         DefaultLookUp.put("givenname", GIVENNAME);
    332         DefaultLookUp.put("initials", INITIALS);
    333         DefaultLookUp.put("generation", GENERATION);
    334         DefaultLookUp.put("unstructuredaddress", UnstructuredAddress);
    335         DefaultLookUp.put("unstructuredname", UnstructuredName);
    336         DefaultLookUp.put("uniqueidentifier", UNIQUE_IDENTIFIER);
    337         DefaultLookUp.put("dn", DN_QUALIFIER);
    338         DefaultLookUp.put("pseudonym", PSEUDONYM);
    339         DefaultLookUp.put("postaladdress", POSTAL_ADDRESS);
    340         DefaultLookUp.put("nameofbirth", NAME_AT_BIRTH);
    341         DefaultLookUp.put("countryofcitizenship", COUNTRY_OF_CITIZENSHIP);
    342         DefaultLookUp.put("countryofresidence", COUNTRY_OF_RESIDENCE);
    343         DefaultLookUp.put("gender", GENDER);
    344         DefaultLookUp.put("placeofbirth", PLACE_OF_BIRTH);
    345         DefaultLookUp.put("dateofbirth", DATE_OF_BIRTH);
    346         DefaultLookUp.put("postalcode", POSTAL_CODE);
    347         DefaultLookUp.put("businesscategory", BUSINESS_CATEGORY);
    348         DefaultLookUp.put("telephonenumber", TELEPHONE_NUMBER);
    349         DefaultLookUp.put("name", NAME);
    350     }
    351 
    352     private X509NameEntryConverter  converter = null;
    353     private Vector                  ordering = new Vector();
    354     private Vector                  values = new Vector();
    355     private Vector                  added = new Vector();
    356 
    357     private ASN1Sequence            seq;
    358 
    359     private boolean                 isHashCodeCalculated;
    360     private int                     hashCodeValue;
    361 
    362     /**
    363      * Return a X509Name based on the passed in tagged object.
    364      *
    365      * @param obj tag object holding name.
    366      * @param explicit true if explicitly tagged false otherwise.
    367      * @return the X509Name
    368      */
    369     public static X509Name getInstance(
    370         ASN1TaggedObject obj,
    371         boolean          explicit)
    372     {
    373         return getInstance(ASN1Sequence.getInstance(obj, explicit));
    374     }
    375 
    376     public static X509Name getInstance(
    377         Object  obj)
    378     {
    379         if (obj == null || obj instanceof X509Name)
    380         {
    381             return (X509Name)obj;
    382         }
    383         else if (obj instanceof X500Name)
    384         {
    385             return new X509Name(ASN1Sequence.getInstance(((X500Name)obj).toASN1Primitive()));
    386         }
    387         else if (obj != null)
    388         {
    389             return new X509Name(ASN1Sequence.getInstance(obj));
    390         }
    391 
    392         return null;
    393     }
    394 
    395     protected X509Name()
    396     {
    397         // constructure use by new X500 Name class
    398     }
    399     /**
    400      * Constructor from ASN1Sequence
    401      *
    402      * the principal will be a list of constructed sets, each containing an (OID, String) pair.
    403      * @deprecated use X500Name.getInstance()
    404      */
    405     public X509Name(
    406         ASN1Sequence  seq)
    407     {
    408         this.seq = seq;
    409 
    410         Enumeration e = seq.getObjects();
    411 
    412         while (e.hasMoreElements())
    413         {
    414             ASN1Set         set = ASN1Set.getInstance(((ASN1Encodable)e.nextElement()).toASN1Primitive());
    415 
    416             for (int i = 0; i < set.size(); i++)
    417             {
    418                    ASN1Sequence s = ASN1Sequence.getInstance(set.getObjectAt(i).toASN1Primitive());
    419 
    420                    if (s.size() != 2)
    421                    {
    422                        throw new IllegalArgumentException("badly sized pair");
    423                    }
    424 
    425                    ordering.addElement(ASN1ObjectIdentifier.getInstance(s.getObjectAt(0)));
    426 
    427                    ASN1Encodable value = s.getObjectAt(1);
    428                    if (value instanceof ASN1String && !(value instanceof DERUniversalString))
    429                    {
    430                        String v = ((ASN1String)value).getString();
    431                        if (v.length() > 0 && v.charAt(0) == '#')
    432                        {
    433                            values.addElement("\\" + v);
    434                        }
    435                        else
    436                        {
    437                            values.addElement(v);
    438                        }
    439                    }
    440                    else
    441                    {
    442                        try
    443                        {
    444                            values.addElement("#" + bytesToString(Hex.encode(value.toASN1Primitive().getEncoded(ASN1Encoding.DER))));
    445                        }
    446                        catch (IOException e1)
    447                        {
    448                            throw new IllegalArgumentException("cannot encode value");
    449                        }
    450                    }
    451                    added.addElement((i != 0) ? TRUE : FALSE);  // to allow earlier JDK compatibility
    452             }
    453         }
    454     }
    455 
    456     /**
    457      * constructor from a table of attributes.
    458      * <p>
    459      * it's is assumed the table contains OID/String pairs, and the contents
    460      * of the table are copied into an internal table as part of the
    461      * construction process.
    462      * <p>
    463      * <b>Note:</b> if the name you are trying to generate should be
    464      * following a specific ordering, you should use the constructor
    465      * with the ordering specified below.
    466      * @deprecated use an ordered constructor! The hashtable ordering is rarely correct
    467      */
    468     public X509Name(
    469         Hashtable  attributes)
    470     {
    471         this(null, attributes);
    472     }
    473 
    474     /**
    475      * Constructor from a table of attributes with ordering.
    476      * <p>
    477      * it's is assumed the table contains OID/String pairs, and the contents
    478      * of the table are copied into an internal table as part of the
    479      * construction process. The ordering vector should contain the OIDs
    480      * in the order they are meant to be encoded or printed in toString.
    481      */
    482     public X509Name(
    483         Vector      ordering,
    484         Hashtable   attributes)
    485     {
    486         this(ordering, attributes, new X509DefaultEntryConverter());
    487     }
    488 
    489     /**
    490      * Constructor from a table of attributes with ordering.
    491      * <p>
    492      * it's is assumed the table contains OID/String pairs, and the contents
    493      * of the table are copied into an internal table as part of the
    494      * construction process. The ordering vector should contain the OIDs
    495      * in the order they are meant to be encoded or printed in toString.
    496      * <p>
    497      * The passed in converter will be used to convert the strings into their
    498      * ASN.1 counterparts.
    499      * @deprecated use X500Name, X500NameBuilder
    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      * @deprecated use X500Name, X500NameBuilder
    543      */
    544     public X509Name(
    545         Vector  oids,
    546         Vector  values)
    547     {
    548         this(oids, values, new X509DefaultEntryConverter());
    549     }
    550 
    551     /**
    552      * Takes two vectors one of the oids and the other of the values.
    553      * <p>
    554      * The passed in converter will be used to convert the strings into their
    555      * ASN.1 counterparts.
    556      * @deprecated use X500Name, X500NameBuilder
    557      */
    558     public X509Name(
    559         Vector                  oids,
    560         Vector                  values,
    561         X509NameEntryConverter  converter)
    562     {
    563         this.converter = converter;
    564 
    565         if (oids.size() != values.size())
    566         {
    567             throw new IllegalArgumentException("oids vector must be same length as values.");
    568         }
    569 
    570         for (int i = 0; i < oids.size(); i++)
    571         {
    572             this.ordering.addElement(oids.elementAt(i));
    573             this.values.addElement(values.elementAt(i));
    574             this.added.addElement(FALSE);
    575         }
    576     }
    577 
    578 //    private Boolean isEncoded(String s)
    579 //    {
    580 //        if (s.charAt(0) == '#')
    581 //        {
    582 //            return TRUE;
    583 //        }
    584 //
    585 //        return FALSE;
    586 //    }
    587 
    588     /**
    589      * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
    590      * some such, converting it into an ordered set of name attributes.
    591      * @deprecated use X500Name, X500NameBuilder
    592      */
    593     public X509Name(
    594         String  dirName)
    595     {
    596         this(DefaultReverse, DefaultLookUp, dirName);
    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 with each
    602      * string value being converted to its associated ASN.1 type using the passed
    603      * in converter.
    604      * @deprecated use X500Name, X500NameBuilder
    605      */
    606     public X509Name(
    607         String                  dirName,
    608         X509NameEntryConverter  converter)
    609     {
    610         this(DefaultReverse, DefaultLookUp, dirName, converter);
    611     }
    612 
    613     /**
    614      * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
    615      * some such, converting it into an ordered set of name attributes. If reverse
    616      * is true, create the encoded version of the sequence starting from the
    617      * last element in the string.
    618      * @deprecated use X500Name, X500NameBuilder
    619      */
    620     public X509Name(
    621         boolean reverse,
    622         String  dirName)
    623     {
    624         this(reverse, DefaultLookUp, dirName);
    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 with each
    630      * string value being converted to its associated ASN.1 type using the passed
    631      * in converter. If reverse is true the ASN.1 sequence representing the DN will
    632      * be built by starting at the end of the string, rather than the start.
    633      * @deprecated use X500Name, X500NameBuilder
    634      */
    635     public X509Name(
    636         boolean                 reverse,
    637         String                  dirName,
    638         X509NameEntryConverter  converter)
    639     {
    640         this(reverse, DefaultLookUp, dirName, converter);
    641     }
    642 
    643     /**
    644      * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
    645      * some such, converting it into an ordered set of name attributes. lookUp
    646      * should provide a table of lookups, indexed by lowercase only strings and
    647      * yielding a ASN1ObjectIdentifier, other than that OID. and numeric oids
    648      * will be processed automatically.
    649      * <br>
    650      * If reverse is true, create the encoded version of the sequence
    651      * starting from the last element in the string.
    652      * @param reverse true if we should start scanning from the end (RFC 2553).
    653      * @param lookUp table of names and their oids.
    654      * @param dirName the X.500 string to be parsed.
    655      * @deprecated use X500Name, X500NameBuilder
    656      */
    657     public X509Name(
    658         boolean     reverse,
    659         Hashtable   lookUp,
    660         String      dirName)
    661     {
    662         this(reverse, lookUp, dirName, new X509DefaultEntryConverter());
    663     }
    664 
    665     private ASN1ObjectIdentifier decodeOID(
    666         String      name,
    667         Hashtable   lookUp)
    668     {
    669         name = name.trim();
    670         if (Strings.toUpperCase(name).startsWith("OID."))
    671         {
    672             return new ASN1ObjectIdentifier(name.substring(4));
    673         }
    674         else if (name.charAt(0) >= '0' && name.charAt(0) <= '9')
    675         {
    676             return new ASN1ObjectIdentifier(name);
    677         }
    678 
    679         ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)lookUp.get(Strings.toLowerCase(name));
    680         if (oid == null)
    681         {
    682             throw new IllegalArgumentException("Unknown object id - " + name + " - passed to distinguished name");
    683         }
    684 
    685         return oid;
    686     }
    687 
    688     private String unescape(String elt)
    689     {
    690         if (elt.length() == 0 || (elt.indexOf('\\') < 0 && elt.indexOf('"') < 0))
    691         {
    692             return elt.trim();
    693         }
    694 
    695         char[] elts = elt.toCharArray();
    696         boolean escaped = false;
    697         boolean quoted = false;
    698         StringBuffer buf = new StringBuffer(elt.length());
    699         int start = 0;
    700 
    701         // if it's an escaped hash string and not an actual encoding in string form
    702         // we need to leave it escaped.
    703         if (elts[0] == '\\')
    704         {
    705             if (elts[1] == '#')
    706             {
    707                 start = 2;
    708                 buf.append("\\#");
    709             }
    710         }
    711 
    712         boolean nonWhiteSpaceEncountered = false;
    713         int     lastEscaped = 0;
    714 
    715         for (int i = start; i != elts.length; i++)
    716         {
    717             char c = elts[i];
    718 
    719             if (c != ' ')
    720             {
    721                 nonWhiteSpaceEncountered = true;
    722             }
    723 
    724             if (c == '"')
    725             {
    726                 if (!escaped)
    727                 {
    728                     quoted = !quoted;
    729                 }
    730                 else
    731                 {
    732                     buf.append(c);
    733                 }
    734                 escaped = false;
    735             }
    736             else if (c == '\\' && !(escaped || quoted))
    737             {
    738                 escaped = true;
    739                 lastEscaped = buf.length();
    740             }
    741             else
    742             {
    743                 if (c == ' ' && !escaped && !nonWhiteSpaceEncountered)
    744                 {
    745                     continue;
    746                 }
    747                 buf.append(c);
    748                 escaped = false;
    749             }
    750         }
    751 
    752         if (buf.length() > 0)
    753         {
    754             while (buf.charAt(buf.length() - 1) == ' ' && lastEscaped != (buf.length() - 1))
    755             {
    756                 buf.setLength(buf.length() - 1);
    757             }
    758         }
    759 
    760         return buf.toString();
    761     }
    762 
    763     /**
    764      * Takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
    765      * some such, converting it into an ordered set of name attributes. lookUp
    766      * should provide a table of lookups, indexed by lowercase only strings and
    767      * yielding a ASN1ObjectIdentifier, other than that OID. and numeric oids
    768      * will be processed automatically. The passed in converter is used to convert the
    769      * string values to the right of each equals sign to their ASN.1 counterparts.
    770      * <br>
    771      * @param reverse true if we should start scanning from the end, false otherwise.
    772      * @param lookUp table of names and oids.
    773      * @param dirName the string dirName
    774      * @param converter the converter to convert string values into their ASN.1 equivalents
    775      */
    776     public X509Name(
    777         boolean                 reverse,
    778         Hashtable               lookUp,
    779         String                  dirName,
    780         X509NameEntryConverter  converter)
    781     {
    782         this.converter = converter;
    783         X509NameTokenizer   nTok = new X509NameTokenizer(dirName);
    784 
    785         while (nTok.hasMoreTokens())
    786         {
    787             String  token = nTok.nextToken();
    788 
    789             if (token.indexOf('+') > 0)
    790             {
    791                 X509NameTokenizer   pTok = new X509NameTokenizer(token, '+');
    792 
    793                 addEntry(lookUp, pTok.nextToken(), FALSE);
    794 
    795                 while (pTok.hasMoreTokens())
    796                 {
    797                     addEntry(lookUp, pTok.nextToken(), TRUE);
    798                 }
    799             }
    800             else
    801             {
    802                 addEntry(lookUp, token, FALSE);
    803             }
    804         }
    805 
    806         if (reverse)
    807         {
    808             Vector  o = new Vector();
    809             Vector  v = new Vector();
    810             Vector  a = new Vector();
    811 
    812             int count = 1;
    813 
    814             for (int i = 0; i < this.ordering.size(); i++)
    815             {
    816                 if (((Boolean)this.added.elementAt(i)).booleanValue())
    817                 {
    818                     o.insertElementAt(this.ordering.elementAt(i), count);
    819                     v.insertElementAt(this.values.elementAt(i), count);
    820                     a.insertElementAt(this.added.elementAt(i), count);
    821                     count++;
    822                 }
    823                 else
    824                 {
    825                     o.insertElementAt(this.ordering.elementAt(i), 0);
    826                     v.insertElementAt(this.values.elementAt(i), 0);
    827                     a.insertElementAt(this.added.elementAt(i), 0);
    828                     count = 1;
    829                 }
    830             }
    831 
    832             this.ordering = o;
    833             this.values = v;
    834             this.added = a;
    835         }
    836     }
    837 
    838     private void addEntry(Hashtable lookUp, String token, Boolean isAdded)
    839     {
    840         X509NameTokenizer vTok;
    841         String name;
    842         String value;ASN1ObjectIdentifier oid;
    843         vTok = new X509NameTokenizer(token, '=');
    844 
    845         name = vTok.nextToken();
    846 
    847         if (!vTok.hasMoreTokens())
    848         {
    849            throw new IllegalArgumentException("badly formatted directory string");
    850         }
    851 
    852         value = vTok.nextToken();
    853 
    854         oid = decodeOID(name, lookUp);
    855 
    856         this.ordering.addElement(oid);
    857         this.values.addElement(unescape(value));
    858         this.added.addElement(isAdded);
    859     }
    860 
    861     /**
    862      * return a vector of the oids in the name, in the order they were found.
    863      */
    864     public Vector getOIDs()
    865     {
    866         Vector  v = new Vector();
    867 
    868         for (int i = 0; i != ordering.size(); i++)
    869         {
    870             v.addElement(ordering.elementAt(i));
    871         }
    872 
    873         return v;
    874     }
    875 
    876     /**
    877      * return a vector of the values found in the name, in the order they
    878      * were found.
    879      */
    880     public Vector getValues()
    881     {
    882         Vector  v = new Vector();
    883 
    884         for (int i = 0; i != values.size(); i++)
    885         {
    886             v.addElement(values.elementAt(i));
    887         }
    888 
    889         return v;
    890     }
    891 
    892     /**
    893      * return a vector of the values found in the name, in the order they
    894      * were found, with the DN label corresponding to passed in oid.
    895      */
    896     public Vector getValues(
    897         ASN1ObjectIdentifier oid)
    898     {
    899         Vector  v = new Vector();
    900 
    901         for (int i = 0; i != values.size(); i++)
    902         {
    903             if (ordering.elementAt(i).equals(oid))
    904             {
    905                 String val = (String)values.elementAt(i);
    906 
    907                 if (val.length() > 2 && val.charAt(0) == '\\' && val.charAt(1) == '#')
    908                 {
    909                     v.addElement(val.substring(1));
    910                 }
    911                 else
    912                 {
    913                     v.addElement(val);
    914                 }
    915             }
    916         }
    917 
    918         return v;
    919     }
    920 
    921     public ASN1Primitive toASN1Primitive()
    922     {
    923         if (seq == null)
    924         {
    925             ASN1EncodableVector  vec = new ASN1EncodableVector();
    926             ASN1EncodableVector  sVec = new ASN1EncodableVector();
    927             ASN1ObjectIdentifier  lstOid = null;
    928 
    929             for (int i = 0; i != ordering.size(); i++)
    930             {
    931                 ASN1EncodableVector     v = new ASN1EncodableVector();
    932                 ASN1ObjectIdentifier     oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
    933 
    934                 v.add(oid);
    935 
    936                 String  str = (String)values.elementAt(i);
    937 
    938                 v.add(converter.getConvertedValue(oid, str));
    939 
    940                 if (lstOid == null
    941                     || ((Boolean)this.added.elementAt(i)).booleanValue())
    942                 {
    943                     sVec.add(new DERSequence(v));
    944                 }
    945                 else
    946                 {
    947                     vec.add(new DERSet(sVec));
    948                     sVec = new ASN1EncodableVector();
    949 
    950                     sVec.add(new DERSequence(v));
    951                 }
    952 
    953                 lstOid = oid;
    954             }
    955 
    956             vec.add(new DERSet(sVec));
    957 
    958             seq = new DERSequence(vec);
    959         }
    960 
    961         return seq;
    962     }
    963 
    964     /**
    965      * @param inOrder if true the order of both X509 names must be the same,
    966      * as well as the values associated with each element.
    967      */
    968     public boolean equals(Object obj, boolean inOrder)
    969     {
    970         if (!inOrder)
    971         {
    972             return this.equals(obj);
    973         }
    974 
    975         if (obj == this)
    976         {
    977             return true;
    978         }
    979 
    980         if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
    981         {
    982             return false;
    983         }
    984 
    985         ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
    986 
    987         if (this.toASN1Primitive().equals(derO))
    988         {
    989             return true;
    990         }
    991 
    992         X509Name other;
    993 
    994         try
    995         {
    996             other = X509Name.getInstance(obj);
    997         }
    998         catch (IllegalArgumentException e)
    999         {
   1000             return false;
   1001         }
   1002 
   1003         int      orderingSize = ordering.size();
   1004 
   1005         if (orderingSize != other.ordering.size())
   1006         {
   1007             return false;
   1008         }
   1009 
   1010         for (int i = 0; i < orderingSize; i++)
   1011         {
   1012             ASN1ObjectIdentifier  oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
   1013             ASN1ObjectIdentifier  oOid = (ASN1ObjectIdentifier)other.ordering.elementAt(i);
   1014 
   1015             if (oid.equals(oOid))
   1016             {
   1017                 String value = (String)values.elementAt(i);
   1018                 String oValue = (String)other.values.elementAt(i);
   1019 
   1020                 if (!equivalentStrings(value, oValue))
   1021                 {
   1022                     return false;
   1023                 }
   1024             }
   1025             else
   1026             {
   1027                 return false;
   1028             }
   1029         }
   1030 
   1031         return true;
   1032     }
   1033 
   1034     public int hashCode()
   1035     {
   1036         if (isHashCodeCalculated)
   1037         {
   1038             return hashCodeValue;
   1039         }
   1040 
   1041         isHashCodeCalculated = true;
   1042 
   1043         // this needs to be order independent, like equals
   1044         for (int i = 0; i != ordering.size(); i += 1)
   1045         {
   1046             String value = (String)values.elementAt(i);
   1047 
   1048             value = canonicalize(value);
   1049             value = stripInternalSpaces(value);
   1050 
   1051             hashCodeValue ^= ordering.elementAt(i).hashCode();
   1052             hashCodeValue ^= value.hashCode();
   1053         }
   1054 
   1055         return hashCodeValue;
   1056     }
   1057 
   1058     /**
   1059      * test for equality - note: case is ignored.
   1060      */
   1061     public boolean equals(Object obj)
   1062     {
   1063         if (obj == this)
   1064         {
   1065             return true;
   1066         }
   1067 
   1068         if (!(obj instanceof X509Name || obj instanceof ASN1Sequence))
   1069         {
   1070             return false;
   1071         }
   1072 
   1073         ASN1Primitive derO = ((ASN1Encodable)obj).toASN1Primitive();
   1074 
   1075         if (this.toASN1Primitive().equals(derO))
   1076         {
   1077             return true;
   1078         }
   1079 
   1080         X509Name other;
   1081 
   1082         try
   1083         {
   1084             other = X509Name.getInstance(obj);
   1085         }
   1086         catch (IllegalArgumentException e)
   1087         {
   1088             return false;
   1089         }
   1090 
   1091         int      orderingSize = ordering.size();
   1092 
   1093         if (orderingSize != other.ordering.size())
   1094         {
   1095             return false;
   1096         }
   1097 
   1098         boolean[] indexes = new boolean[orderingSize];
   1099         int       start, end, delta;
   1100 
   1101         if (ordering.elementAt(0).equals(other.ordering.elementAt(0)))   // guess forward
   1102         {
   1103             start = 0;
   1104             end = orderingSize;
   1105             delta = 1;
   1106         }
   1107         else  // guess reversed - most common problem
   1108         {
   1109             start = orderingSize - 1;
   1110             end = -1;
   1111             delta = -1;
   1112         }
   1113 
   1114         for (int i = start; i != end; i += delta)
   1115         {
   1116             boolean              found = false;
   1117             ASN1ObjectIdentifier  oid = (ASN1ObjectIdentifier)ordering.elementAt(i);
   1118             String               value = (String)values.elementAt(i);
   1119 
   1120             for (int j = 0; j < orderingSize; j++)
   1121             {
   1122                 if (indexes[j])
   1123                 {
   1124                     continue;
   1125                 }
   1126 
   1127                 ASN1ObjectIdentifier oOid = (ASN1ObjectIdentifier)other.ordering.elementAt(j);
   1128 
   1129                 if (oid.equals(oOid))
   1130                 {
   1131                     String oValue = (String)other.values.elementAt(j);
   1132 
   1133                     if (equivalentStrings(value, oValue))
   1134                     {
   1135                         indexes[j] = true;
   1136                         found      = true;
   1137                         break;
   1138                     }
   1139                 }
   1140             }
   1141 
   1142             if (!found)
   1143             {
   1144                 return false;
   1145             }
   1146         }
   1147 
   1148         return true;
   1149     }
   1150 
   1151     private boolean equivalentStrings(String s1, String s2)
   1152     {
   1153         String value = canonicalize(s1);
   1154         String oValue = canonicalize(s2);
   1155 
   1156         if (!value.equals(oValue))
   1157         {
   1158             value = stripInternalSpaces(value);
   1159             oValue = stripInternalSpaces(oValue);
   1160 
   1161             if (!value.equals(oValue))
   1162             {
   1163                 return false;
   1164             }
   1165         }
   1166 
   1167         return true;
   1168     }
   1169 
   1170     private String canonicalize(String s)
   1171     {
   1172         String value = Strings.toLowerCase(s.trim());
   1173 
   1174         if (value.length() > 0 && value.charAt(0) == '#')
   1175         {
   1176             ASN1Primitive obj = decodeObject(value);
   1177 
   1178             if (obj instanceof ASN1String)
   1179             {
   1180                 value = Strings.toLowerCase(((ASN1String)obj).getString().trim());
   1181             }
   1182         }
   1183 
   1184         return value;
   1185     }
   1186 
   1187     private ASN1Primitive decodeObject(String oValue)
   1188     {
   1189         try
   1190         {
   1191             return ASN1Primitive.fromByteArray(Hex.decode(oValue.substring(1)));
   1192         }
   1193         catch (IOException e)
   1194         {
   1195             throw new IllegalStateException("unknown encoding in name: " + e);
   1196         }
   1197     }
   1198 
   1199     private String stripInternalSpaces(
   1200         String str)
   1201     {
   1202         StringBuffer res = new StringBuffer();
   1203 
   1204         if (str.length() != 0)
   1205         {
   1206             char    c1 = str.charAt(0);
   1207 
   1208             res.append(c1);
   1209 
   1210             for (int k = 1; k < str.length(); k++)
   1211             {
   1212                 char    c2 = str.charAt(k);
   1213                 if (!(c1 == ' ' && c2 == ' '))
   1214                 {
   1215                     res.append(c2);
   1216                 }
   1217                 c1 = c2;
   1218             }
   1219         }
   1220 
   1221         return res.toString();
   1222     }
   1223 
   1224     private void appendValue(
   1225         StringBuffer        buf,
   1226         Hashtable           oidSymbols,
   1227         ASN1ObjectIdentifier oid,
   1228         String              value)
   1229     {
   1230         String  sym = (String)oidSymbols.get(oid);
   1231 
   1232         if (sym != null)
   1233         {
   1234             buf.append(sym);
   1235         }
   1236         else
   1237         {
   1238             buf.append(oid.getId());
   1239         }
   1240 
   1241         buf.append('=');
   1242 
   1243         int start = buf.length();
   1244         buf.append(value);
   1245         int end = buf.length();
   1246 
   1247         if (value.length() >= 2 && value.charAt(0) == '\\' && value.charAt(1) == '#')
   1248         {
   1249             start += 2;
   1250         }
   1251 
   1252         while (start < end && buf.charAt(start) == ' ')
   1253         {
   1254             buf.insert(start, "\\");
   1255             start += 2;
   1256             ++end;
   1257         }
   1258 
   1259         while (--end > start && buf.charAt(end) == ' ')
   1260         {
   1261             buf.insert(end, '\\');
   1262         }
   1263 
   1264         while (start <= end)
   1265         {
   1266             switch (buf.charAt(start))
   1267             {
   1268             case ',':
   1269             case '"':
   1270             case '\\':
   1271             case '+':
   1272             case '=':
   1273             case '<':
   1274             case '>':
   1275             case ';':
   1276                 buf.insert(start, "\\");
   1277                 start += 2;
   1278                 ++end;
   1279                 break;
   1280             default:
   1281                 ++start;
   1282                 break;
   1283             }
   1284         }
   1285     }
   1286 
   1287     /**
   1288      * convert the structure to a string - if reverse is true the
   1289      * oids and values are listed out starting with the last element
   1290      * in the sequence (ala RFC 2253), otherwise the string will begin
   1291      * with the first element of the structure. If no string definition
   1292      * for the oid is found in oidSymbols the string value of the oid is
   1293      * added. Two standard symbol tables are provided DefaultSymbols, and
   1294      * RFC2253Symbols as part of this class.
   1295      *
   1296      * @param reverse if true start at the end of the sequence and work back.
   1297      * @param oidSymbols look up table strings for oids.
   1298      */
   1299     public String toString(
   1300         boolean     reverse,
   1301         Hashtable   oidSymbols)
   1302     {
   1303         StringBuffer            buf = new StringBuffer();
   1304         Vector                  components = new Vector();
   1305         boolean                 first = true;
   1306 
   1307         StringBuffer ava = null;
   1308 
   1309         for (int i = 0; i < ordering.size(); i++)
   1310         {
   1311             if (((Boolean)added.elementAt(i)).booleanValue())
   1312             {
   1313                 ava.append('+');
   1314                 appendValue(ava, oidSymbols,
   1315                     (ASN1ObjectIdentifier)ordering.elementAt(i),
   1316                     (String)values.elementAt(i));
   1317             }
   1318             else
   1319             {
   1320                 ava = new StringBuffer();
   1321                 appendValue(ava, oidSymbols,
   1322                     (ASN1ObjectIdentifier)ordering.elementAt(i),
   1323                     (String)values.elementAt(i));
   1324                 components.addElement(ava);
   1325             }
   1326         }
   1327 
   1328         if (reverse)
   1329         {
   1330             for (int i = components.size() - 1; i >= 0; i--)
   1331             {
   1332                 if (first)
   1333                 {
   1334                     first = false;
   1335                 }
   1336                 else
   1337                 {
   1338                     buf.append(',');
   1339                 }
   1340 
   1341                 buf.append(components.elementAt(i).toString());
   1342             }
   1343         }
   1344         else
   1345         {
   1346             for (int i = 0; i < components.size(); i++)
   1347             {
   1348                 if (first)
   1349                 {
   1350                     first = false;
   1351                 }
   1352                 else
   1353                 {
   1354                     buf.append(',');
   1355                 }
   1356 
   1357                 buf.append(components.elementAt(i).toString());
   1358             }
   1359         }
   1360 
   1361         return buf.toString();
   1362     }
   1363 
   1364     private String bytesToString(
   1365         byte[] data)
   1366     {
   1367         char[]  cs = new char[data.length];
   1368 
   1369         for (int i = 0; i != cs.length; i++)
   1370         {
   1371             cs[i] = (char)(data[i] & 0xff);
   1372         }
   1373 
   1374         return new String(cs);
   1375     }
   1376 
   1377     public String toString()
   1378     {
   1379         return toString(DefaultReverse, DefaultSymbols);
   1380     }
   1381 }
   1382