Merge from mainline (gomp-merge-2005-02-26).
[official-gcc.git] / libjava / javax / security / auth / x500 / X500Principal.java
blobe9032ffaf7838b85eceb586d7c564a79baa54381
1 /* X500Principal.java -- X.500 principal.
2 Copyright (C) 2003, 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 javax.security.auth.x500;
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.ByteArrayInputStream;
47 import java.io.EOFException;
48 import java.io.IOException;
49 import java.io.InputStream;
50 import java.io.NotActiveException;
51 import java.io.ObjectInputStream;
52 import java.io.ObjectOutputStream;
53 import java.io.Reader;
54 import java.io.Serializable;
55 import java.io.StringReader;
57 import java.security.Principal;
59 import java.util.ArrayList;
60 import java.util.HashSet;
61 import java.util.Iterator;
62 import java.util.LinkedHashMap;
63 import java.util.LinkedList;
64 import java.util.List;
65 import java.util.Locale;
66 import java.util.Map;
67 import java.util.Set;
69 public final class X500Principal implements Principal, Serializable
71 private static final long serialVersionUID = -500463348111345721L;
73 // Constants and fields.
74 // ------------------------------------------------------------------------
76 public static final String CANONICAL = "CANONICAL";
77 public static final String RFC1779 = "RFC1779";
78 public static final String RFC2253 = "RFC2253";
80 private static final OID CN = new OID("2.5.4.3");
81 private static final OID C = new OID("2.5.4.6");
82 private static final OID L = new OID("2.5.4.7");
83 private static final OID ST = new OID("2.5.4.8");
84 private static final OID STREET = new OID("2.5.4.9");
85 private static final OID O = new OID("2.5.4.10");
86 private static final OID OU = new OID("2.5.4.11");
87 private static final OID DC = new OID("0.9.2342.19200300.100.1.25");
88 private static final OID UID = new OID("0.9.2342.19200300.100.1.1");
90 private transient List components;
91 private transient Map currentRdn;
92 private transient boolean fixed;
93 private transient byte[] encoded;
95 // Constructors.
96 // ------------------------------------------------------------------------
98 private X500Principal()
100 components = new LinkedList();
101 currentRdn = new LinkedHashMap();
102 components.add (currentRdn);
105 public X500Principal (String name)
107 this();
108 if (name == null)
109 throw new NullPointerException();
112 parseString (name);
114 catch (IOException ioe)
116 IllegalArgumentException iae = new IllegalArgumentException("malformed name");
117 iae.initCause (ioe);
118 throw iae;
122 public X500Principal (byte[] encoded)
124 this(new ByteArrayInputStream (encoded));
127 public X500Principal (InputStream encoded)
129 this();
132 parseDer (encoded);
134 catch (IOException ioe)
136 throw new IllegalArgumentException (ioe.toString());
140 // Instance methods.
141 // ------------------------------------------------------------------------
143 public boolean equals(Object o)
145 if (!(o instanceof X500Principal))
146 return false;
147 if (size() != ((X500Principal) o).size())
148 return false;
149 for (int i = 0; i < size(); i++)
151 Map m = (Map) components.get (i);
152 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
154 Map.Entry e = (Map.Entry) it2.next();
155 OID oid = (OID) e.getKey();
156 String v1 = (String) e.getValue();
157 String v2 = ((X500Principal) o).getComponent (oid, i);
158 if (v2 == null)
159 return false;
160 if (!compressWS (v1).equalsIgnoreCase (compressWS (v2)))
161 return false;
164 return true;
167 public byte[] getEncoded()
169 if (encoded == null)
170 encodeDer();
171 return (byte[]) encoded.clone();
174 public String getName()
176 return getName (RFC2253);
179 public String getName (final String format)
181 boolean rfc2253 = RFC2253.equalsIgnoreCase (format) ||
182 CANONICAL.equalsIgnoreCase (format);
183 boolean rfc1779 = RFC1779.equalsIgnoreCase (format);
184 boolean canon = CANONICAL.equalsIgnoreCase (format);
185 if (! (rfc2253 || rfc1779 || canon))
186 throw new IllegalArgumentException ("unsupported format " + format);
187 StringBuffer str = new StringBuffer();
188 for (Iterator it = components.iterator(); it.hasNext(); )
190 Map m = (Map) it.next();
191 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
193 Map.Entry entry = (Map.Entry) it2.next();
194 OID oid = (OID) entry.getKey();
195 String value = (String) entry.getValue();
196 if (oid.equals (CN))
197 str.append ("CN");
198 else if (oid.equals (C))
199 str.append ("C");
200 else if (oid.equals (L))
201 str.append ("L");
202 else if (oid.equals (ST))
203 str.append ("ST");
204 else if (oid.equals (STREET))
205 str.append ("STREET");
206 else if (oid.equals (O))
207 str.append ("O");
208 else if (oid.equals (OU))
209 str.append ("OU");
210 else if (oid.equals (DC) && rfc2253)
211 str.append ("DC");
212 else if (oid.equals ("UID") && rfc2253)
213 str.append ("UID");
214 else
215 str.append (oid.toString());
216 str.append('=');
217 str.append(value);
218 if (it2.hasNext())
219 str.append('+');
221 if (it.hasNext())
222 str.append(',');
224 if (canon)
225 return str.toString().toUpperCase (Locale.US).toLowerCase (Locale.US);
226 return str.toString();
229 public String toString()
231 return getName (RFC2253);
234 // Serialization methods.
235 // ------------------------------------------------------------------------
237 private void writeObject (ObjectOutputStream out) throws IOException
239 if (encoded != null)
240 encodeDer();
241 out.writeObject (encoded);
244 private void readObject (ObjectInputStream in)
245 throws IOException, NotActiveException, ClassNotFoundException
247 byte[] buf = (byte[]) in.readObject();
248 parseDer (new ByteArrayInputStream (buf));
251 // Own methods.
252 // -------------------------------------------------------------------------
254 private int size()
256 return components.size();
259 private String getComponent(OID oid, int rdn)
261 if (rdn >= size())
262 return null;
263 return (String) ((Map) components.get (rdn)).get (oid);
266 private void encodeDer()
268 ArrayList name = new ArrayList(components.size());
269 for (Iterator it = components.iterator(); it.hasNext(); )
271 Map m = (Map) it.next();
272 if (m.isEmpty())
273 continue;
274 Set rdn = new HashSet();
275 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
277 Map.Entry e = (Map.Entry) it.next();
278 ArrayList atav = new ArrayList(2);
279 atav.add(new DERValue(DER.OBJECT_IDENTIFIER, e.getKey()));
280 atav.add(new DERValue(DER.UTF8_STRING, e.getValue()));
281 rdn.add(new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, atav));
283 name.add(new DERValue(DER.SET|DER.CONSTRUCTED, rdn));
285 DERValue val = new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, name);
286 encoded = val.getEncoded();
289 private int sep;
291 private void parseString(String str) throws IOException
293 Reader in = new StringReader(str);
294 while (true)
296 String key = readAttributeType(in);
297 if (key == null)
298 break;
299 String value = readAttributeValue(in);
300 putComponent(key, value);
301 if (sep == ',')
302 newRelativeDistinguishedName();
306 private String readAttributeType(Reader in) throws IOException
308 StringBuffer buf = new StringBuffer();
309 int ch;
310 while ((ch = in.read()) != '=')
312 if (ch == -1)
314 if (buf.length() > 0)
315 throw new EOFException();
316 return null;
318 if (ch > 127)
319 throw new IOException("Invalid char: " + (char) ch);
320 if (Character.isLetterOrDigit((char) ch) || ch == '-' || ch == '.')
321 buf.append((char) ch);
322 else
323 throw new IOException("Invalid char: " + (char) ch);
325 return buf.toString();
328 private String readAttributeValue(Reader in) throws IOException
330 StringBuffer buf = new StringBuffer();
331 int ch = in.read();
332 if (ch == '#')
334 while (true)
336 ch = in.read();
337 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
338 || Character.isDigit((char) ch))
339 buf.append((char) ch);
340 else if (ch == '+' || ch == ',')
342 sep = ch;
343 String hex = buf.toString();
344 return new String(toByteArray(hex));
346 else
347 throw new IOException("illegal character: " + (char) ch);
350 else if (ch == '"')
352 while (true)
354 ch = in.read();
355 if (ch == '"')
356 break;
357 else if (ch == '\\')
359 ch = in.read();
360 if (ch == -1)
361 throw new EOFException();
362 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
363 || Character.isDigit((char) ch))
365 int i = Character.digit((char) ch, 16) << 4;
366 ch = in.read();
367 if (!(('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
368 || Character.isDigit((char) ch)))
369 throw new IOException("illegal hex char");
370 i |= Character.digit((char) ch, 16);
371 buf.append((char) i);
373 else
374 buf.append((char) ch);
376 else
377 buf.append((char) ch);
379 sep = in.read();
380 if (sep != '+' || sep != ',')
381 throw new IOException("illegal character: " + (char) ch);
382 return buf.toString();
384 else
386 while (true)
388 switch (ch)
390 case '+':
391 case ',':
392 sep = ch;
393 return buf.toString();
394 case '\\':
395 ch = in.read();
396 if (ch == -1)
397 throw new EOFException();
398 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
399 || Character.isDigit((char) ch))
401 int i = Character.digit((char) ch, 16) << 4;
402 ch = in.read();
403 if (!(('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
404 || Character.isDigit((char) ch)))
405 throw new IOException("illegal hex char");
406 i |= Character.digit((char) ch, 16);
407 buf.append((char) i);
409 else
410 buf.append((char) ch);
411 break;
412 case '=':
413 case '<':
414 case '>':
415 case '#':
416 case ';':
417 throw new IOException("illegal character: " + (char) ch);
418 case -1:
419 throw new EOFException();
420 default:
421 buf.append((char) ch);
427 private void parseDer (InputStream encoded) throws IOException
429 DERReader der = new DERReader (encoded);
430 DERValue name = der.read();
431 if (!name.isConstructed())
432 throw new IOException ("malformed Name");
433 this.encoded = name.getEncoded();
434 int len = 0;
435 while (len < name.getLength())
437 DERValue rdn = der.read();
438 if (!rdn.isConstructed())
439 throw new IOException ("badly formed RDNSequence");
440 int len2 = 0;
441 while (len2 < rdn.getLength())
443 DERValue atav = der.read();
444 if (!atav.isConstructed())
445 throw new IOException ("badly formed AttributeTypeAndValue");
446 DERValue val = der.read();
447 if (val.getTag() != DER.OBJECT_IDENTIFIER)
448 throw new IOException ("badly formed AttributeTypeAndValue");
449 OID oid = (OID) val.getValue();
450 val = der.read();
451 if (!(val.getValue() instanceof String))
452 throw new IOException ("badly formed AttributeTypeAndValue");
453 String value = (String) val.getValue();
454 putComponent(oid, value);
455 len2 += atav.getEncodedLength();
457 len += rdn.getEncodedLength();
458 if (len < name.getLength())
459 newRelativeDistinguishedName();
463 private void newRelativeDistinguishedName()
465 currentRdn = new LinkedHashMap();
466 components.add(currentRdn);
469 private void putComponent(OID oid, String value)
471 currentRdn.put(oid, value);
474 private void putComponent(String name, String value)
476 name = name.trim().toLowerCase();
477 if (name.equals("cn"))
478 putComponent(CN, value);
479 else if (name.equals("c"))
480 putComponent(C, value);
481 else if (name.equals("l"))
482 putComponent(L, value);
483 else if (name.equals("street"))
484 putComponent(STREET, value);
485 else if (name.equals("st"))
486 putComponent(ST, value);
487 else if (name.equals("dc"))
488 putComponent(DC, value);
489 else if (name.equals("uid"))
490 putComponent(UID, value);
491 else
492 putComponent(new OID(name), value);
495 private static String compressWS(String str)
497 StringBuffer buf = new StringBuffer();
498 char lastChar = 0;
499 for (int i = 0; i < str.length(); i++)
501 char c = str.charAt(i);
502 if (Character.isWhitespace(c))
504 if (!Character.isWhitespace(lastChar))
505 buf.append(' ');
507 else
508 buf.append(c);
509 lastChar = c;
511 return buf.toString().trim();
514 private static byte[] toByteArray (String str)
516 int limit = str.length();
517 byte[] result = new byte[((limit + 1) / 2)];
518 int i = 0, j = 0;
519 if ((limit % 2) == 1)
521 result[j++] = (byte) Character.digit (str.charAt(i++), 16);
523 while (i < limit)
525 result[j ] = (byte) (Character.digit (str.charAt(i++), 16) << 4);
526 result[j++] |= (byte) Character.digit (str.charAt(i++), 16);
528 return result;