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
.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 boolean equals(Object o
)
145 if (!(o
instanceof X500Principal
))
147 if (size() != ((X500Principal
) o
).size())
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
);
160 if (!compressWS (v1
).equalsIgnoreCase (compressWS (v2
)))
167 public byte[] getEncoded()
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();
198 else if (oid
.equals (C
))
200 else if (oid
.equals (L
))
202 else if (oid
.equals (ST
))
204 else if (oid
.equals (STREET
))
205 str
.append ("STREET");
206 else if (oid
.equals (O
))
208 else if (oid
.equals (OU
))
210 else if (oid
.equals (DC
) && rfc2253
)
212 else if (oid
.equals ("UID") && rfc2253
)
215 str
.append (oid
.toString());
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
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
));
252 // -------------------------------------------------------------------------
256 return components
.size();
259 private String
getComponent(OID oid
, int rdn
)
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();
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();
291 private void parseString(String str
) throws IOException
293 Reader in
= new StringReader(str
);
296 String key
= readAttributeType(in
);
299 String value
= readAttributeValue(in
);
300 putComponent(key
, value
);
302 newRelativeDistinguishedName();
306 private String
readAttributeType(Reader in
) throws IOException
308 StringBuffer buf
= new StringBuffer();
310 while ((ch
= in
.read()) != '=')
314 if (buf
.length() > 0)
315 throw new EOFException();
319 throw new IOException("Invalid char: " + (char) ch
);
320 if (Character
.isLetterOrDigit((char) ch
) || ch
== '-' || ch
== '.')
321 buf
.append((char) ch
);
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();
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
== ',')
343 String hex
= buf
.toString();
344 return new String(toByteArray(hex
));
347 throw new IOException("illegal character: " + (char) ch
);
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;
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
);
374 buf
.append((char) ch
);
377 buf
.append((char) ch
);
380 if (sep
!= '+' || sep
!= ',')
381 throw new IOException("illegal character: " + (char) ch
);
382 return buf
.toString();
393 return buf
.toString();
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;
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
);
410 buf
.append((char) ch
);
417 throw new IOException("illegal character: " + (char) ch
);
419 throw new EOFException();
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();
435 while (len
< name
.getLength())
437 DERValue rdn
= der
.read();
438 if (!rdn
.isConstructed())
439 throw new IOException ("badly formed RDNSequence");
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();
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
);
492 putComponent(new OID(name
), value
);
495 private static String
compressWS(String str
)
497 StringBuffer buf
= new StringBuffer();
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
))
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)];
519 if ((limit
% 2) == 1)
521 result
[j
++] = (byte) Character
.digit (str
.charAt(i
++), 16);
525 result
[j
] = (byte) (Character
.digit (str
.charAt(i
++), 16) << 4);
526 result
[j
++] |= (byte) Character
.digit (str
.charAt(i
++), 16);