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
.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
;
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
;
96 // ------------------------------------------------------------------------
98 private X500Principal()
100 components
= new LinkedList();
101 currentRdn
= new LinkedHashMap();
102 components
.add (currentRdn
);
105 public X500Principal (String name
)
109 throw new NullPointerException();
114 catch (IOException ioe
)
116 IllegalArgumentException iae
= new IllegalArgumentException("malformed name");
122 public X500Principal (byte[] encoded
)
124 this(new ByteArrayInputStream (encoded
));
127 public X500Principal (InputStream encoded
)
134 catch (IOException ioe
)
136 throw new IllegalArgumentException (ioe
.toString());
141 // ------------------------------------------------------------------------
143 public int hashCode()
146 for (int i
= 0; i
< size(); ++i
)
148 Map m
= (Map
) components
.get(i
);
149 for (Iterator it2
= m
.entrySet().iterator(); it2
.hasNext(); )
151 Map
.Entry e
= (Map
.Entry
) it2
.next();
152 // We don't bother looking at the value of the entry.
153 result
= result
* 31 + ((OID
) e
.getKey()).hashCode();
159 public boolean equals(Object o
)
161 if (!(o
instanceof X500Principal
))
163 if (size() != ((X500Principal
) o
).size())
165 for (int i
= 0; i
< size(); i
++)
167 Map m
= (Map
) components
.get (i
);
168 for (Iterator it2
= m
.entrySet().iterator(); it2
.hasNext(); )
170 Map
.Entry e
= (Map
.Entry
) it2
.next();
171 OID oid
= (OID
) e
.getKey();
172 String v1
= (String
) e
.getValue();
173 String v2
= ((X500Principal
) o
).getComponent (oid
, i
);
176 if (!compressWS (v1
).equalsIgnoreCase (compressWS (v2
)))
183 public byte[] getEncoded()
187 return (byte[]) encoded
.clone();
190 public String
getName()
192 return getName (RFC2253
);
195 public String
getName (final String format
)
197 boolean rfc2253
= RFC2253
.equalsIgnoreCase (format
) ||
198 CANONICAL
.equalsIgnoreCase (format
);
199 boolean rfc1779
= RFC1779
.equalsIgnoreCase (format
);
200 boolean canon
= CANONICAL
.equalsIgnoreCase (format
);
201 if (! (rfc2253
|| rfc1779
|| canon
))
202 throw new IllegalArgumentException ("unsupported format " + format
);
203 StringBuffer str
= new StringBuffer();
204 for (Iterator it
= components
.iterator(); it
.hasNext(); )
206 Map m
= (Map
) it
.next();
207 for (Iterator it2
= m
.entrySet().iterator(); it2
.hasNext(); )
209 Map
.Entry entry
= (Map
.Entry
) it2
.next();
210 OID oid
= (OID
) entry
.getKey();
211 String value
= (String
) entry
.getValue();
214 else if (oid
.equals (C
))
216 else if (oid
.equals (L
))
218 else if (oid
.equals (ST
))
220 else if (oid
.equals (STREET
))
221 str
.append ("STREET");
222 else if (oid
.equals (O
))
224 else if (oid
.equals (OU
))
226 else if (oid
.equals (DC
) && rfc2253
)
228 else if (oid
.equals (UID
) && rfc2253
)
231 str
.append (oid
.toString());
241 return str
.toString().toUpperCase (Locale
.US
).toLowerCase (Locale
.US
);
242 return str
.toString();
245 public String
toString()
247 return getName (RFC2253
);
250 // Serialization methods.
251 // ------------------------------------------------------------------------
253 private void writeObject (ObjectOutputStream out
) throws IOException
257 out
.writeObject (encoded
);
260 private void readObject (ObjectInputStream in
)
261 throws IOException
, NotActiveException
, ClassNotFoundException
263 byte[] buf
= (byte[]) in
.readObject();
264 parseDer (new ByteArrayInputStream (buf
));
268 // -------------------------------------------------------------------------
272 return components
.size();
275 private String
getComponent(OID oid
, int rdn
)
279 return (String
) ((Map
) components
.get (rdn
)).get (oid
);
282 private void encodeDer()
284 ArrayList name
= new ArrayList(components
.size());
285 for (Iterator it
= components
.iterator(); it
.hasNext(); )
287 Map m
= (Map
) it
.next();
290 Set rdn
= new HashSet();
291 for (Iterator it2
= m
.entrySet().iterator(); it2
.hasNext(); )
293 Map
.Entry e
= (Map
.Entry
) it2
.next();
294 ArrayList atav
= new ArrayList(2);
295 atav
.add(new DERValue(DER
.OBJECT_IDENTIFIER
, e
.getKey()));
296 atav
.add(new DERValue(DER
.UTF8_STRING
, e
.getValue()));
297 rdn
.add(new DERValue(DER
.SEQUENCE
|DER
.CONSTRUCTED
, atav
));
299 name
.add(new DERValue(DER
.SET
|DER
.CONSTRUCTED
, rdn
));
301 DERValue val
= new DERValue(DER
.SEQUENCE
|DER
.CONSTRUCTED
, name
);
302 encoded
= val
.getEncoded();
307 private void parseString(String str
) throws IOException
309 Reader in
= new StringReader(str
);
312 String key
= readAttributeType(in
);
315 String value
= readAttributeValue(in
);
316 putComponent(key
, value
);
318 newRelativeDistinguishedName();
324 private String
readAttributeType(Reader in
) throws IOException
326 StringBuffer buf
= new StringBuffer();
328 while ((ch
= in
.read()) != '=')
332 if (buf
.length() > 0)
333 throw new EOFException("partial name read: " + buf
);
337 throw new IOException("Invalid char: " + (char) ch
);
338 if (Character
.isLetterOrDigit((char) ch
) || ch
== '-' || ch
== '.')
339 buf
.append((char) ch
);
341 throw new IOException("Invalid char: " + (char) ch
);
343 return buf
.toString();
346 private String
readAttributeValue(Reader in
) throws IOException
348 StringBuffer buf
= new StringBuffer();
355 if (('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
356 || Character
.isDigit((char) ch
))
357 buf
.append((char) ch
);
358 else if (ch
== '+' || ch
== ',')
361 String hex
= buf
.toString();
362 return new String(toByteArray(hex
));
365 throw new IOException("illegal character: " + (char) ch
);
379 throw new EOFException();
380 if (('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
381 || Character
.isDigit((char) ch
))
383 int i
= Character
.digit((char) ch
, 16) << 4;
385 if (!(('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
386 || Character
.isDigit((char) ch
)))
387 throw new IOException("illegal hex char");
388 i
|= Character
.digit((char) ch
, 16);
389 buf
.append((char) i
);
392 buf
.append((char) ch
);
395 buf
.append((char) ch
);
398 if (sep
!= '+' || sep
!= ',')
399 throw new IOException("illegal character: " + (char) ch
);
400 return buf
.toString();
411 return buf
.toString();
415 throw new EOFException();
416 if (('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
417 || Character
.isDigit((char) ch
))
419 int i
= Character
.digit((char) ch
, 16) << 4;
421 if (!(('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
422 || Character
.isDigit((char) ch
)))
423 throw new IOException("illegal hex char");
424 i
|= Character
.digit((char) ch
, 16);
425 buf
.append((char) i
);
428 buf
.append((char) ch
);
435 throw new IOException("illegal character: " + (char) ch
);
438 return buf
.toString ();
440 buf
.append((char) ch
);
447 private void parseDer (InputStream encoded
) throws IOException
449 DERReader der
= new DERReader (encoded
);
450 DERValue name
= der
.read();
451 if (!name
.isConstructed())
452 throw new IOException ("malformed Name");
453 this.encoded
= name
.getEncoded();
455 while (len
< name
.getLength())
457 DERValue rdn
= der
.read();
458 if (!rdn
.isConstructed())
459 throw new IOException ("badly formed RDNSequence");
461 while (len2
< rdn
.getLength())
463 DERValue atav
= der
.read();
464 if (!atav
.isConstructed())
465 throw new IOException ("badly formed AttributeTypeAndValue");
466 DERValue val
= der
.read();
467 if (val
.getTag() != DER
.OBJECT_IDENTIFIER
)
468 throw new IOException ("badly formed AttributeTypeAndValue");
469 OID oid
= (OID
) val
.getValue();
471 if (!(val
.getValue() instanceof String
))
472 throw new IOException ("badly formed AttributeTypeAndValue");
473 String value
= (String
) val
.getValue();
474 putComponent(oid
, value
);
475 len2
+= atav
.getEncodedLength();
477 len
+= rdn
.getEncodedLength();
478 if (len
< name
.getLength())
479 newRelativeDistinguishedName();
483 private void newRelativeDistinguishedName()
485 currentRdn
= new LinkedHashMap();
486 components
.add(currentRdn
);
489 private void putComponent(OID oid
, String value
)
491 currentRdn
.put(oid
, value
);
494 private void putComponent(String name
, String value
)
496 name
= name
.trim().toLowerCase();
497 if (name
.equals("cn"))
498 putComponent(CN
, value
);
499 else if (name
.equals("c"))
500 putComponent(C
, value
);
501 else if (name
.equals("l"))
502 putComponent(L
, value
);
503 else if (name
.equals("street"))
504 putComponent(STREET
, value
);
505 else if (name
.equals("st"))
506 putComponent(ST
, value
);
507 else if (name
.equals ("o"))
508 putComponent (O
, value
);
509 else if (name
.equals ("ou"))
510 putComponent (OU
, value
);
511 else if (name
.equals("dc"))
512 putComponent(DC
, value
);
513 else if (name
.equals("uid"))
514 putComponent(UID
, value
);
516 putComponent(new OID(name
), value
);
519 private static String
compressWS(String str
)
521 StringBuffer buf
= new StringBuffer();
523 for (int i
= 0; i
< str
.length(); i
++)
525 char c
= str
.charAt(i
);
526 if (Character
.isWhitespace(c
))
528 if (!Character
.isWhitespace(lastChar
))
535 return buf
.toString().trim();
538 private static byte[] toByteArray (String str
)
540 int limit
= str
.length();
541 byte[] result
= new byte[((limit
+ 1) / 2)];
543 if ((limit
% 2) == 1)
545 result
[j
++] = (byte) Character
.digit (str
.charAt(i
++), 16);
549 result
[j
] = (byte) (Character
.digit (str
.charAt(i
++), 16) << 4);
550 result
[j
++] |= (byte) Character
.digit (str
.charAt(i
++), 16);