libjava/ChangeLog:
[official-gcc.git] / libjava / classpath / gnu / java / security / x509 / X500DistinguishedName.java
blobe2e05c57e4bdf3f9459abffae28d3b21082bdac9
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.lang.CPStringBuilder;
43 import gnu.java.security.OID;
44 import gnu.java.security.der.DER;
45 import gnu.java.security.der.DERReader;
46 import gnu.java.security.der.DERValue;
48 import java.io.EOFException;
49 import java.io.IOException;
50 import java.io.InputStream;
51 import java.io.Reader;
52 import java.io.StringReader;
53 import java.security.Principal;
54 import java.util.ArrayList;
55 import java.util.Collections;
56 import java.util.HashSet;
57 import java.util.Iterator;
58 import java.util.LinkedHashMap;
59 import java.util.LinkedList;
60 import java.util.List;
61 import java.util.Map;
62 import java.util.Set;
64 public class X500DistinguishedName implements Principal
66 // Constants and fields.
67 // -------------------------------------------------------------------------
69 public static final OID CN = new OID("2.5.4.3");
70 public static final OID C = new OID("2.5.4.6");
71 public static final OID L = new OID("2.5.4.7");
72 public static final OID ST = new OID("2.5.4.8");
73 public static final OID STREET = new OID("2.5.4.9");
74 public static final OID O = new OID("2.5.4.10");
75 public static final OID OU = new OID("2.5.4.11");
76 public static final OID T = new OID("2.5.4.12");
77 public static final OID DNQ = new OID("2.5.4.46");
78 public static final OID NAME = new OID("2.5.4.41");
79 public static final OID GIVENNAME = new OID("2.5.4.42");
80 public static final OID INITIALS = new OID("2.5.4.43");
81 public static final OID GENERATION = new OID("2.5.4.44");
82 public static final OID EMAIL = new OID("1.2.840.113549.1.9.1");
83 public static final OID DC = new OID("0.9.2342.19200300.100.1.25");
84 public static final OID UID = new OID("0.9.2342.19200300.100.1.1");
86 private List components;
87 private Map currentRdn;
88 private boolean fixed;
89 private String stringRep;
90 private byte[] encoded;
92 // Constructors.
93 // -------------------------------------------------------------------------
95 public X500DistinguishedName()
97 components = new LinkedList();
98 currentRdn = new LinkedHashMap();
99 components.add(currentRdn);
102 public X500DistinguishedName(String name)
104 this();
107 parseString(name);
109 catch (IOException ioe)
111 throw new IllegalArgumentException(ioe.toString());
115 public X500DistinguishedName(byte[] encoded) throws IOException
117 this();
118 parseDer(new DERReader(encoded));
121 public X500DistinguishedName(InputStream encoded) throws IOException
123 this();
124 parseDer(new DERReader(encoded));
127 // Instance methods.
128 // -------------------------------------------------------------------------
130 public String getName()
132 return toString();
135 public void newRelativeDistinguishedName()
137 if (fixed || currentRdn.isEmpty()) return;
138 currentRdn = new LinkedHashMap();
139 components.add(currentRdn);
142 public int size()
144 return components.size();
147 public int countComponents()
149 int count = 0;
150 for (Iterator it = components.iterator(); it.hasNext(); )
152 count += ((Map) it.next()).size();
154 return count;
157 public boolean containsComponent(OID oid, String value)
159 for (Iterator it = components.iterator(); it.hasNext(); )
161 Map rdn = (Map) it.next();
162 String s = (String) rdn.get(oid);
163 if (s == null)
164 continue;
165 if (compressWS(value).equalsIgnoreCase(compressWS(s)))
166 return true;
168 return false;
171 public String getComponent(OID oid)
173 for (Iterator it = components.iterator(); it.hasNext(); )
175 Map rdn = (Map) it.next();
176 if (rdn.containsKey(oid))
177 return (String) rdn.get(oid);
179 return null;
182 public String getComponent(OID oid, int rdn)
184 if (rdn >= size())
185 return null;
186 return (String) ((Map) components.get(rdn)).get(oid);
189 public void putComponent(OID oid, String value)
191 currentRdn.put(oid, value);
194 public void putComponent(String name, String value)
196 name = name.trim().toLowerCase();
197 if (name.equals("cn"))
198 putComponent(CN, value);
199 else if (name.equals("c"))
200 putComponent(C, value);
201 else if (name.equals("l"))
202 putComponent(L, value);
203 else if (name.equals("street"))
204 putComponent(STREET, value);
205 else if (name.equals("st"))
206 putComponent(ST, value);
207 else if (name.equals("t"))
208 putComponent(T, value);
209 else if (name.equals("dnq"))
210 putComponent(DNQ, value);
211 else if (name.equals("name"))
212 putComponent(NAME, value);
213 else if (name.equals("givenname"))
214 putComponent(GIVENNAME, value);
215 else if (name.equals("initials"))
216 putComponent(INITIALS, value);
217 else if (name.equals("generation"))
218 putComponent(GENERATION, value);
219 else if (name.equals("email"))
220 putComponent(EMAIL, value);
221 else if (name.equals("dc"))
222 putComponent(DC, value);
223 else if (name.equals("uid"))
224 putComponent(UID, value);
225 else if (name.equals("o"))
226 putComponent(O, value);
227 else if (name.equals("ou"))
228 putComponent(OU, value);
229 else
230 putComponent(new OID(name), value);
233 public void setUnmodifiable()
235 if (fixed) return;
236 fixed = true;
237 List newComps = new ArrayList(components.size());
238 for (Iterator it = components.iterator(); it.hasNext(); )
240 Map rdn = (Map) it.next();
241 rdn = Collections.unmodifiableMap(rdn);
242 newComps.add(rdn);
244 components = Collections.unmodifiableList(newComps);
245 currentRdn = Collections.EMPTY_MAP;
248 public int hashCode()
250 int sum = 0;
251 for (Iterator it = components.iterator(); it.hasNext(); )
253 Map m = (Map) it.next();
254 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
256 Map.Entry e = (Map.Entry) it2.next();
257 sum += e.getKey().hashCode();
258 sum += e.getValue().hashCode();
261 return sum;
264 public boolean equals(Object o)
266 if (!(o instanceof X500DistinguishedName))
267 return false;
268 if (size() != ((X500DistinguishedName) o).size())
269 return false;
270 for (int i = 0; i < size(); i++)
272 Map m = (Map) components.get(i);
273 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
275 Map.Entry e = (Map.Entry) it2.next();
276 OID oid = (OID) e.getKey();
277 String v1 = (String) e.getValue();
278 String v2 = ((X500DistinguishedName) o).getComponent(oid, i);
279 if (!compressWS(v1).equalsIgnoreCase(compressWS(v2)))
280 return false;
283 return true;
286 public String toString()
288 if (fixed && stringRep != null)
289 return stringRep;
290 CPStringBuilder str = new CPStringBuilder();
291 for (Iterator it = components.iterator(); it.hasNext(); )
293 Map m = (Map) it.next();
294 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
296 Map.Entry entry = (Map.Entry) it2.next();
297 OID oid = (OID) entry.getKey();
298 String value = (String) entry.getValue();
299 if (oid.equals(CN))
300 str.append("CN");
301 else if (oid.equals(C))
302 str.append("C");
303 else if (oid.equals(L))
304 str.append("L");
305 else if (oid.equals(ST))
306 str.append("ST");
307 else if (oid.equals(STREET))
308 str.append("STREET");
309 else if (oid.equals(O))
310 str.append("O");
311 else if (oid.equals(OU))
312 str.append("OU");
313 else if (oid.equals(T))
314 str.append("T");
315 else if (oid.equals(DNQ))
316 str.append("DNQ");
317 else if (oid.equals(NAME))
318 str.append("NAME");
319 else
320 str.append(oid.toString());
321 str.append('=');
322 str.append(value);
323 if (it2.hasNext())
324 str.append("+");
326 if (it.hasNext())
327 str.append(',');
329 return (stringRep = str.toString());
332 public byte[] getDer()
334 if (fixed && encoded != null)
335 return (byte[]) encoded.clone();
337 ArrayList name = new ArrayList(components.size());
338 for (Iterator it = components.iterator(); it.hasNext(); )
340 Map m = (Map) it.next();
341 if (m.isEmpty())
342 continue;
344 Set rdn = new HashSet();
345 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
347 Map.Entry e = (Map.Entry) it2.next();
348 ArrayList atav = new ArrayList(2);
349 atav.add(new DERValue(DER.OBJECT_IDENTIFIER, e.getKey()));
350 atav.add(new DERValue(DER.UTF8_STRING, e.getValue()));
351 rdn.add(new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, atav));
353 name.add(new DERValue(DER.SET|DER.CONSTRUCTED, rdn));
355 DERValue val = new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, name);
356 return (byte[]) (encoded = val.getEncoded()).clone();
359 // Own methods.
360 // -------------------------------------------------------------------------
362 private int sep;
364 private void parseString(String str) throws IOException
366 Reader in = new StringReader(str);
367 while (true)
369 String key = readAttributeType(in);
370 if (key == null)
371 break;
372 String value = readAttributeValue(in);
373 putComponent(key, value);
374 if (sep == ',')
375 newRelativeDistinguishedName();
377 setUnmodifiable();
380 private String readAttributeType(Reader in) throws IOException
382 CPStringBuilder buf = new CPStringBuilder();
383 int ch;
384 while ((ch = in.read()) != '=')
386 if (ch == -1)
388 if (buf.length() > 0)
389 throw new EOFException();
390 return null;
392 if (ch > 127)
393 throw new IOException("Invalid char: " + (char) ch);
394 if (Character.isLetterOrDigit((char) ch) || ch == '-' || ch == '.')
395 buf.append((char) ch);
396 else
397 throw new IOException("Invalid char: " + (char) ch);
399 return buf.toString();
402 private String readAttributeValue(Reader in) throws IOException
404 CPStringBuilder buf = new CPStringBuilder();
405 int ch = in.read();
406 if (ch == '#')
408 while (true)
410 ch = in.read();
411 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
412 || Character.isDigit((char) ch))
413 buf.append((char) ch);
414 else if (ch == '+' || ch == ',')
416 sep = ch;
417 String hex = buf.toString();
418 return new String(Util.toByteArray(hex));
420 else
421 throw new IOException("illegal character: " + (char) ch);
424 else if (ch == '"')
426 while (true)
428 ch = in.read();
429 if (ch == '"')
430 break;
431 else if (ch == '\\')
433 ch = in.read();
434 if (ch == -1)
435 throw new EOFException();
436 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
437 || Character.isDigit((char) ch))
439 int i = Character.digit((char) ch, 16) << 4;
440 ch = in.read();
441 if (!(('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
442 || Character.isDigit((char) ch)))
443 throw new IOException("illegal hex char");
444 i |= Character.digit((char) ch, 16);
445 buf.append((char) i);
447 else
448 buf.append((char) ch);
450 else
451 buf.append((char) ch);
453 sep = in.read();
454 if (sep != '+' || sep != ',')
455 throw new IOException("illegal character: " + (char) ch);
456 return buf.toString();
458 else
460 while (true)
462 switch (ch)
464 case '+':
465 case ',':
466 sep = ch;
467 return buf.toString();
468 case '\\':
469 ch = in.read();
470 if (ch == -1)
471 throw new EOFException();
472 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
473 || Character.isDigit((char) ch))
475 int i = Character.digit((char) ch, 16) << 4;
476 ch = in.read();
477 if (!(('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
478 || Character.isDigit((char) ch)))
479 throw new IOException("illegal hex char");
480 i |= Character.digit((char) ch, 16);
481 buf.append((char) i);
483 else
484 buf.append((char) ch);
485 break;
486 case '=':
487 case '<':
488 case '>':
489 case '#':
490 case ';':
491 throw new IOException("illegal character: " + (char) ch);
492 case -1:
493 throw new EOFException();
494 default:
495 buf.append((char) ch);
496 ch = in.read();
497 if (ch == -1)
498 return buf.toString();
504 private void parseDer(DERReader der) throws IOException
506 DERValue name = der.read();
507 if (!name.isConstructed())
508 throw new IOException("malformed Name");
509 encoded = name.getEncoded();
510 int len = 0;
511 while (len < name.getLength())
513 DERValue rdn = der.read();
514 if (!rdn.isConstructed())
515 throw new IOException("badly formed RDNSequence");
516 int len2 = 0;
517 while (len2 < rdn.getLength())
519 DERValue atav = der.read();
520 if (!atav.isConstructed())
521 throw new IOException("badly formed AttributeTypeAndValue");
522 DERValue val = der.read();
523 if (val.getTag() != DER.OBJECT_IDENTIFIER)
524 throw new IOException("badly formed AttributeTypeAndValue");
525 OID oid = (OID) val.getValue();
526 val = der.read();
527 if (!(val.getValue() instanceof String))
528 throw new IOException("badly formed AttributeTypeAndValue");
529 String value = (String) val.getValue();
530 putComponent(oid, value);
531 len2 += atav.getEncodedLength();
533 len += rdn.getEncodedLength();
534 if (len < name.getLength())
535 newRelativeDistinguishedName();
537 setUnmodifiable();
540 private static String compressWS(String str)
542 CPStringBuilder buf = new CPStringBuilder();
543 char lastChar = 0;
544 for (int i = 0; i < str.length(); i++)
546 char c = str.charAt(i);
547 if (Character.isWhitespace(c))
549 if (!Character.isWhitespace(lastChar))
550 buf.append(' ');
552 else
553 buf.append(c);
554 lastChar = c;
556 return buf.toString().trim();