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
;
59 import gnu
.java
.security
.der
.DER
;
60 import gnu
.java
.security
.der
.DERReader
;
61 import gnu
.java
.security
.der
.DERValue
;
62 import gnu
.java
.security
.OID
;
64 public class X500DistinguishedName
implements Principal
67 // Constants and fields.
68 // -------------------------------------------------------------------------
70 public static final OID CN
= new OID("2.5.4.3");
71 public static final OID C
= new OID("2.5.4.6");
72 public static final OID L
= new OID("2.5.4.7");
73 public static final OID ST
= new OID("2.5.4.8");
74 public static final OID STREET
= new OID("2.5.4.9");
75 public static final OID O
= new OID("2.5.4.10");
76 public static final OID OU
= new OID("2.5.4.11");
77 public static final OID T
= new OID("2.5.4.12");
78 public static final OID DNQ
= new OID("2.5.4.46");
79 public static final OID NAME
= new OID("2.5.4.41");
80 public static final OID GIVENNAME
= new OID("2.5.4.42");
81 public static final OID INITIALS
= new OID("2.5.4.43");
82 public static final OID GENERATION
= new OID("2.5.4.44");
83 public static final OID EMAIL
= new OID("1.2.840.113549.1.9.1");
84 public static final OID DC
= new OID("0.9.2342.19200300.100.1.25");
85 public static final OID UID
= new OID("0.9.2342.19200300.100.1.1");
87 private List components
;
88 private Map currentRdn
;
89 private boolean fixed
;
90 private String stringRep
;
91 private byte[] encoded
;
94 // -------------------------------------------------------------------------
96 public X500DistinguishedName()
98 components
= new LinkedList();
99 currentRdn
= new LinkedHashMap();
100 components
.add(currentRdn
);
103 public X500DistinguishedName(String name
)
110 catch (IOException ioe
)
112 throw new IllegalArgumentException(ioe
.toString());
116 public X500DistinguishedName(byte[] encoded
) throws IOException
119 parseDer(new DERReader(encoded
));
122 public X500DistinguishedName(InputStream encoded
) throws IOException
125 parseDer(new DERReader(encoded
));
129 // -------------------------------------------------------------------------
131 public String
getName()
136 public void newRelativeDistinguishedName()
138 if (fixed
|| currentRdn
.isEmpty()) return;
139 currentRdn
= new LinkedHashMap();
140 components
.add(currentRdn
);
145 return components
.size();
148 public int countComponents()
151 for (Iterator it
= components
.iterator(); it
.hasNext(); )
153 count
+= ((Map
) it
.next()).size();
158 public boolean containsComponent(OID oid
, String value
)
160 for (Iterator it
= components
.iterator(); it
.hasNext(); )
162 Map rdn
= (Map
) it
.next();
163 String s
= (String
) rdn
.get(oid
);
166 if (compressWS(value
).equalsIgnoreCase(compressWS(s
)))
172 public String
getComponent(OID oid
)
174 for (Iterator it
= components
.iterator(); it
.hasNext(); )
176 Map rdn
= (Map
) it
.next();
177 if (rdn
.containsKey(oid
))
178 return (String
) rdn
.get(oid
);
183 public String
getComponent(OID oid
, int rdn
)
187 return (String
) ((Map
) components
.get(rdn
)).get(oid
);
190 public void putComponent(OID oid
, String value
)
192 currentRdn
.put(oid
, value
);
195 public void putComponent(String name
, String value
)
197 name
= name
.trim().toLowerCase();
198 if (name
.equals("cn"))
199 putComponent(CN
, value
);
200 else if (name
.equals("c"))
201 putComponent(C
, value
);
202 else if (name
.equals("l"))
203 putComponent(L
, value
);
204 else if (name
.equals("street"))
205 putComponent(STREET
, value
);
206 else if (name
.equals("st"))
207 putComponent(ST
, value
);
208 else if (name
.equals("t"))
209 putComponent(T
, value
);
210 else if (name
.equals("dnq"))
211 putComponent(DNQ
, value
);
212 else if (name
.equals("name"))
213 putComponent(NAME
, value
);
214 else if (name
.equals("givenname"))
215 putComponent(GIVENNAME
, value
);
216 else if (name
.equals("initials"))
217 putComponent(INITIALS
, value
);
218 else if (name
.equals("generation"))
219 putComponent(GENERATION
, value
);
220 else if (name
.equals("email"))
221 putComponent(EMAIL
, value
);
222 else if (name
.equals("dc"))
223 putComponent(DC
, value
);
224 else if (name
.equals("uid"))
225 putComponent(UID
, value
);
227 putComponent(new OID(name
), value
);
230 public void setUnmodifiable()
234 List newComps
= new ArrayList(components
.size());
235 for (Iterator it
= components
.iterator(); it
.hasNext(); )
237 Map rdn
= (Map
) it
.next();
238 rdn
= Collections
.unmodifiableMap(rdn
);
241 components
= Collections
.unmodifiableList(newComps
);
242 currentRdn
= Collections
.EMPTY_MAP
;
245 public int hashCode()
248 for (Iterator it
= components
.iterator(); it
.hasNext(); )
250 Map m
= (Map
) it
.next();
251 for (Iterator it2
= m
.entrySet().iterator(); it2
.hasNext(); )
253 Map
.Entry e
= (Map
.Entry
) it2
.next();
254 sum
+= e
.getKey().hashCode();
255 sum
+= e
.getValue().hashCode();
261 public boolean equals(Object o
)
263 if (!(o
instanceof X500DistinguishedName
))
265 if (size() != ((X500DistinguishedName
) o
).size())
267 for (int i
= 0; i
< size(); i
++)
269 Map m
= (Map
) components
.get(i
);
270 for (Iterator it2
= m
.entrySet().iterator(); it2
.hasNext(); )
272 Map
.Entry e
= (Map
.Entry
) it2
.next();
273 OID oid
= (OID
) e
.getKey();
274 String v1
= (String
) e
.getValue();
275 String v2
= ((X500DistinguishedName
) o
).getComponent(oid
, i
);
276 if (!compressWS(v1
).equalsIgnoreCase(compressWS(v2
)))
283 public String
toString()
285 if (fixed
&& stringRep
!= null)
287 StringBuffer str
= new StringBuffer();
288 for (Iterator it
= components
.iterator(); it
.hasNext(); )
290 Map m
= (Map
) it
.next();
291 for (Iterator it2
= m
.entrySet().iterator(); it2
.hasNext(); )
293 Map
.Entry entry
= (Map
.Entry
) it2
.next();
294 OID oid
= (OID
) entry
.getKey();
295 String value
= (String
) entry
.getValue();
298 else if (oid
.equals(C
))
300 else if (oid
.equals(L
))
302 else if (oid
.equals(ST
))
304 else if (oid
.equals(STREET
))
305 str
.append("STREET");
306 else if (oid
.equals(O
))
308 else if (oid
.equals(OU
))
310 else if (oid
.equals(T
))
312 else if (oid
.equals(DNQ
))
314 else if (oid
.equals(NAME
))
317 str
.append(oid
.toString());
326 return (stringRep
= str
.toString());
329 public byte[] getDer()
331 if (fixed
&& encoded
!= null)
332 return (byte[]) encoded
.clone();
333 ArrayList name
= new ArrayList(components
.size());
334 for (Iterator it
= components
.iterator(); it
.hasNext(); )
336 Map m
= (Map
) it
.next();
339 Set rdn
= new HashSet();
340 for (Iterator it2
= m
.entrySet().iterator(); it2
.hasNext(); )
342 Map
.Entry e
= (Map
.Entry
) it
.next();
343 ArrayList atav
= new ArrayList(2);
344 atav
.add(new DERValue(DER
.OBJECT_IDENTIFIER
, e
.getKey()));
345 atav
.add(new DERValue(DER
.UTF8_STRING
, e
.getValue()));
346 rdn
.add(new DERValue(DER
.SEQUENCE
|DER
.CONSTRUCTED
, atav
));
348 name
.add(new DERValue(DER
.SET
|DER
.CONSTRUCTED
, rdn
));
350 DERValue val
= new DERValue(DER
.SEQUENCE
|DER
.CONSTRUCTED
, name
);
351 return (byte[]) (encoded
= val
.getEncoded()).clone();
355 // -------------------------------------------------------------------------
359 private void parseString(String str
) throws IOException
361 Reader in
= new StringReader(str
);
364 String key
= readAttributeType(in
);
367 String value
= readAttributeValue(in
);
368 putComponent(key
, value
);
370 newRelativeDistinguishedName();
375 private String
readAttributeType(Reader in
) throws IOException
377 StringBuffer buf
= new StringBuffer();
379 while ((ch
= in
.read()) != '=')
383 if (buf
.length() > 0)
384 throw new EOFException();
388 throw new IOException("Invalid char: " + (char) ch
);
389 if (Character
.isLetterOrDigit((char) ch
) || ch
== '-' || ch
== '.')
390 buf
.append((char) ch
);
392 throw new IOException("Invalid char: " + (char) ch
);
394 return buf
.toString();
397 private String
readAttributeValue(Reader in
) throws IOException
399 StringBuffer buf
= new StringBuffer();
406 if (('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
407 || Character
.isDigit((char) ch
))
408 buf
.append((char) ch
);
409 else if (ch
== '+' || ch
== ',')
412 String hex
= buf
.toString();
413 return new String(Util
.toByteArray(hex
));
416 throw new IOException("illegal character: " + (char) ch
);
430 throw new EOFException();
431 if (('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
432 || Character
.isDigit((char) ch
))
434 int i
= Character
.digit((char) ch
, 16) << 4;
436 if (!(('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
437 || Character
.isDigit((char) ch
)))
438 throw new IOException("illegal hex char");
439 i
|= Character
.digit((char) ch
, 16);
440 buf
.append((char) i
);
443 buf
.append((char) ch
);
446 buf
.append((char) ch
);
449 if (sep
!= '+' || sep
!= ',')
450 throw new IOException("illegal character: " + (char) ch
);
451 return buf
.toString();
462 return buf
.toString();
466 throw new EOFException();
467 if (('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
468 || Character
.isDigit((char) ch
))
470 int i
= Character
.digit((char) ch
, 16) << 4;
472 if (!(('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
473 || Character
.isDigit((char) ch
)))
474 throw new IOException("illegal hex char");
475 i
|= Character
.digit((char) ch
, 16);
476 buf
.append((char) i
);
479 buf
.append((char) ch
);
486 throw new IOException("illegal character: " + (char) ch
);
488 throw new EOFException();
490 buf
.append((char) ch
);
496 private void parseDer(DERReader der
) throws IOException
498 DERValue name
= der
.read();
499 if (!name
.isConstructed())
500 throw new IOException("malformed Name");
501 encoded
= name
.getEncoded();
503 while (len
< name
.getLength())
505 DERValue rdn
= der
.read();
506 if (!rdn
.isConstructed())
507 throw new IOException("badly formed RDNSequence");
509 while (len2
< rdn
.getLength())
511 DERValue atav
= der
.read();
512 if (!atav
.isConstructed())
513 throw new IOException("badly formed AttributeTypeAndValue");
514 DERValue val
= der
.read();
515 if (val
.getTag() != DER
.OBJECT_IDENTIFIER
)
516 throw new IOException("badly formed AttributeTypeAndValue");
517 OID oid
= (OID
) val
.getValue();
519 if (!(val
.getValue() instanceof String
))
520 throw new IOException("badly formed AttributeTypeAndValue");
521 String value
= (String
) val
.getValue();
522 putComponent(oid
, value
);
523 len2
+= atav
.getEncodedLength();
525 len
+= rdn
.getEncodedLength();
526 if (len
< name
.getLength())
527 newRelativeDistinguishedName();
532 private static String
compressWS(String str
)
534 StringBuffer buf
= new StringBuffer();
536 for (int i
= 0; i
< str
.length(); i
++)
538 char c
= str
.charAt(i
);
539 if (Character
.isWhitespace(c
))
541 if (!Character
.isWhitespace(lastChar
))
548 return buf
.toString().trim();