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)
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
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
.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
;
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
;
98 // ------------------------------------------------------------------------
100 private X500Principal()
102 components
= new LinkedList();
103 currentRdn
= new LinkedHashMap();
104 components
.add (currentRdn
);
107 public X500Principal (String name
)
111 throw new NullPointerException();
116 catch (IOException ioe
)
118 IllegalArgumentException iae
= new IllegalArgumentException("malformed name");
124 public X500Principal (byte[] encoded
)
126 this(new ByteArrayInputStream (encoded
));
129 public X500Principal (InputStream encoded
)
136 catch (IOException ioe
)
138 throw new IllegalArgumentException (ioe
.toString());
143 // ------------------------------------------------------------------------
145 public int hashCode()
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();
161 public boolean equals(Object o
)
163 if (!(o
instanceof X500Principal
))
165 if (size() != ((X500Principal
) o
).size())
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
);
178 if (!compressWS (v1
).equalsIgnoreCase (compressWS (v2
)))
185 public byte[] getEncoded()
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();
216 else if (oid
.equals (C
))
218 else if (oid
.equals (L
))
220 else if (oid
.equals (ST
))
222 else if (oid
.equals (STREET
))
223 str
.append ("STREET");
224 else if (oid
.equals (O
))
226 else if (oid
.equals (OU
))
228 else if (oid
.equals (DC
) && rfc2253
)
230 else if (oid
.equals (UID
) && rfc2253
)
233 str
.append (oid
.toString());
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
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
));
270 // -------------------------------------------------------------------------
274 return components
.size();
277 private String
getComponent(OID oid
, int rdn
)
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();
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();
309 private void parseString(String str
) throws IOException
311 Reader in
= new StringReader(str
);
314 String key
= readAttributeType(in
);
317 String value
= readAttributeValue(in
);
318 putComponent(key
, value
);
320 newRelativeDistinguishedName();
326 private String
readAttributeType(Reader in
) throws IOException
328 CPStringBuilder buf
= new CPStringBuilder();
330 while ((ch
= in
.read()) != '=')
334 if (buf
.length() > 0)
335 throw new EOFException("partial name read: " + buf
);
339 throw new IOException("Invalid char: " + (char) ch
);
340 if (Character
.isLetterOrDigit((char) ch
) || ch
== '-' || ch
== '.')
341 buf
.append((char) ch
);
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();
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
== ',')
363 String hex
= buf
.toString();
364 return new String(toByteArray(hex
));
367 throw new IOException("illegal character: " + (char) ch
);
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;
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
);
394 buf
.append((char) ch
);
397 buf
.append((char) ch
);
400 if (sep
!= '+' && sep
!= ',')
401 throw new IOException("illegal character: " + (char) ch
);
402 return buf
.toString();
413 return buf
.toString();
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;
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
);
430 buf
.append((char) ch
);
437 throw new IOException("illegal character: " + (char) ch
);
440 return buf
.toString ();
442 buf
.append((char) ch
);
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();
457 while (len
< name
.getLength())
459 DERValue rdn
= der
.read();
460 if (!rdn
.isConstructed())
461 throw new IOException ("badly formed RDNSequence");
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();
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
);
518 putComponent(new OID(name
), value
);
521 private static String
compressWS(String str
)
523 CPStringBuilder buf
= new CPStringBuilder();
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
))
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)];
545 if ((limit
% 2) == 1)
547 result
[j
++] = (byte) Character
.digit (str
.charAt(i
++), 16);
551 result
[j
] = (byte) (Character
.digit (str
.charAt(i
++), 16) << 4);
552 result
[j
++] |= (byte) Character
.digit (str
.charAt(i
++), 16);