libjava/ChangeLog:
[official-gcc.git] / libjava / classpath / javax / security / auth / x500 / X500Principal.java
blob0a1e8c665182b771cfed51820ba6ee7b8033b2a3
1 /* X500Principal.java -- X.500 principal.
2 Copyright (C) 2003, 2004, 2005 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 javax.security.auth.x500;
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.ByteArrayInputStream;
49 import java.io.EOFException;
50 import java.io.IOException;
51 import java.io.InputStream;
52 import java.io.NotActiveException;
53 import java.io.ObjectInputStream;
54 import java.io.ObjectOutputStream;
55 import java.io.Reader;
56 import java.io.Serializable;
57 import java.io.StringReader;
59 import java.security.Principal;
61 import java.util.ArrayList;
62 import java.util.HashSet;
63 import java.util.Iterator;
64 import java.util.LinkedHashMap;
65 import java.util.LinkedList;
66 import java.util.List;
67 import java.util.Locale;
68 import java.util.Map;
69 import java.util.Set;
71 public final class X500Principal implements Principal, Serializable
73 private static final long serialVersionUID = -500463348111345721L;
75 // Constants and fields.
76 // ------------------------------------------------------------------------
78 public static final String CANONICAL = "CANONICAL";
79 public static final String RFC1779 = "RFC1779";
80 public static final String RFC2253 = "RFC2253";
82 private static final OID CN = new OID("2.5.4.3");
83 private static final OID C = new OID("2.5.4.6");
84 private static final OID L = new OID("2.5.4.7");
85 private static final OID ST = new OID("2.5.4.8");
86 private static final OID STREET = new OID("2.5.4.9");
87 private static final OID O = new OID("2.5.4.10");
88 private static final OID OU = new OID("2.5.4.11");
89 private static final OID DC = new OID("0.9.2342.19200300.100.1.25");
90 private static final OID UID = new OID("0.9.2342.19200300.100.1.1");
92 private transient List components;
93 private transient Map currentRdn;
94 private transient boolean fixed;
95 private transient byte[] encoded;
97 // Constructors.
98 // ------------------------------------------------------------------------
100 private X500Principal()
102 components = new LinkedList();
103 currentRdn = new LinkedHashMap();
104 components.add (currentRdn);
107 public X500Principal (String name)
109 this();
110 if (name == null)
111 throw new NullPointerException();
114 parseString (name);
116 catch (IOException ioe)
118 IllegalArgumentException iae = new IllegalArgumentException("malformed name");
119 iae.initCause (ioe);
120 throw iae;
124 public X500Principal (byte[] encoded)
126 this(new ByteArrayInputStream (encoded));
129 public X500Principal (InputStream encoded)
131 this();
134 parseDer (encoded);
136 catch (IOException ioe)
138 throw new IllegalArgumentException (ioe.toString());
142 // Instance methods.
143 // ------------------------------------------------------------------------
145 public int hashCode()
147 int result = size();
148 for (int i = 0; i < size(); ++i)
150 Map m = (Map) components.get(i);
151 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
153 Map.Entry e = (Map.Entry) it2.next();
154 // We don't bother looking at the value of the entry.
155 result = result * 31 + ((OID) e.getKey()).hashCode();
158 return result;
161 public boolean equals(Object o)
163 if (!(o instanceof X500Principal))
164 return false;
165 if (size() != ((X500Principal) o).size())
166 return false;
167 for (int i = 0; i < size(); i++)
169 Map m = (Map) components.get (i);
170 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
172 Map.Entry e = (Map.Entry) it2.next();
173 OID oid = (OID) e.getKey();
174 String v1 = (String) e.getValue();
175 String v2 = ((X500Principal) o).getComponent (oid, i);
176 if (v2 == null)
177 return false;
178 if (!compressWS (v1).equalsIgnoreCase (compressWS (v2)))
179 return false;
182 return true;
185 public byte[] getEncoded()
187 if (encoded == null)
188 encodeDer();
189 return (byte[]) encoded.clone();
192 public String getName()
194 return getName (RFC2253);
197 public String getName (final String format)
199 boolean rfc2253 = RFC2253.equalsIgnoreCase (format) ||
200 CANONICAL.equalsIgnoreCase (format);
201 boolean rfc1779 = RFC1779.equalsIgnoreCase (format);
202 boolean canon = CANONICAL.equalsIgnoreCase (format);
203 if (! (rfc2253 || rfc1779 || canon))
204 throw new IllegalArgumentException ("unsupported format " + format);
205 CPStringBuilder str = new CPStringBuilder();
206 for (Iterator it = components.iterator(); it.hasNext(); )
208 Map m = (Map) it.next();
209 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
211 Map.Entry entry = (Map.Entry) it2.next();
212 OID oid = (OID) entry.getKey();
213 String value = (String) entry.getValue();
214 if (oid.equals (CN))
215 str.append ("CN");
216 else if (oid.equals (C))
217 str.append ("C");
218 else if (oid.equals (L))
219 str.append ("L");
220 else if (oid.equals (ST))
221 str.append ("ST");
222 else if (oid.equals (STREET))
223 str.append ("STREET");
224 else if (oid.equals (O))
225 str.append ("O");
226 else if (oid.equals (OU))
227 str.append ("OU");
228 else if (oid.equals (DC) && rfc2253)
229 str.append ("DC");
230 else if (oid.equals (UID) && rfc2253)
231 str.append ("UID");
232 else
233 str.append (oid.toString());
234 str.append('=');
235 str.append(value);
236 if (it2.hasNext())
237 str.append('+');
239 if (it.hasNext())
240 str.append(',');
242 if (canon)
243 return str.toString().toUpperCase (Locale.US).toLowerCase (Locale.US);
244 return str.toString();
247 public String toString()
249 return getName (RFC2253);
252 // Serialization methods.
253 // ------------------------------------------------------------------------
255 private void writeObject (ObjectOutputStream out) throws IOException
257 if (encoded != null)
258 encodeDer();
259 out.writeObject (encoded);
262 private void readObject (ObjectInputStream in)
263 throws IOException, NotActiveException, ClassNotFoundException
265 byte[] buf = (byte[]) in.readObject();
266 parseDer (new ByteArrayInputStream (buf));
269 // Own methods.
270 // -------------------------------------------------------------------------
272 private int size()
274 return components.size();
277 private String getComponent(OID oid, int rdn)
279 if (rdn >= size())
280 return null;
281 return (String) ((Map) components.get (rdn)).get (oid);
284 private void encodeDer()
286 ArrayList name = new ArrayList(components.size());
287 for (Iterator it = components.iterator(); it.hasNext(); )
289 Map m = (Map) it.next();
290 if (m.isEmpty())
291 continue;
292 Set rdn = new HashSet();
293 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
295 Map.Entry e = (Map.Entry) it2.next();
296 ArrayList atav = new ArrayList(2);
297 atav.add(new DERValue(DER.OBJECT_IDENTIFIER, e.getKey()));
298 atav.add(new DERValue(DER.UTF8_STRING, e.getValue()));
299 rdn.add(new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, atav));
301 name.add(new DERValue(DER.SET|DER.CONSTRUCTED, rdn));
303 DERValue val = new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, name);
304 encoded = val.getEncoded();
307 private int sep;
309 private void parseString(String str) throws IOException
311 Reader in = new StringReader(str);
312 while (true)
314 String key = readAttributeType(in);
315 if (key == null)
316 break;
317 String value = readAttributeValue(in);
318 putComponent(key, value);
319 if (sep == ',')
320 newRelativeDistinguishedName();
321 if (sep == -1)
322 break;
326 private String readAttributeType(Reader in) throws IOException
328 CPStringBuilder buf = new CPStringBuilder();
329 int ch;
330 while ((ch = in.read()) != '=')
332 if (ch == -1)
334 if (buf.length() > 0)
335 throw new EOFException("partial name read: " + buf);
336 return null;
338 if (ch > 127)
339 throw new IOException("Invalid char: " + (char) ch);
340 if (Character.isLetterOrDigit((char) ch) || ch == '-' || ch == '.')
341 buf.append((char) ch);
342 else
343 throw new IOException("Invalid char: " + (char) ch);
345 return buf.toString();
348 private String readAttributeValue(Reader in) throws IOException
350 CPStringBuilder buf = new CPStringBuilder();
351 int ch = in.read();
352 if (ch == '#')
354 while (true)
356 ch = in.read();
357 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
358 || Character.isDigit((char) ch))
359 buf.append((char) ch);
360 else if (ch == '+' || ch == ',')
362 sep = ch;
363 String hex = buf.toString();
364 return new String(toByteArray(hex));
366 else
367 throw new IOException("illegal character: " + (char) ch);
370 else if (ch == '"')
372 while (true)
374 ch = in.read();
375 if (ch == '"')
376 break;
377 else if (ch == '\\')
379 ch = in.read();
380 if (ch == -1)
381 throw new EOFException();
382 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
383 || Character.isDigit((char) ch))
385 int i = Character.digit((char) ch, 16) << 4;
386 ch = in.read();
387 if (!(('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
388 || Character.isDigit((char) ch)))
389 throw new IOException("illegal hex char");
390 i |= Character.digit((char) ch, 16);
391 buf.append((char) i);
393 else
394 buf.append((char) ch);
396 else
397 buf.append((char) ch);
399 sep = in.read();
400 if (sep != '+' && sep != ',')
401 throw new IOException("illegal character: " + (char) ch);
402 return buf.toString();
404 else
406 while (true)
408 switch (ch)
410 case '+':
411 case ',':
412 sep = ch;
413 return buf.toString();
414 case '\\':
415 ch = in.read();
416 if (ch == -1)
417 throw new EOFException();
418 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
419 || Character.isDigit((char) ch))
421 int i = Character.digit((char) ch, 16) << 4;
422 ch = in.read();
423 if (!(('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
424 || Character.isDigit((char) ch)))
425 throw new IOException("illegal hex char");
426 i |= Character.digit((char) ch, 16);
427 buf.append((char) i);
429 else
430 buf.append((char) ch);
431 break;
432 case '=':
433 case '<':
434 case '>':
435 case '#':
436 case ';':
437 throw new IOException("illegal character: " + (char) ch);
438 case -1:
439 sep = -1;
440 return buf.toString ();
441 default:
442 buf.append((char) ch);
444 ch = in.read ();
449 private void parseDer (InputStream encoded) throws IOException
451 DERReader der = new DERReader (encoded);
452 DERValue name = der.read();
453 if (!name.isConstructed())
454 throw new IOException ("malformed Name");
455 this.encoded = name.getEncoded();
456 int len = 0;
457 while (len < name.getLength())
459 DERValue rdn = der.read();
460 if (!rdn.isConstructed())
461 throw new IOException ("badly formed RDNSequence");
462 int len2 = 0;
463 while (len2 < rdn.getLength())
465 DERValue atav = der.read();
466 if (!atav.isConstructed())
467 throw new IOException ("badly formed AttributeTypeAndValue");
468 DERValue val = der.read();
469 if (val.getTag() != DER.OBJECT_IDENTIFIER)
470 throw new IOException ("badly formed AttributeTypeAndValue");
471 OID oid = (OID) val.getValue();
472 val = der.read();
473 if (!(val.getValue() instanceof String))
474 throw new IOException ("badly formed AttributeTypeAndValue");
475 String value = (String) val.getValue();
476 putComponent(oid, value);
477 len2 += atav.getEncodedLength();
479 len += rdn.getEncodedLength();
480 if (len < name.getLength())
481 newRelativeDistinguishedName();
485 private void newRelativeDistinguishedName()
487 currentRdn = new LinkedHashMap();
488 components.add(currentRdn);
491 private void putComponent(OID oid, String value)
493 currentRdn.put(oid, value);
496 private void putComponent(String name, String value)
498 name = name.trim().toLowerCase();
499 if (name.equals("cn"))
500 putComponent(CN, value);
501 else if (name.equals("c"))
502 putComponent(C, value);
503 else if (name.equals("l"))
504 putComponent(L, value);
505 else if (name.equals("street"))
506 putComponent(STREET, value);
507 else if (name.equals("st"))
508 putComponent(ST, value);
509 else if (name.equals ("o"))
510 putComponent (O, value);
511 else if (name.equals ("ou"))
512 putComponent (OU, value);
513 else if (name.equals("dc"))
514 putComponent(DC, value);
515 else if (name.equals("uid"))
516 putComponent(UID, value);
517 else
518 putComponent(new OID(name), value);
521 private static String compressWS(String str)
523 CPStringBuilder buf = new CPStringBuilder();
524 char lastChar = 0;
525 for (int i = 0; i < str.length(); i++)
527 char c = str.charAt(i);
528 if (Character.isWhitespace(c))
530 if (!Character.isWhitespace(lastChar))
531 buf.append(' ');
533 else
534 buf.append(c);
535 lastChar = c;
537 return buf.toString().trim();
540 private static byte[] toByteArray (String str)
542 int limit = str.length();
543 byte[] result = new byte[((limit + 1) / 2)];
544 int i = 0, j = 0;
545 if ((limit % 2) == 1)
547 result[j++] = (byte) Character.digit (str.charAt(i++), 16);
549 while (i < limit)
551 result[j ] = (byte) (Character.digit (str.charAt(i++), 16) << 4);
552 result[j++] |= (byte) Character.digit (str.charAt(i++), 16);
554 return result;