1 /* X500DistinguishedName.java -- X.500 distinguished name.
2 Copyright (C) 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 gnu
.java
.security
.x509
;
41 import java
.io
.EOFException
;
42 import java
.io
.InputStream
;
43 import java
.io
.IOException
;
44 import java
.io
.Reader
;
45 import java
.io
.StringReader
;
47 import java
.security
.Principal
;
49 import java
.util
.ArrayList
;
50 import java
.util
.Collections
;
51 import java
.util
.HashSet
;
52 import java
.util
.Iterator
;
53 import java
.util
.LinkedHashMap
;
54 import java
.util
.LinkedList
;
55 import java
.util
.List
;
58 import java
.util
.TreeMap
;
60 import gnu
.java
.security
.der
.DER
;
61 import gnu
.java
.security
.der
.DERReader
;
62 import gnu
.java
.security
.der
.DERValue
;
63 import gnu
.java
.security
.OID
;
65 public class X500DistinguishedName
implements Principal
68 // Constants and fields.
69 // -------------------------------------------------------------------------
71 public static final OID CN
= new OID("2.5.4.3");
72 public static final OID C
= new OID("2.5.4.6");
73 public static final OID L
= new OID("2.5.4.7");
74 public static final OID ST
= new OID("2.5.4.8");
75 public static final OID STREET
= new OID("2.5.4.9");
76 public static final OID O
= new OID("2.5.4.10");
77 public static final OID OU
= new OID("2.5.4.11");
78 public static final OID T
= new OID("2.5.4.12");
79 public static final OID DNQ
= new OID("2.5.4.46");
80 public static final OID NAME
= new OID("2.5.4.41");
81 public static final OID GIVENNAME
= new OID("2.5.4.42");
82 public static final OID INITIALS
= new OID("2.5.4.43");
83 public static final OID GENERATION
= new OID("2.5.4.44");
84 public static final OID EMAIL
= new OID("1.2.840.113549.1.9.1");
85 public static final OID DC
= new OID("0.9.2342.19200300.100.1.25");
86 public static final OID UID
= new OID("0.9.2342.19200300.100.1.1");
88 private List components
;
89 private Map currentRdn
;
90 private boolean fixed
;
91 private String stringRep
;
92 private byte[] encoded
;
95 // -------------------------------------------------------------------------
97 public X500DistinguishedName()
99 components
= new LinkedList();
100 currentRdn
= new LinkedHashMap();
101 components
.add(currentRdn
);
104 public X500DistinguishedName(String name
)
111 catch (IOException ioe
)
113 throw new IllegalArgumentException(ioe
.toString());
117 public X500DistinguishedName(byte[] encoded
) throws IOException
120 parseDer(new DERReader(encoded
));
123 public X500DistinguishedName(InputStream encoded
) throws IOException
126 parseDer(new DERReader(encoded
));
130 // -------------------------------------------------------------------------
132 public String
getName()
137 public void newRelativeDistinguishedName()
139 if (fixed
|| currentRdn
.isEmpty()) return;
140 currentRdn
= new LinkedHashMap();
141 components
.add(currentRdn
);
146 return components
.size();
149 public int countComponents()
152 for (Iterator it
= components
.iterator(); it
.hasNext(); )
154 count
+= ((Map
) it
.next()).size();
159 public boolean containsComponent(OID oid
, String value
)
161 for (Iterator it
= components
.iterator(); it
.hasNext(); )
163 Map rdn
= (Map
) it
.next();
164 String s
= (String
) rdn
.get(oid
);
167 if (compressWS(value
).equalsIgnoreCase(compressWS(s
)))
173 public String
getComponent(OID oid
)
175 for (Iterator it
= components
.iterator(); it
.hasNext(); )
177 Map rdn
= (Map
) it
.next();
178 if (rdn
.containsKey(oid
))
179 return (String
) rdn
.get(oid
);
184 public String
getComponent(OID oid
, int rdn
)
188 return (String
) ((Map
) components
.get(rdn
)).get(oid
);
191 public void putComponent(OID oid
, String value
)
193 currentRdn
.put(oid
, value
);
196 public void putComponent(String name
, String value
)
198 name
= name
.trim().toLowerCase();
199 if (name
.equals("cn"))
200 putComponent(CN
, value
);
201 else if (name
.equals("c"))
202 putComponent(C
, value
);
203 else if (name
.equals("l"))
204 putComponent(L
, value
);
205 else if (name
.equals("street"))
206 putComponent(STREET
, value
);
207 else if (name
.equals("st"))
208 putComponent(ST
, value
);
209 else if (name
.equals("t"))
210 putComponent(T
, value
);
211 else if (name
.equals("dnq"))
212 putComponent(DNQ
, value
);
213 else if (name
.equals("name"))
214 putComponent(NAME
, value
);
215 else if (name
.equals("givenname"))
216 putComponent(GIVENNAME
, value
);
217 else if (name
.equals("initials"))
218 putComponent(INITIALS
, value
);
219 else if (name
.equals("generation"))
220 putComponent(GENERATION
, value
);
221 else if (name
.equals("email"))
222 putComponent(EMAIL
, value
);
223 else if (name
.equals("dc"))
224 putComponent(DC
, value
);
225 else if (name
.equals("uid"))
226 putComponent(UID
, value
);
228 putComponent(new OID(name
), value
);
231 public void setUnmodifiable()
235 List newComps
= new ArrayList(components
.size());
236 for (Iterator it
= components
.iterator(); it
.hasNext(); )
238 Map rdn
= (Map
) it
.next();
239 rdn
= Collections
.unmodifiableMap(rdn
);
242 components
= Collections
.unmodifiableList(newComps
);
243 currentRdn
= Collections
.EMPTY_MAP
;
246 public int hashCode()
249 for (Iterator it
= components
.iterator(); it
.hasNext(); )
251 Map m
= (Map
) it
.next();
252 for (Iterator it2
= m
.entrySet().iterator(); it2
.hasNext(); )
254 Map
.Entry e
= (Map
.Entry
) it2
.next();
255 sum
+= e
.getKey().hashCode();
256 sum
+= e
.getValue().hashCode();
262 public boolean equals(Object o
)
264 if (!(o
instanceof X500DistinguishedName
))
266 if (size() != ((X500DistinguishedName
) o
).size())
268 for (int i
= 0; i
< size(); i
++)
270 Map m
= (Map
) components
.get(i
);
271 for (Iterator it2
= m
.entrySet().iterator(); it2
.hasNext(); )
273 Map
.Entry e
= (Map
.Entry
) it2
.next();
274 OID oid
= (OID
) e
.getKey();
275 String v1
= (String
) e
.getValue();
276 String v2
= ((X500DistinguishedName
) o
).getComponent(oid
, i
);
277 if (!compressWS(v1
).equalsIgnoreCase(compressWS(v2
)))
284 public String
toString()
286 if (fixed
&& stringRep
!= null)
288 StringBuffer str
= new StringBuffer();
289 for (Iterator it
= components
.iterator(); it
.hasNext(); )
291 Map m
= (Map
) it
.next();
292 for (Iterator it2
= m
.entrySet().iterator(); it2
.hasNext(); )
294 Map
.Entry entry
= (Map
.Entry
) it2
.next();
295 OID oid
= (OID
) entry
.getKey();
296 String value
= (String
) entry
.getValue();
299 else if (oid
.equals(C
))
301 else if (oid
.equals(L
))
303 else if (oid
.equals(ST
))
305 else if (oid
.equals(STREET
))
306 str
.append("STREET");
307 else if (oid
.equals(O
))
309 else if (oid
.equals(OU
))
311 else if (oid
.equals(T
))
313 else if (oid
.equals(DNQ
))
315 else if (oid
.equals(NAME
))
318 str
.append(oid
.toString());
327 return (stringRep
= str
.toString());
330 public byte[] getDer()
332 if (fixed
&& encoded
!= null)
333 return (byte[]) encoded
.clone();
334 ArrayList name
= new ArrayList(components
.size());
335 for (Iterator it
= components
.iterator(); it
.hasNext(); )
337 Map m
= (Map
) it
.next();
340 Set rdn
= new HashSet();
341 for (Iterator it2
= m
.entrySet().iterator(); it2
.hasNext(); )
343 Map
.Entry e
= (Map
.Entry
) it
.next();
344 ArrayList atav
= new ArrayList(2);
345 atav
.add(new DERValue(DER
.OBJECT_IDENTIFIER
, e
.getKey()));
346 atav
.add(new DERValue(DER
.UTF8_STRING
, e
.getValue()));
347 rdn
.add(new DERValue(DER
.SEQUENCE
|DER
.CONSTRUCTED
, atav
));
349 name
.add(new DERValue(DER
.SET
|DER
.CONSTRUCTED
, rdn
));
351 DERValue val
= new DERValue(DER
.SEQUENCE
|DER
.CONSTRUCTED
, name
);
352 return (byte[]) (encoded
= val
.getEncoded()).clone();
356 // -------------------------------------------------------------------------
360 private void parseString(String str
) throws IOException
362 Reader in
= new StringReader(str
);
365 String key
= readAttributeType(in
);
368 String value
= readAttributeValue(in
);
369 putComponent(key
, value
);
371 newRelativeDistinguishedName();
376 private String
readAttributeType(Reader in
) throws IOException
378 StringBuffer buf
= new StringBuffer();
380 while ((ch
= in
.read()) != '=')
384 if (buf
.length() > 0)
385 throw new EOFException();
389 throw new IOException("Invalid char: " + (char) ch
);
390 if (Character
.isLetterOrDigit((char) ch
) || ch
== '-' || ch
== '.')
391 buf
.append((char) ch
);
393 throw new IOException("Invalid char: " + (char) ch
);
395 return buf
.toString();
398 private String
readAttributeValue(Reader in
) throws IOException
400 StringBuffer buf
= new StringBuffer();
407 if (('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
408 || Character
.isDigit((char) ch
))
409 buf
.append((char) ch
);
410 else if (ch
== '+' || ch
== ',')
413 String hex
= buf
.toString();
414 return new String(Util
.toByteArray(hex
));
417 throw new IOException("illegal character: " + (char) ch
);
431 throw new EOFException();
432 if (('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
433 || Character
.isDigit((char) ch
))
435 int i
= Character
.digit((char) ch
, 16) << 4;
437 if (!(('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
438 || Character
.isDigit((char) ch
)))
439 throw new IOException("illegal hex char");
440 i
|= Character
.digit((char) ch
, 16);
441 buf
.append((char) i
);
444 buf
.append((char) ch
);
447 buf
.append((char) ch
);
450 if (sep
!= '+' || sep
!= ',')
451 throw new IOException("illegal character: " + (char) ch
);
452 return buf
.toString();
463 return buf
.toString();
467 throw new EOFException();
468 if (('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
469 || Character
.isDigit((char) ch
))
471 int i
= Character
.digit((char) ch
, 16) << 4;
473 if (!(('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
474 || Character
.isDigit((char) ch
)))
475 throw new IOException("illegal hex char");
476 i
|= Character
.digit((char) ch
, 16);
477 buf
.append((char) i
);
480 buf
.append((char) ch
);
487 throw new IOException("illegal character: " + (char) ch
);
489 throw new EOFException();
491 buf
.append((char) ch
);
497 private void parseDer(DERReader der
) throws IOException
499 DERValue name
= der
.read();
500 if (!name
.isConstructed())
501 throw new IOException("malformed Name");
502 encoded
= name
.getEncoded();
504 while (len
< name
.getLength())
506 DERValue rdn
= der
.read();
507 if (!rdn
.isConstructed())
508 throw new IOException("badly formed RDNSequence");
510 while (len2
< rdn
.getLength())
512 DERValue atav
= der
.read();
513 if (!atav
.isConstructed())
514 throw new IOException("badly formed AttributeTypeAndValue");
515 DERValue val
= der
.read();
516 if (val
.getTag() != DER
.OBJECT_IDENTIFIER
)
517 throw new IOException("badly formed AttributeTypeAndValue");
518 OID oid
= (OID
) val
.getValue();
520 if (!(val
.getValue() instanceof String
))
521 throw new IOException("badly formed AttributeTypeAndValue");
522 String value
= (String
) val
.getValue();
523 putComponent(oid
, value
);
524 len2
+= atav
.getEncodedLength();
526 len
+= rdn
.getEncodedLength();
527 if (len
< name
.getLength())
528 newRelativeDistinguishedName();
533 private static String
compressWS(String str
)
535 StringBuffer buf
= new StringBuffer();
537 for (int i
= 0; i
< str
.length(); i
++)
539 char c
= str
.charAt(i
);
540 if (Character
.isWhitespace(c
))
542 if (!Character
.isWhitespace(lastChar
))
549 return buf
.toString().trim();