2003/02/12 06:21:04
[org.ibex.core.git] / src / org / bouncycastle / asn1 / x509 / X509Name.java
1 package org.bouncycastle.asn1.x509;
2
3 import java.io.*;
4 import java.util.*;
5
6 import org.bouncycastle.asn1.*;
7
8 public class X509Name
9     implements DEREncodable
10 {
11     /**
12      * country code - StringType(SIZE(2))
13      */
14     public static final DERObjectIdentifier C = new DERObjectIdentifier("2.5.4.6");
15
16     /**
17      * organization - StringType(SIZE(1..64))
18      */
19     public static final DERObjectIdentifier O = new DERObjectIdentifier("2.5.4.10");
20
21     /**
22      * organizational unit name - StringType(SIZE(1..64))
23      */
24     public static final DERObjectIdentifier OU = new DERObjectIdentifier("2.5.4.11");
25
26     /**
27      * Title
28      */
29     public static final DERObjectIdentifier T = new DERObjectIdentifier("2.5.4.12");
30
31     /**
32      * common name - StringType(SIZE(1..64))
33      */
34     public static final DERObjectIdentifier CN = new DERObjectIdentifier("2.5.4.3");
35
36     /**
37      * device serial number name - StringType(SIZE(1..64))
38      */
39     public static final DERObjectIdentifier SN = new DERObjectIdentifier("2.5.4.5");
40
41     /**
42      * locality name - StringType(SIZE(1..64))
43      */
44     public static final DERObjectIdentifier L = new DERObjectIdentifier("2.5.4.7");
45
46     /**
47      * state, or province name - StringType(SIZE(1..64))
48      */
49     public static final DERObjectIdentifier ST = new DERObjectIdentifier("2.5.4.8");
50
51
52     /**
53      * email address (RSA PKCS#9 extension) - IA5String
54      * <p>
55      * note: if you're trying to be ultra orthodox, don't use this! It shouldn't be in here.
56      */
57     public static final DERObjectIdentifier EmailAddress = new DERObjectIdentifier("1.2.840.113549.1.9.1");
58         
59         /**
60          * email address in Verisign certificates
61          */
62         public static final DERObjectIdentifier E = EmailAddress;
63         
64     /*
65      * others...
66      */
67     public static final DERObjectIdentifier DC = new DERObjectIdentifier("0.9.2342.19200300.100.1.25");
68
69     /**
70      * LDAP User id.
71      */
72     public static final DERObjectIdentifier UID = new DERObjectIdentifier("0.9.2342.19200300.100.1.1");
73
74     /**
75      * look up table translating OID values into their common symbols.
76      */
77     public static Hashtable OIDLookUp = new Hashtable();
78
79     /**
80      * look up table translating common symbols into their OIDS.
81      */
82     public static Hashtable SymbolLookUp = new Hashtable();
83
84     static
85     {
86         OIDLookUp.put(C, "C");
87         OIDLookUp.put(O, "O");
88         OIDLookUp.put(T, "T");
89         OIDLookUp.put(OU, "OU");
90         OIDLookUp.put(CN, "CN");
91         OIDLookUp.put(L, "L");
92         OIDLookUp.put(ST, "ST");
93         OIDLookUp.put(SN, "SN");
94         OIDLookUp.put(EmailAddress, "E");
95         OIDLookUp.put(DC, "DC");
96         OIDLookUp.put(UID, "UID");
97
98         SymbolLookUp.put("c", C);
99         SymbolLookUp.put("o", O);
100         SymbolLookUp.put("t", T);
101         SymbolLookUp.put("ou", OU);
102         SymbolLookUp.put("cn", CN);
103         SymbolLookUp.put("l", L);
104         SymbolLookUp.put("st", ST);
105         SymbolLookUp.put("sn", SN);
106         SymbolLookUp.put("emailaddress", E);
107         SymbolLookUp.put("dc", DC);
108         SymbolLookUp.put("e", E);
109         SymbolLookUp.put("uid", UID);
110     }
111
112     private Vector                  ordering = new Vector();
113     private Vector                  values = new Vector();
114     private ASN1Sequence            seq;
115
116     public static X509Name getInstance(
117         ASN1TaggedObject obj,
118         boolean          explicit)
119     {
120         return getInstance(ASN1Sequence.getInstance(obj, explicit));
121     }
122
123     public static X509Name getInstance(
124         Object  obj)
125     {
126         if (obj == null || obj instanceof X509Name)
127         {
128             return (X509Name)obj;
129         }
130         else if (obj instanceof ASN1Sequence)
131         {
132             return new X509Name((ASN1Sequence)obj);
133         }
134
135         throw new IllegalArgumentException("unknown object in factory");
136     }
137
138     /**
139      * Constructor from ASN1Sequence
140      *
141      * the principal will be a list of constructed sets, each containing an (OID, String) pair.
142      */
143     public X509Name(
144         ASN1Sequence  seq)
145     {
146         this.seq = seq;
147
148         Enumeration e = seq.getObjects();
149
150         while (e.hasMoreElements())
151         {
152             ASN1Set         set = (ASN1Set)e.nextElement();
153             ASN1Sequence    s = (ASN1Sequence)set.getObjectAt(0);
154
155             ordering.addElement(s.getObjectAt(0));
156             values.addElement(((DERString)s.getObjectAt(1)).getString());
157         }
158     }
159
160     /**
161      * constructor from a table of attributes.
162      * <p>
163      * it's is assumed the table contains OID/String pairs, and the contents
164      * of the table are copied into an internal table as part of the 
165      * construction process.
166      * <p>
167      * <b>Note:</b> if the name you are trying to generate should be
168      * following a specific ordering, you should use the constructor
169      * with the ordering specified below.
170      */
171     public X509Name(
172         Hashtable  attributes)
173     {
174         this(null, attributes);
175     }
176
177     /**
178      * constructor from a table of attributes with ordering.
179      * <p>
180      * it's is assumed the table contains OID/String pairs, and the contents
181      * of the table are copied into an internal table as part of the 
182      * construction process. The ordering vector should contain the OIDs
183      * in the order they are meant to be encoded or printed in toString.
184      */
185     public X509Name(
186         Vector      ordering,
187         Hashtable   attributes)
188     {
189         if (ordering != null)
190         {
191             for (int i = 0; i != ordering.size(); i++)
192             {
193                 this.ordering.addElement(ordering.elementAt(i));
194             }
195         }
196         else
197         {
198             Enumeration     e = attributes.keys();
199
200             while (e.hasMoreElements())
201             {
202                 this.ordering.addElement(e.nextElement());
203             }
204         }
205
206         for (int i = 0; i != this.ordering.size(); i++)
207         {
208             DERObjectIdentifier     oid = (DERObjectIdentifier)this.ordering.elementAt(i);
209
210             if (OIDLookUp.get(oid) == null)
211             {
212                 throw new IllegalArgumentException("Unknown object id - " + oid.getId() + " - passed to distinguished name");
213             }
214
215             if (attributes.get(oid) == null)
216             {
217                 throw new IllegalArgumentException("No attribute for object id - " + oid.getId() + " - passed to distinguished name");
218             }
219
220             this.values.addElement(attributes.get(oid)); // copy the hash table
221         }
222     }
223
224     /**
225      * takes two vectors one of the oids and the other of the values.
226      */
227     public X509Name(
228         Vector  ordering,
229         Vector  values)
230     {
231         if (ordering.size() != values.size())
232         {
233             throw new IllegalArgumentException("ordering vector must be same length as values.");
234         }
235
236         for (int i = 0; i < ordering.size(); i++)
237         {
238             this.ordering.addElement(ordering.elementAt(i));
239             this.values.addElement(values.elementAt(i));
240         }
241     }
242
243     /**
244      * takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
245      * some such, converting it into an ordered set of name attributes.
246      */
247     public X509Name(
248         String  dirName)
249     {
250         X509NameTokenizer   nTok = new X509NameTokenizer(dirName);
251
252         while (nTok.hasMoreTokens())
253         {
254             String  token = nTok.nextToken();
255             int     index = token.indexOf('=');
256
257             if (index == -1)
258             {
259                 throw new IllegalArgumentException("badly formated directory string");
260             }
261
262             String              name = token.substring(0, index);
263             String              value = token.substring(index + 1);
264             DERObjectIdentifier oid = null;
265
266             if (name.toUpperCase().startsWith("OID."))
267             {
268                 oid = new DERObjectIdentifier(name.substring(4));
269             }
270             else if (name.charAt(0) >= '0' && name.charAt(0) <= '9')
271             {
272                 oid = new DERObjectIdentifier(name);
273             }
274             else
275             {
276                 oid = (DERObjectIdentifier)SymbolLookUp.get(name.toLowerCase());
277                 if (oid == null)
278                 {
279                     throw new IllegalArgumentException("Unknown object id - " + name + " - passed to distinguished name");
280                 }
281             }
282
283             this.ordering.addElement(oid);
284             this.values.addElement(value);
285         }
286     }
287
288     /**
289      * return false if we have characters out of the range of a printable
290      * string, true otherwise.
291      */
292     private boolean canBePrintable(
293         String  str)
294     {
295         for (int i = str.length() - 1; i >= 0; i--)
296         {
297             if (str.charAt(i) > 0x007f)
298             {
299                 return false;
300             }
301         }
302
303         return true;
304     }
305
306     public DERObject getDERObject()
307     {
308         if (seq == null)
309         {
310             DEREncodableVector  vec = new DEREncodableVector();
311
312             for (int i = 0; i != ordering.size(); i++)
313             {
314                 DEREncodableVector      v = new DEREncodableVector();
315                 DERObjectIdentifier     oid = (DERObjectIdentifier)ordering.elementAt(i);
316
317                 v.add(oid);
318
319                 String  str = (String)values.elementAt(i);
320
321                 if (oid.equals(EmailAddress))
322                 {
323                     v.add(new DERIA5String(str));
324                 }
325                 else
326                 {
327                     if (canBePrintable(str))
328                     {
329                         v.add(new DERPrintableString(str));
330                     }
331                     else
332                     {
333                         v.add(new DERUTF8String(str));
334                     }
335                 }
336
337                 vec.add(new DERSet(new DERSequence(v)));
338             }
339
340             seq = new DERSequence(vec);
341         }
342
343         return seq;
344     }
345
346     /**
347      * test for equality - note: case is ignored.
348      */
349     public boolean equals(Object _obj) 
350     {
351         if (_obj == this)
352         {
353             return true;
354         }
355
356         if (_obj == null || !(_obj instanceof X509Name))
357         {
358             return false;
359         }
360         
361         X509Name _oxn          = (X509Name)_obj;
362         int      _orderingSize = ordering.size();
363
364         if (_orderingSize != _oxn.ordering.size()) 
365         {
366                         return false;
367                 }
368                 
369                 boolean[] _indexes = new boolean[_orderingSize];
370
371                 for(int i = 0; i < _orderingSize; i++) 
372                 {
373                         boolean _found = false;
374                         String  _oid   = ((DERObjectIdentifier)ordering.elementAt(i)).getId();
375                         String  _val   = (String)values.elementAt(i);
376                         
377                         for(int j = 0; j < _orderingSize; j++) 
378                         {
379                                 if(_indexes[j] == true)
380                                 {
381                                         continue;
382                                 }
383                                 
384                                 String _oOID = ((DERObjectIdentifier)_oxn.ordering.elementAt(j)).getId();
385                                 String _oVal = (String)_oxn.values.elementAt(j);
386
387                 // was equalsIgnoreCase but MIDP doesn't like that.
388                                 if(_oid.equals(_oOID) && _val.toLowerCase().equals(_oVal.toLowerCase()))
389                                 {
390                                         _indexes[j] = true;
391                                         _found      = true;
392                                         break;
393                                 }
394
395                         }
396
397                         if(!_found)
398                         {
399                                 return false;
400                         }
401                 }
402                 
403                 return true;
404         }
405         
406     public int hashCode()
407     {
408         ASN1Sequence  seq = (ASN1Sequence)this.getDERObject();
409         Enumeration   e = seq.getObjects();
410         int           hashCode = 0;
411
412         while (e.hasMoreElements())
413         {
414             hashCode ^= e.nextElement().hashCode();
415         }
416
417         return hashCode;
418     }
419
420     public String toString()
421     {
422         StringBuffer            buf = new StringBuffer();
423         boolean                 first = true;
424         Enumeration             e1 = ordering.elements();
425         Enumeration             e2 = values.elements();
426
427         while (e1.hasMoreElements())
428         {
429             Object                  oid = e1.nextElement();
430             String                  sym = (String)OIDLookUp.get(oid);
431             
432             if (first)
433             {
434                 first = false;
435             }
436             else
437             {
438                 buf.append(",");
439             }
440
441             if (sym != null)
442             {
443                 buf.append(sym);
444             }
445             else
446             {
447                 buf.append(((DERObjectIdentifier)oid).getId());
448             }
449
450             buf.append("=");
451
452             int     index = buf.length();
453
454             buf.append((String)e2.nextElement());
455
456             int     end = buf.length();
457
458             while (index != end)
459             {
460                 if ((buf.charAt(index) == ',')
461                    || (buf.charAt(index) == '"')
462                    || (buf.charAt(index) == '\\')
463                    || (buf.charAt(index) == '+')
464                    || (buf.charAt(index) == '<')
465                    || (buf.charAt(index) == '>')
466                    || (buf.charAt(index) == ';'))
467                 {
468                     buf.insert(index, "\\");
469                     index++;
470                     end++;
471                 }
472
473                 index++;
474             }
475         }
476
477         return buf.toString();
478     }
479 }