Imported GNU Classpath 0.90
[official-gcc.git] / libjava / classpath / gnu / java / security / x509 / X500DistinguishedName.java
blob02adad7d2fdc02551e7f990c6457095908da7206
1 /* X500DistinguishedName.java -- X.500 distinguished name.
2 Copyright (C) 2004, 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu.java.security.x509;
41 import gnu.java.security.OID;
42 import gnu.java.security.der.DER;
43 import gnu.java.security.der.DERReader;
44 import gnu.java.security.der.DERValue;
46 import java.io.EOFException;
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.io.Reader;
50 import java.io.StringReader;
51 import java.security.Principal;
52 import java.util.ArrayList;
53 import java.util.Collections;
54 import java.util.HashSet;
55 import java.util.Iterator;
56 import java.util.LinkedHashMap;
57 import java.util.LinkedList;
58 import java.util.List;
59 import java.util.Map;
60 import java.util.Set;
62 public class X500DistinguishedName implements Principal
64 // Constants and fields.
65 // -------------------------------------------------------------------------
67 public static final OID CN = new OID("2.5.4.3");
68 public static final OID C = new OID("2.5.4.6");
69 public static final OID L = new OID("2.5.4.7");
70 public static final OID ST = new OID("2.5.4.8");
71 public static final OID STREET = new OID("2.5.4.9");
72 public static final OID O = new OID("2.5.4.10");
73 public static final OID OU = new OID("2.5.4.11");
74 public static final OID T = new OID("2.5.4.12");
75 public static final OID DNQ = new OID("2.5.4.46");
76 public static final OID NAME = new OID("2.5.4.41");
77 public static final OID GIVENNAME = new OID("2.5.4.42");
78 public static final OID INITIALS = new OID("2.5.4.43");
79 public static final OID GENERATION = new OID("2.5.4.44");
80 public static final OID EMAIL = new OID("1.2.840.113549.1.9.1");
81 public static final OID DC = new OID("0.9.2342.19200300.100.1.25");
82 public static final OID UID = new OID("0.9.2342.19200300.100.1.1");
84 private List components;
85 private Map currentRdn;
86 private boolean fixed;
87 private String stringRep;
88 private byte[] encoded;
90 // Constructors.
91 // -------------------------------------------------------------------------
93 public X500DistinguishedName()
95 components = new LinkedList();
96 currentRdn = new LinkedHashMap();
97 components.add(currentRdn);
100 public X500DistinguishedName(String name)
102 this();
105 parseString(name);
107 catch (IOException ioe)
109 throw new IllegalArgumentException(ioe.toString());
113 public X500DistinguishedName(byte[] encoded) throws IOException
115 this();
116 parseDer(new DERReader(encoded));
119 public X500DistinguishedName(InputStream encoded) throws IOException
121 this();
122 parseDer(new DERReader(encoded));
125 // Instance methods.
126 // -------------------------------------------------------------------------
128 public String getName()
130 return toString();
133 public void newRelativeDistinguishedName()
135 if (fixed || currentRdn.isEmpty()) return;
136 currentRdn = new LinkedHashMap();
137 components.add(currentRdn);
140 public int size()
142 return components.size();
145 public int countComponents()
147 int count = 0;
148 for (Iterator it = components.iterator(); it.hasNext(); )
150 count += ((Map) it.next()).size();
152 return count;
155 public boolean containsComponent(OID oid, String value)
157 for (Iterator it = components.iterator(); it.hasNext(); )
159 Map rdn = (Map) it.next();
160 String s = (String) rdn.get(oid);
161 if (s == null)
162 continue;
163 if (compressWS(value).equalsIgnoreCase(compressWS(s)))
164 return true;
166 return false;
169 public String getComponent(OID oid)
171 for (Iterator it = components.iterator(); it.hasNext(); )
173 Map rdn = (Map) it.next();
174 if (rdn.containsKey(oid))
175 return (String) rdn.get(oid);
177 return null;
180 public String getComponent(OID oid, int rdn)
182 if (rdn >= size())
183 return null;
184 return (String) ((Map) components.get(rdn)).get(oid);
187 public void putComponent(OID oid, String value)
189 currentRdn.put(oid, value);
192 public void putComponent(String name, String value)
194 name = name.trim().toLowerCase();
195 if (name.equals("cn"))
196 putComponent(CN, value);
197 else if (name.equals("c"))
198 putComponent(C, value);
199 else if (name.equals("l"))
200 putComponent(L, value);
201 else if (name.equals("street"))
202 putComponent(STREET, value);
203 else if (name.equals("st"))
204 putComponent(ST, value);
205 else if (name.equals("t"))
206 putComponent(T, value);
207 else if (name.equals("dnq"))
208 putComponent(DNQ, value);
209 else if (name.equals("name"))
210 putComponent(NAME, value);
211 else if (name.equals("givenname"))
212 putComponent(GIVENNAME, value);
213 else if (name.equals("initials"))
214 putComponent(INITIALS, value);
215 else if (name.equals("generation"))
216 putComponent(GENERATION, value);
217 else if (name.equals("email"))
218 putComponent(EMAIL, value);
219 else if (name.equals("dc"))
220 putComponent(DC, value);
221 else if (name.equals("uid"))
222 putComponent(UID, value);
223 else if (name.equals("o"))
224 putComponent(O, value);
225 else if (name.equals("ou"))
226 putComponent(OU, value);
227 else
228 putComponent(new OID(name), value);
231 public void setUnmodifiable()
233 if (fixed) return;
234 fixed = true;
235 List newComps = new ArrayList(components.size());
236 for (Iterator it = components.iterator(); it.hasNext(); )
238 Map rdn = (Map) it.next();
239 rdn = Collections.unmodifiableMap(rdn);
240 newComps.add(rdn);
242 components = Collections.unmodifiableList(newComps);
243 currentRdn = Collections.EMPTY_MAP;
246 public int hashCode()
248 int sum = 0;
249 for (Iterator it = components.iterator(); it.hasNext(); )
251 Map m = (Map) it.next();
252 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
254 Map.Entry e = (Map.Entry) it2.next();
255 sum += e.getKey().hashCode();
256 sum += e.getValue().hashCode();
259 return sum;
262 public boolean equals(Object o)
264 if (!(o instanceof X500DistinguishedName))
265 return false;
266 if (size() != ((X500DistinguishedName) o).size())
267 return false;
268 for (int i = 0; i < size(); i++)
270 Map m = (Map) components.get(i);
271 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
273 Map.Entry e = (Map.Entry) it2.next();
274 OID oid = (OID) e.getKey();
275 String v1 = (String) e.getValue();
276 String v2 = ((X500DistinguishedName) o).getComponent(oid, i);
277 if (!compressWS(v1).equalsIgnoreCase(compressWS(v2)))
278 return false;
281 return true;
284 public String toString()
286 if (fixed && stringRep != null)
287 return stringRep;
288 StringBuffer str = new StringBuffer();
289 for (Iterator it = components.iterator(); it.hasNext(); )
291 Map m = (Map) it.next();
292 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
294 Map.Entry entry = (Map.Entry) it2.next();
295 OID oid = (OID) entry.getKey();
296 String value = (String) entry.getValue();
297 if (oid.equals(CN))
298 str.append("CN");
299 else if (oid.equals(C))
300 str.append("C");
301 else if (oid.equals(L))
302 str.append("L");
303 else if (oid.equals(ST))
304 str.append("ST");
305 else if (oid.equals(STREET))
306 str.append("STREET");
307 else if (oid.equals(O))
308 str.append("O");
309 else if (oid.equals(OU))
310 str.append("OU");
311 else if (oid.equals(T))
312 str.append("T");
313 else if (oid.equals(DNQ))
314 str.append("DNQ");
315 else if (oid.equals(NAME))
316 str.append("NAME");
317 else
318 str.append(oid.toString());
319 str.append('=');
320 str.append(value);
321 if (it2.hasNext())
322 str.append("+");
324 if (it.hasNext())
325 str.append(',');
327 return (stringRep = str.toString());
330 public byte[] getDer()
332 if (fixed && encoded != null)
333 return (byte[]) encoded.clone();
335 ArrayList name = new ArrayList(components.size());
336 for (Iterator it = components.iterator(); it.hasNext(); )
338 Map m = (Map) it.next();
339 if (m.isEmpty())
340 continue;
342 Set rdn = new HashSet();
343 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
345 Map.Entry e = (Map.Entry) it2.next();
346 ArrayList atav = new ArrayList(2);
347 atav.add(new DERValue(DER.OBJECT_IDENTIFIER, e.getKey()));
348 atav.add(new DERValue(DER.UTF8_STRING, e.getValue()));
349 rdn.add(new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, atav));
351 name.add(new DERValue(DER.SET|DER.CONSTRUCTED, rdn));
353 DERValue val = new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, name);
354 return (byte[]) (encoded = val.getEncoded()).clone();
357 // Own methods.
358 // -------------------------------------------------------------------------
360 private int sep;
362 private void parseString(String str) throws IOException
364 Reader in = new StringReader(str);
365 while (true)
367 String key = readAttributeType(in);
368 if (key == null)
369 break;
370 String value = readAttributeValue(in);
371 putComponent(key, value);
372 if (sep == ',')
373 newRelativeDistinguishedName();
375 setUnmodifiable();
378 private String readAttributeType(Reader in) throws IOException
380 StringBuffer buf = new StringBuffer();
381 int ch;
382 while ((ch = in.read()) != '=')
384 if (ch == -1)
386 if (buf.length() > 0)
387 throw new EOFException();
388 return null;
390 if (ch > 127)
391 throw new IOException("Invalid char: " + (char) ch);
392 if (Character.isLetterOrDigit((char) ch) || ch == '-' || ch == '.')
393 buf.append((char) ch);
394 else
395 throw new IOException("Invalid char: " + (char) ch);
397 return buf.toString();
400 private String readAttributeValue(Reader in) throws IOException
402 StringBuffer buf = new StringBuffer();
403 int ch = in.read();
404 if (ch == '#')
406 while (true)
408 ch = in.read();
409 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
410 || Character.isDigit((char) ch))
411 buf.append((char) ch);
412 else if (ch == '+' || ch == ',')
414 sep = ch;
415 String hex = buf.toString();
416 return new String(Util.toByteArray(hex));
418 else
419 throw new IOException("illegal character: " + (char) ch);
422 else if (ch == '"')
424 while (true)
426 ch = in.read();
427 if (ch == '"')
428 break;
429 else if (ch == '\\')
431 ch = in.read();
432 if (ch == -1)
433 throw new EOFException();
434 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
435 || Character.isDigit((char) ch))
437 int i = Character.digit((char) ch, 16) << 4;
438 ch = in.read();
439 if (!(('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
440 || Character.isDigit((char) ch)))
441 throw new IOException("illegal hex char");
442 i |= Character.digit((char) ch, 16);
443 buf.append((char) i);
445 else
446 buf.append((char) ch);
448 else
449 buf.append((char) ch);
451 sep = in.read();
452 if (sep != '+' || sep != ',')
453 throw new IOException("illegal character: " + (char) ch);
454 return buf.toString();
456 else
458 while (true)
460 switch (ch)
462 case '+':
463 case ',':
464 sep = ch;
465 return buf.toString();
466 case '\\':
467 ch = in.read();
468 if (ch == -1)
469 throw new EOFException();
470 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
471 || Character.isDigit((char) ch))
473 int i = Character.digit((char) ch, 16) << 4;
474 ch = in.read();
475 if (!(('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
476 || Character.isDigit((char) ch)))
477 throw new IOException("illegal hex char");
478 i |= Character.digit((char) ch, 16);
479 buf.append((char) i);
481 else
482 buf.append((char) ch);
483 break;
484 case '=':
485 case '<':
486 case '>':
487 case '#':
488 case ';':
489 throw new IOException("illegal character: " + (char) ch);
490 case -1:
491 throw new EOFException();
492 default:
493 buf.append((char) ch);
494 ch = in.read();
495 if (ch == -1)
496 return buf.toString();
502 private void parseDer(DERReader der) throws IOException
504 DERValue name = der.read();
505 if (!name.isConstructed())
506 throw new IOException("malformed Name");
507 encoded = name.getEncoded();
508 int len = 0;
509 while (len < name.getLength())
511 DERValue rdn = der.read();
512 if (!rdn.isConstructed())
513 throw new IOException("badly formed RDNSequence");
514 int len2 = 0;
515 while (len2 < rdn.getLength())
517 DERValue atav = der.read();
518 if (!atav.isConstructed())
519 throw new IOException("badly formed AttributeTypeAndValue");
520 DERValue val = der.read();
521 if (val.getTag() != DER.OBJECT_IDENTIFIER)
522 throw new IOException("badly formed AttributeTypeAndValue");
523 OID oid = (OID) val.getValue();
524 val = der.read();
525 if (!(val.getValue() instanceof String))
526 throw new IOException("badly formed AttributeTypeAndValue");
527 String value = (String) val.getValue();
528 putComponent(oid, value);
529 len2 += atav.getEncodedLength();
531 len += rdn.getEncodedLength();
532 if (len < name.getLength())
533 newRelativeDistinguishedName();
535 setUnmodifiable();
538 private static String compressWS(String str)
540 StringBuffer buf = new StringBuffer();
541 char lastChar = 0;
542 for (int i = 0; i < str.length(); i++)
544 char c = str.charAt(i);
545 if (Character.isWhitespace(c))
547 if (!Character.isWhitespace(lastChar))
548 buf.append(' ');
550 else
551 buf.append(c);
552 lastChar = c;
554 return buf.toString().trim();