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)
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
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
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.BitString;
43 import gnu.java.security.der.DER;
44 import gnu.java.security.der.DEREncodingException;
45 import gnu.java.security.der.DERReader;
46 import gnu.java.security.der.DERValue;
48 import java.io.ByteArrayInputStream;
49 import java.io.IOException;
50 import java.io.InputStream;
51 import java.io.EOFException;
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.Collections;
63 import java.util.HashSet;
64 import java.util.Iterator;
65 import java.util.LinkedHashMap;
66 import java.util.LinkedList;
67 import java.util.List;
68 import java.util.Locale;
71 import java.util.TreeMap;
73 public final class X500Principal implements Principal, Serializable
75 private static final long serialVersionUID = -500463348111345721L;
77 // Constants and fields.
78 // ------------------------------------------------------------------------
80 public static final String CANONICAL = "CANONICAL";
81 public static final String RFC1779 = "RFC1779";
82 public static final String RFC2253 = "RFC2253";
84 private static final OID CN = new OID("2.5.4.3");
85 private static final OID C = new OID("2.5.4.6");
86 private static final OID L = new OID("2.5.4.7");
87 private static final OID ST = new OID("2.5.4.8");
88 private static final OID STREET = new OID("2.5.4.9");
89 private static final OID O = new OID("2.5.4.10");
90 private static final OID OU = new OID("2.5.4.11");
91 private static final OID DC = new OID("0.9.2342.19200300.100.1.25");
92 private static final OID UID = new OID("0.9.2342.19200300.100.1.1");
94 private transient List components;
95 private transient Map currentRdn;
96 private transient boolean fixed;
97 private transient byte[] encoded;
100 // ------------------------------------------------------------------------
102 private X500Principal()
104 components = new LinkedList();
105 currentRdn = new LinkedHashMap();
106 components.add (currentRdn);
109 public X500Principal (String name)
113 throw new NullPointerException();
118 catch (IOException ioe)
120 IllegalArgumentException iae = new IllegalArgumentException("malformed name");
126 public X500Principal (byte[] encoded)
128 this(new ByteArrayInputStream (encoded));
131 public X500Principal (InputStream encoded)
138 catch (IOException ioe)
140 throw new IllegalArgumentException (ioe.toString());
145 // ------------------------------------------------------------------------
147 public boolean equals(Object o)
149 if (!(o instanceof X500Principal))
151 if (size() != ((X500Principal) o).size())
153 for (int i = 0; i < size(); i++)
155 Map m = (Map) components.get (i);
156 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
158 Map.Entry e = (Map.Entry) it2.next();
159 OID oid = (OID) e.getKey();
160 String v1 = (String) e.getValue();
161 String v2 = ((X500Principal) o).getComponent (oid, i);
164 if (!compressWS (v1).equalsIgnoreCase (compressWS (v2)))
171 public byte[] getEncoded()
175 return (byte[]) encoded.clone();
178 public String getName()
180 return getName (RFC2253);
183 public String getName (final String format)
185 boolean rfc2253 = RFC2253.equalsIgnoreCase (format) ||
186 CANONICAL.equalsIgnoreCase (format);
187 boolean rfc1779 = RFC1779.equalsIgnoreCase (format);
188 boolean canon = CANONICAL.equalsIgnoreCase (format);
189 if (! (rfc2253 || rfc1779 || canon))
190 throw new IllegalArgumentException ("unsupported format " + format);
191 StringBuffer str = new StringBuffer();
192 for (Iterator it = components.iterator(); it.hasNext(); )
194 Map m = (Map) it.next();
195 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
197 Map.Entry entry = (Map.Entry) it2.next();
198 OID oid = (OID) entry.getKey();
199 String value = (String) entry.getValue();
202 else if (oid.equals (C))
204 else if (oid.equals (L))
206 else if (oid.equals (ST))
208 else if (oid.equals (STREET))
209 str.append ("STREET");
210 else if (oid.equals (O))
212 else if (oid.equals (OU))
214 else if (oid.equals (DC) && rfc2253)
216 else if (oid.equals ("UID") && rfc2253)
219 str.append (oid.toString());
229 return str.toString().toUpperCase (Locale.US).toLowerCase (Locale.US);
230 return str.toString();
233 public String toString()
235 return getName (RFC2253);
238 // Serialization methods.
239 // ------------------------------------------------------------------------
241 private void writeObject (ObjectOutputStream out) throws IOException
245 out.writeObject (encoded);
248 private void readObject (ObjectInputStream in)
249 throws IOException, NotActiveException, ClassNotFoundException
251 byte[] buf = (byte[]) in.readObject();
252 parseDer (new ByteArrayInputStream (buf));
256 // -------------------------------------------------------------------------
260 return components.size();
263 private String getComponent(OID oid, int rdn)
267 return (String) ((Map) components.get (rdn)).get (oid);
270 private void encodeDer()
272 ArrayList name = new ArrayList(components.size());
273 for (Iterator it = components.iterator(); it.hasNext(); )
275 Map m = (Map) it.next();
278 Set rdn = new HashSet();
279 for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
281 Map.Entry e = (Map.Entry) it.next();
282 ArrayList atav = new ArrayList(2);
283 atav.add(new DERValue(DER.OBJECT_IDENTIFIER, e.getKey()));
284 atav.add(new DERValue(DER.UTF8_STRING, e.getValue()));
285 rdn.add(new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, atav));
287 name.add(new DERValue(DER.SET|DER.CONSTRUCTED, rdn));
289 DERValue val = new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, name);
290 encoded = val.getEncoded();
295 private void parseString(String str) throws IOException
297 Reader in = new StringReader(str);
300 String key = readAttributeType(in);
303 String value = readAttributeValue(in);
304 putComponent(key, value);
306 newRelativeDistinguishedName();
310 private String readAttributeType(Reader in) throws IOException
312 StringBuffer buf = new StringBuffer();
314 while ((ch = in.read()) != '=')
318 if (buf.length() > 0)
319 throw new EOFException();
323 throw new IOException("Invalid char: " + (char) ch);
324 if (Character.isLetterOrDigit((char) ch) || ch == '-' || ch == '.')
325 buf.append((char) ch);
327 throw new IOException("Invalid char: " + (char) ch);
329 return buf.toString();
332 private String readAttributeValue(Reader in) throws IOException
334 StringBuffer buf = new StringBuffer();
341 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
342 || Character.isDigit((char) ch))
343 buf.append((char) ch);
344 else if (ch == '+' || ch == ',')
347 String hex = buf.toString();
348 return new String(toByteArray(hex));
351 throw new IOException("illegal character: " + (char) ch);
365 throw new EOFException();
366 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
367 || Character.isDigit((char) ch))
369 int i = Character.digit((char) ch, 16) << 4;
371 if (!(('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
372 || Character.isDigit((char) ch)))
373 throw new IOException("illegal hex char");
374 i |= Character.digit((char) ch, 16);
375 buf.append((char) i);
378 buf.append((char) ch);
381 buf.append((char) ch);
384 if (sep != '+' || sep != ',')
385 throw new IOException("illegal character: " + (char) ch);
386 return buf.toString();
397 return buf.toString();
401 throw new EOFException();
402 if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
403 || Character.isDigit((char) ch))
405 int i = Character.digit((char) ch, 16) << 4;
407 if (!(('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
408 || Character.isDigit((char) ch)))
409 throw new IOException("illegal hex char");
410 i |= Character.digit((char) ch, 16);
411 buf.append((char) i);
414 buf.append((char) ch);
421 throw new IOException("illegal character: " + (char) ch);
423 throw new EOFException();
425 buf.append((char) ch);
431 private void parseDer (InputStream encoded) throws IOException
433 DERReader der = new DERReader (encoded);
434 DERValue name = der.read();
435 if (!name.isConstructed())
436 throw new IOException ("malformed Name");
437 this.encoded = name.getEncoded();
439 while (len < name.getLength())
441 DERValue rdn = der.read();
442 if (!rdn.isConstructed())
443 throw new IOException ("badly formed RDNSequence");
445 while (len2 < rdn.getLength())
447 DERValue atav = der.read();
448 if (!atav.isConstructed())
449 throw new IOException ("badly formed AttributeTypeAndValue");
450 DERValue val = der.read();
451 if (val.getTag() != DER.OBJECT_IDENTIFIER)
452 throw new IOException ("badly formed AttributeTypeAndValue");
453 OID oid = (OID) val.getValue();
455 if (!(val.getValue() instanceof String))
456 throw new IOException ("badly formed AttributeTypeAndValue");
457 String value = (String) val.getValue();
458 putComponent(oid, value);
459 len2 += atav.getEncodedLength();
461 len += rdn.getEncodedLength();
462 if (len < name.getLength())
463 newRelativeDistinguishedName();
467 private void newRelativeDistinguishedName()
469 currentRdn = new LinkedHashMap();
470 components.add(currentRdn);
473 private void putComponent(OID oid, String value)
475 currentRdn.put(oid, value);
478 private void putComponent(String name, String value)
480 name = name.trim().toLowerCase();
481 if (name.equals("cn"))
482 putComponent(CN, value);
483 else if (name.equals("c"))
484 putComponent(C, value);
485 else if (name.equals("l"))
486 putComponent(L, value);
487 else if (name.equals("street"))
488 putComponent(STREET, value);
489 else if (name.equals("st"))
490 putComponent(ST, value);
491 else if (name.equals("dc"))
492 putComponent(DC, value);
493 else if (name.equals("uid"))
494 putComponent(UID, value);
496 putComponent(new OID(name), value);
499 private static String compressWS(String str)
501 StringBuffer buf = new StringBuffer();
503 for (int i = 0; i < str.length(); i++)
505 char c = str.charAt(i);
506 if (Character.isWhitespace(c))
508 if (!Character.isWhitespace(lastChar))
515 return buf.toString().trim();
518 private static byte[] toByteArray (String str)
520 int limit = str.length();
521 byte[] result = new byte[((limit + 1) / 2)];
523 if ((limit % 2) == 1)
525 result[j++] = (byte) Character.digit (str.charAt(i++), 16);
529 result[j ] = (byte) (Character.digit (str.charAt(i++), 16) << 4);
530 result[j++] |= (byte) Character.digit (str.charAt(i++), 16);