Merge from the pain train
[official-gcc.git] / libjava / gnu / java / security / x509 / X500DistinguishedName.java
blobdaf76a22dfa94d50b16485212f6f967819ffe809
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;
59 import gnu.java.security.der.DER;
60 import gnu.java.security.der.DERReader;
61 import gnu.java.security.der.DERValue;
62 import gnu.java.security.OID;
64 public class X500DistinguishedName implements Principal
67 // Constants and fields.
68 // -------------------------------------------------------------------------
70 public static final OID CN = new OID("2.5.4.3");
71 public static final OID C = new OID("2.5.4.6");
72 public static final OID L = new OID("2.5.4.7");
73 public static final OID ST = new OID("2.5.4.8");
74 public static final OID STREET = new OID("2.5.4.9");
75 public static final OID O = new OID("2.5.4.10");
76 public static final OID OU = new OID("2.5.4.11");
77 public static final OID T = new OID("2.5.4.12");
78 public static final OID DNQ = new OID("2.5.4.46");
79 public static final OID NAME = new OID("2.5.4.41");
80 public static final OID GIVENNAME = new OID("2.5.4.42");
81 public static final OID INITIALS = new OID("2.5.4.43");
82 public static final OID GENERATION = new OID("2.5.4.44");
83 public static final OID EMAIL = new OID("1.2.840.113549.1.9.1");
84 public static final OID DC = new OID("0.9.2342.19200300.100.1.25");
85 public static final OID UID = new OID("0.9.2342.19200300.100.1.1");
87 private List components;
88 private Map currentRdn;
89 private boolean fixed;
90 private String stringRep;
91 private byte[] encoded;
93 // Constructors.
94 // -------------------------------------------------------------------------
96 public X500DistinguishedName()
98 components = new LinkedList();
99 currentRdn = new LinkedHashMap();
100 components.add(currentRdn);
103 public X500DistinguishedName(String name)
105 this();
108 parseString(name);
110 catch (IOException ioe)
112 throw new IllegalArgumentException(ioe.toString());
116 public X500DistinguishedName(byte[] encoded) throws IOException
118 this();
119 parseDer(new DERReader(encoded));
122 public X500DistinguishedName(InputStream encoded) throws IOException
124 this();
125 parseDer(new DERReader(encoded));
128 // Instance methods.
129 // -------------------------------------------------------------------------
131 public String getName()
133 return toString();
136 public void newRelativeDistinguishedName()
138 if (fixed || currentRdn.isEmpty()) return;
139 currentRdn = new LinkedHashMap();
140 components.add(currentRdn);
143 public int size()
145 return components.size();
148 public int countComponents()
150 int count = 0;
151 for (Iterator it = components.iterator(); it.hasNext(); )
153 count += ((Map) it.next()).size();
155 return count;
158 public boolean containsComponent(OID oid, String value)
160 for (Iterator it = components.iterator(); it.hasNext(); )
162 Map rdn = (Map) it.next();
163 String s = (String) rdn.get(oid);
164 if (s == null)
165 continue;
166 if (compressWS(value).equalsIgnoreCase(compressWS(s)))
167 return true;
169 return false;
172 public String getComponent(OID oid)
174 for (Iterator it = components.iterator(); it.hasNext(); )
176 Map rdn = (Map) it.next();
177 if (rdn.containsKey(oid))
178 return (String) rdn.get(oid);
180 return null;
183 public String getComponent(OID oid, int rdn)
185 if (rdn >= size())
186 return null;
187 return (String) ((Map) components.get(rdn)).get(oid);
190 public void putComponent(OID oid, String value)
192 currentRdn.put(oid, value);
195 public void putComponent(String name, String value)
197 name = name.trim().toLowerCase();
198 if (name.equals("cn"))
199 putComponent(CN, value);
200 else if (name.equals("c"))
201 putComponent(C, value);
202 else if (name.equals("l"))
203 putComponent(L, value);
204 else if (name.equals("street"))
205 putComponent(STREET, value);
206 else if (name.equals("st"))
207 putComponent(ST, value);
208 else if (name.equals("t"))
209 putComponent(T, value);
210 else if (name.equals("dnq"))
211 putComponent(DNQ, value);
212 else if (name.equals("name"))
213 putComponent(NAME, value);
214 else if (name.equals("givenname"))
215 putComponent(GIVENNAME, value);
216 else if (name.equals("initials"))
217 putComponent(INITIALS, value);
218 else if (name.equals("generation"))
219 putComponent(GENERATION, value);
220 else if (name.equals("email"))
221 putComponent(EMAIL, value);
222 else if (name.equals("dc"))
223 putComponent(DC, value);
224 else if (name.equals("uid"))
225 putComponent(UID, value);
226 else
227 putComponent(new OID(name), value);
230 public void setUnmodifiable()
232 if (fixed) return;
233 fixed = true;
234 List newComps = new ArrayList(components.size());
235 for (Iterator it = components.iterator(); it.hasNext(); )
237 Map rdn = (Map) it.next();
238 rdn = Collections.unmodifiableMap(rdn);
239 newComps.add(rdn);
241 components = Collections.unmodifiableList(newComps);
242 currentRdn = Collections.EMPTY_MAP;
245 public int hashCode()
247 int sum = 0;
248 for (Iterator it = components.iterator(); it.hasNext(); )
250 Map m = (Map) it.next();
251 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
253 Map.Entry e = (Map.Entry) it2.next();
254 sum += e.getKey().hashCode();
255 sum += e.getValue().hashCode();
258 return sum;
261 public boolean equals(Object o)
263 if (!(o instanceof X500DistinguishedName))
264 return false;
265 if (size() != ((X500DistinguishedName) o).size())
266 return false;
267 for (int i = 0; i < size(); i++)
269 Map m = (Map) components.get(i);
270 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
272 Map.Entry e = (Map.Entry) it2.next();
273 OID oid = (OID) e.getKey();
274 String v1 = (String) e.getValue();
275 String v2 = ((X500DistinguishedName) o).getComponent(oid, i);
276 if (!compressWS(v1).equalsIgnoreCase(compressWS(v2)))
277 return false;
280 return true;
283 public String toString()
285 if (fixed && stringRep != null)
286 return stringRep;
287 StringBuffer str = new StringBuffer();
288 for (Iterator it = components.iterator(); it.hasNext(); )
290 Map m = (Map) it.next();
291 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
293 Map.Entry entry = (Map.Entry) it2.next();
294 OID oid = (OID) entry.getKey();
295 String value = (String) entry.getValue();
296 if (oid.equals(CN))
297 str.append("CN");
298 else if (oid.equals(C))
299 str.append("C");
300 else if (oid.equals(L))
301 str.append("L");
302 else if (oid.equals(ST))
303 str.append("ST");
304 else if (oid.equals(STREET))
305 str.append("STREET");
306 else if (oid.equals(O))
307 str.append("O");
308 else if (oid.equals(OU))
309 str.append("OU");
310 else if (oid.equals(T))
311 str.append("T");
312 else if (oid.equals(DNQ))
313 str.append("DNQ");
314 else if (oid.equals(NAME))
315 str.append("NAME");
316 else
317 str.append(oid.toString());
318 str.append('=');
319 str.append(value);
320 if (it2.hasNext())
321 str.append("+");
323 if (it.hasNext())
324 str.append(',');
326 return (stringRep = str.toString());
329 public byte[] getDer()
331 if (fixed && encoded != null)
332 return (byte[]) encoded.clone();
333 ArrayList name = new ArrayList(components.size());
334 for (Iterator it = components.iterator(); it.hasNext(); )
336 Map m = (Map) it.next();
337 if (m.isEmpty())
338 continue;
339 Set rdn = new HashSet();
340 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
342 Map.Entry e = (Map.Entry) it.next();
343 ArrayList atav = new ArrayList(2);
344 atav.add(new DERValue(DER.OBJECT_IDENTIFIER, e.getKey()));
345 atav.add(new DERValue(DER.UTF8_STRING, e.getValue()));
346 rdn.add(new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, atav));
348 name.add(new DERValue(DER.SET|DER.CONSTRUCTED, rdn));
350 DERValue val = new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, name);
351 return (byte[]) (encoded = val.getEncoded()).clone();
354 // Own methods.
355 // -------------------------------------------------------------------------
357 private int sep;
359 private void parseString(String str) throws IOException
361 Reader in = new StringReader(str);
362 while (true)
364 String key = readAttributeType(in);
365 if (key == null)
366 break;
367 String value = readAttributeValue(in);
368 putComponent(key, value);
369 if (sep == ',')
370 newRelativeDistinguishedName();
372 setUnmodifiable();
375 private String readAttributeType(Reader in) throws IOException
377 StringBuffer buf = new StringBuffer();
378 int ch;
379 while ((ch = in.read()) != '=')
381 if (ch == -1)
383 if (buf.length() > 0)
384 throw new EOFException();
385 return null;
387 if (ch > 127)
388 throw new IOException("Invalid char: " + (char) ch);
389 if (Character.isLetterOrDigit((char) ch) || ch == '-' || ch == '.')
390 buf.append((char) ch);
391 else
392 throw new IOException("Invalid char: " + (char) ch);
394 return buf.toString();
397 private String readAttributeValue(Reader in) throws IOException
399 StringBuffer buf = new StringBuffer();
400 int ch = in.read();
401 if (ch == '#')
403 while (true)
405 ch = in.read();
406 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
407 || Character.isDigit((char) ch))
408 buf.append((char) ch);
409 else if (ch == '+' || ch == ',')
411 sep = ch;
412 String hex = buf.toString();
413 return new String(Util.toByteArray(hex));
415 else
416 throw new IOException("illegal character: " + (char) ch);
419 else if (ch == '"')
421 while (true)
423 ch = in.read();
424 if (ch == '"')
425 break;
426 else if (ch == '\\')
428 ch = in.read();
429 if (ch == -1)
430 throw new EOFException();
431 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
432 || Character.isDigit((char) ch))
434 int i = Character.digit((char) ch, 16) << 4;
435 ch = in.read();
436 if (!(('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
437 || Character.isDigit((char) ch)))
438 throw new IOException("illegal hex char");
439 i |= Character.digit((char) ch, 16);
440 buf.append((char) i);
442 else
443 buf.append((char) ch);
445 else
446 buf.append((char) ch);
448 sep = in.read();
449 if (sep != '+' || sep != ',')
450 throw new IOException("illegal character: " + (char) ch);
451 return buf.toString();
453 else
455 while (true)
457 switch (ch)
459 case '+':
460 case ',':
461 sep = ch;
462 return buf.toString();
463 case '\\':
464 ch = in.read();
465 if (ch == -1)
466 throw new EOFException();
467 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
468 || Character.isDigit((char) ch))
470 int i = Character.digit((char) ch, 16) << 4;
471 ch = in.read();
472 if (!(('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
473 || Character.isDigit((char) ch)))
474 throw new IOException("illegal hex char");
475 i |= Character.digit((char) ch, 16);
476 buf.append((char) i);
478 else
479 buf.append((char) ch);
480 break;
481 case '=':
482 case '<':
483 case '>':
484 case '#':
485 case ';':
486 throw new IOException("illegal character: " + (char) ch);
487 case -1:
488 throw new EOFException();
489 default:
490 buf.append((char) ch);
496 private void parseDer(DERReader der) throws IOException
498 DERValue name = der.read();
499 if (!name.isConstructed())
500 throw new IOException("malformed Name");
501 encoded = name.getEncoded();
502 int len = 0;
503 while (len < name.getLength())
505 DERValue rdn = der.read();
506 if (!rdn.isConstructed())
507 throw new IOException("badly formed RDNSequence");
508 int len2 = 0;
509 while (len2 < rdn.getLength())
511 DERValue atav = der.read();
512 if (!atav.isConstructed())
513 throw new IOException("badly formed AttributeTypeAndValue");
514 DERValue val = der.read();
515 if (val.getTag() != DER.OBJECT_IDENTIFIER)
516 throw new IOException("badly formed AttributeTypeAndValue");
517 OID oid = (OID) val.getValue();
518 val = der.read();
519 if (!(val.getValue() instanceof String))
520 throw new IOException("badly formed AttributeTypeAndValue");
521 String value = (String) val.getValue();
522 putComponent(oid, value);
523 len2 += atav.getEncodedLength();
525 len += rdn.getEncodedLength();
526 if (len < name.getLength())
527 newRelativeDistinguishedName();
529 setUnmodifiable();
532 private static String compressWS(String str)
534 StringBuffer buf = new StringBuffer();
535 char lastChar = 0;
536 for (int i = 0; i < str.length(); i++)
538 char c = str.charAt(i);
539 if (Character.isWhitespace(c))
541 if (!Character.isWhitespace(lastChar))
542 buf.append(' ');
544 else
545 buf.append(c);
546 lastChar = c;
548 return buf.toString().trim();