004-11-15 Andreas Tobler <a.tobler@schweiz.ch>
[official-gcc.git] / libjava / gnu / java / security / x509 / X500DistinguishedName.java
blob64e320bef82ebe623efbdbf08c4bd469866d6eeb
1 /* X500DistinguishedName.java -- X.500 distinguished name.
2 Copyright (C) 2004 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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 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 java.io.EOFException;
42 import java.io.InputStream;
43 import java.io.IOException;
44 import java.io.Reader;
45 import java.io.StringReader;
47 import java.security.Principal;
49 import java.util.ArrayList;
50 import java.util.Collections;
51 import java.util.HashSet;
52 import java.util.Iterator;
53 import java.util.LinkedHashMap;
54 import java.util.LinkedList;
55 import java.util.List;
56 import java.util.Map;
57 import java.util.Set;
58 import java.util.TreeMap;
60 import gnu.java.security.der.DER;
61 import gnu.java.security.der.DERReader;
62 import gnu.java.security.der.DERValue;
63 import gnu.java.security.OID;
65 public class X500DistinguishedName implements Principal
68 // Constants and fields.
69 // -------------------------------------------------------------------------
71 public static final OID CN = new OID("2.5.4.3");
72 public static final OID C = new OID("2.5.4.6");
73 public static final OID L = new OID("2.5.4.7");
74 public static final OID ST = new OID("2.5.4.8");
75 public static final OID STREET = new OID("2.5.4.9");
76 public static final OID O = new OID("2.5.4.10");
77 public static final OID OU = new OID("2.5.4.11");
78 public static final OID T = new OID("2.5.4.12");
79 public static final OID DNQ = new OID("2.5.4.46");
80 public static final OID NAME = new OID("2.5.4.41");
81 public static final OID GIVENNAME = new OID("2.5.4.42");
82 public static final OID INITIALS = new OID("2.5.4.43");
83 public static final OID GENERATION = new OID("2.5.4.44");
84 public static final OID EMAIL = new OID("1.2.840.113549.1.9.1");
85 public static final OID DC = new OID("0.9.2342.19200300.100.1.25");
86 public static final OID UID = new OID("0.9.2342.19200300.100.1.1");
88 private List components;
89 private Map currentRdn;
90 private boolean fixed;
91 private String stringRep;
92 private byte[] encoded;
94 // Constructors.
95 // -------------------------------------------------------------------------
97 public X500DistinguishedName()
99 components = new LinkedList();
100 currentRdn = new LinkedHashMap();
101 components.add(currentRdn);
104 public X500DistinguishedName(String name)
106 this();
109 parseString(name);
111 catch (IOException ioe)
113 throw new IllegalArgumentException(ioe.toString());
117 public X500DistinguishedName(byte[] encoded) throws IOException
119 this();
120 parseDer(new DERReader(encoded));
123 public X500DistinguishedName(InputStream encoded) throws IOException
125 this();
126 parseDer(new DERReader(encoded));
129 // Instance methods.
130 // -------------------------------------------------------------------------
132 public String getName()
134 return toString();
137 public void newRelativeDistinguishedName()
139 if (fixed || currentRdn.isEmpty()) return;
140 currentRdn = new LinkedHashMap();
141 components.add(currentRdn);
144 public int size()
146 return components.size();
149 public int countComponents()
151 int count = 0;
152 for (Iterator it = components.iterator(); it.hasNext(); )
154 count += ((Map) it.next()).size();
156 return count;
159 public boolean containsComponent(OID oid, String value)
161 for (Iterator it = components.iterator(); it.hasNext(); )
163 Map rdn = (Map) it.next();
164 String s = (String) rdn.get(oid);
165 if (s == null)
166 continue;
167 if (compressWS(value).equalsIgnoreCase(compressWS(s)))
168 return true;
170 return false;
173 public String getComponent(OID oid)
175 for (Iterator it = components.iterator(); it.hasNext(); )
177 Map rdn = (Map) it.next();
178 if (rdn.containsKey(oid))
179 return (String) rdn.get(oid);
181 return null;
184 public String getComponent(OID oid, int rdn)
186 if (rdn >= size())
187 return null;
188 return (String) ((Map) components.get(rdn)).get(oid);
191 public void putComponent(OID oid, String value)
193 currentRdn.put(oid, value);
196 public void putComponent(String name, String value)
198 name = name.trim().toLowerCase();
199 if (name.equals("cn"))
200 putComponent(CN, value);
201 else if (name.equals("c"))
202 putComponent(C, value);
203 else if (name.equals("l"))
204 putComponent(L, value);
205 else if (name.equals("street"))
206 putComponent(STREET, value);
207 else if (name.equals("st"))
208 putComponent(ST, value);
209 else if (name.equals("t"))
210 putComponent(T, value);
211 else if (name.equals("dnq"))
212 putComponent(DNQ, value);
213 else if (name.equals("name"))
214 putComponent(NAME, value);
215 else if (name.equals("givenname"))
216 putComponent(GIVENNAME, value);
217 else if (name.equals("initials"))
218 putComponent(INITIALS, value);
219 else if (name.equals("generation"))
220 putComponent(GENERATION, value);
221 else if (name.equals("email"))
222 putComponent(EMAIL, value);
223 else if (name.equals("dc"))
224 putComponent(DC, value);
225 else if (name.equals("uid"))
226 putComponent(UID, 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();
334 ArrayList name = new ArrayList(components.size());
335 for (Iterator it = components.iterator(); it.hasNext(); )
337 Map m = (Map) it.next();
338 if (m.isEmpty())
339 continue;
340 Set rdn = new HashSet();
341 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
343 Map.Entry e = (Map.Entry) it.next();
344 ArrayList atav = new ArrayList(2);
345 atav.add(new DERValue(DER.OBJECT_IDENTIFIER, e.getKey()));
346 atav.add(new DERValue(DER.UTF8_STRING, e.getValue()));
347 rdn.add(new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, atav));
349 name.add(new DERValue(DER.SET|DER.CONSTRUCTED, rdn));
351 DERValue val = new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, name);
352 return (byte[]) (encoded = val.getEncoded()).clone();
355 // Own methods.
356 // -------------------------------------------------------------------------
358 private int sep;
360 private void parseString(String str) throws IOException
362 Reader in = new StringReader(str);
363 while (true)
365 String key = readAttributeType(in);
366 if (key == null)
367 break;
368 String value = readAttributeValue(in);
369 putComponent(key, value);
370 if (sep == ',')
371 newRelativeDistinguishedName();
373 setUnmodifiable();
376 private String readAttributeType(Reader in) throws IOException
378 StringBuffer buf = new StringBuffer();
379 int ch;
380 while ((ch = in.read()) != '=')
382 if (ch == -1)
384 if (buf.length() > 0)
385 throw new EOFException();
386 return null;
388 if (ch > 127)
389 throw new IOException("Invalid char: " + (char) ch);
390 if (Character.isLetterOrDigit((char) ch) || ch == '-' || ch == '.')
391 buf.append((char) ch);
392 else
393 throw new IOException("Invalid char: " + (char) ch);
395 return buf.toString();
398 private String readAttributeValue(Reader in) throws IOException
400 StringBuffer buf = new StringBuffer();
401 int ch = in.read();
402 if (ch == '#')
404 while (true)
406 ch = in.read();
407 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
408 || Character.isDigit((char) ch))
409 buf.append((char) ch);
410 else if (ch == '+' || ch == ',')
412 sep = ch;
413 String hex = buf.toString();
414 return new String(Util.toByteArray(hex));
416 else
417 throw new IOException("illegal character: " + (char) ch);
420 else if (ch == '"')
422 while (true)
424 ch = in.read();
425 if (ch == '"')
426 break;
427 else if (ch == '\\')
429 ch = in.read();
430 if (ch == -1)
431 throw new EOFException();
432 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
433 || Character.isDigit((char) ch))
435 int i = Character.digit((char) ch, 16) << 4;
436 ch = in.read();
437 if (!(('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
438 || Character.isDigit((char) ch)))
439 throw new IOException("illegal hex char");
440 i |= Character.digit((char) ch, 16);
441 buf.append((char) i);
443 else
444 buf.append((char) ch);
446 else
447 buf.append((char) ch);
449 sep = in.read();
450 if (sep != '+' || sep != ',')
451 throw new IOException("illegal character: " + (char) ch);
452 return buf.toString();
454 else
456 while (true)
458 switch (ch)
460 case '+':
461 case ',':
462 sep = ch;
463 return buf.toString();
464 case '\\':
465 ch = in.read();
466 if (ch == -1)
467 throw new EOFException();
468 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
469 || Character.isDigit((char) ch))
471 int i = Character.digit((char) ch, 16) << 4;
472 ch = in.read();
473 if (!(('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
474 || Character.isDigit((char) ch)))
475 throw new IOException("illegal hex char");
476 i |= Character.digit((char) ch, 16);
477 buf.append((char) i);
479 else
480 buf.append((char) ch);
481 break;
482 case '=':
483 case '<':
484 case '>':
485 case '#':
486 case ';':
487 throw new IOException("illegal character: " + (char) ch);
488 case -1:
489 throw new EOFException();
490 default:
491 buf.append((char) ch);
497 private void parseDer(DERReader der) throws IOException
499 DERValue name = der.read();
500 if (!name.isConstructed())
501 throw new IOException("malformed Name");
502 encoded = name.getEncoded();
503 int len = 0;
504 while (len < name.getLength())
506 DERValue rdn = der.read();
507 if (!rdn.isConstructed())
508 throw new IOException("badly formed RDNSequence");
509 int len2 = 0;
510 while (len2 < rdn.getLength())
512 DERValue atav = der.read();
513 if (!atav.isConstructed())
514 throw new IOException("badly formed AttributeTypeAndValue");
515 DERValue val = der.read();
516 if (val.getTag() != DER.OBJECT_IDENTIFIER)
517 throw new IOException("badly formed AttributeTypeAndValue");
518 OID oid = (OID) val.getValue();
519 val = der.read();
520 if (!(val.getValue() instanceof String))
521 throw new IOException("badly formed AttributeTypeAndValue");
522 String value = (String) val.getValue();
523 putComponent(oid, value);
524 len2 += atav.getEncodedLength();
526 len += rdn.getEncodedLength();
527 if (len < name.getLength())
528 newRelativeDistinguishedName();
530 setUnmodifiable();
533 private static String compressWS(String str)
535 StringBuffer buf = new StringBuffer();
536 char lastChar = 0;
537 for (int i = 0; i < str.length(); i++)
539 char c = str.charAt(i);
540 if (Character.isWhitespace(c))
542 if (!Character.isWhitespace(lastChar))
543 buf.append(' ');
545 else
546 buf.append(c);
547 lastChar = c;
549 return buf.toString().trim();