1 /* X500DistinguishedName.java -- X.500 distinguished name.
2 Copyright (C) 2004, 2006 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 gnu
.java
.security
.x509
;
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
.EOFException
;
47 import java
.io
.IOException
;
48 import java
.io
.InputStream
;
49 import java
.io
.Reader
;
50 import java
.io
.StringReader
;
51 import java
.security
.Principal
;
52 import java
.util
.ArrayList
;
53 import java
.util
.Collections
;
54 import java
.util
.HashSet
;
55 import java
.util
.Iterator
;
56 import java
.util
.LinkedHashMap
;
57 import java
.util
.LinkedList
;
58 import java
.util
.List
;
62 public class X500DistinguishedName
implements Principal
64 // Constants and fields.
65 // -------------------------------------------------------------------------
67 public static final OID CN
= new OID("2.5.4.3");
68 public static final OID C
= new OID("2.5.4.6");
69 public static final OID L
= new OID("2.5.4.7");
70 public static final OID ST
= new OID("2.5.4.8");
71 public static final OID STREET
= new OID("2.5.4.9");
72 public static final OID O
= new OID("2.5.4.10");
73 public static final OID OU
= new OID("2.5.4.11");
74 public static final OID T
= new OID("2.5.4.12");
75 public static final OID DNQ
= new OID("2.5.4.46");
76 public static final OID NAME
= new OID("2.5.4.41");
77 public static final OID GIVENNAME
= new OID("2.5.4.42");
78 public static final OID INITIALS
= new OID("2.5.4.43");
79 public static final OID GENERATION
= new OID("2.5.4.44");
80 public static final OID EMAIL
= new OID("1.2.840.113549.1.9.1");
81 public static final OID DC
= new OID("0.9.2342.19200300.100.1.25");
82 public static final OID UID
= new OID("0.9.2342.19200300.100.1.1");
84 private List components
;
85 private Map currentRdn
;
86 private boolean fixed
;
87 private String stringRep
;
88 private byte[] encoded
;
91 // -------------------------------------------------------------------------
93 public X500DistinguishedName()
95 components
= new LinkedList();
96 currentRdn
= new LinkedHashMap();
97 components
.add(currentRdn
);
100 public X500DistinguishedName(String name
)
107 catch (IOException ioe
)
109 throw new IllegalArgumentException(ioe
.toString());
113 public X500DistinguishedName(byte[] encoded
) throws IOException
116 parseDer(new DERReader(encoded
));
119 public X500DistinguishedName(InputStream encoded
) throws IOException
122 parseDer(new DERReader(encoded
));
126 // -------------------------------------------------------------------------
128 public String
getName()
133 public void newRelativeDistinguishedName()
135 if (fixed
|| currentRdn
.isEmpty()) return;
136 currentRdn
= new LinkedHashMap();
137 components
.add(currentRdn
);
142 return components
.size();
145 public int countComponents()
148 for (Iterator it
= components
.iterator(); it
.hasNext(); )
150 count
+= ((Map
) it
.next()).size();
155 public boolean containsComponent(OID oid
, String value
)
157 for (Iterator it
= components
.iterator(); it
.hasNext(); )
159 Map rdn
= (Map
) it
.next();
160 String s
= (String
) rdn
.get(oid
);
163 if (compressWS(value
).equalsIgnoreCase(compressWS(s
)))
169 public String
getComponent(OID oid
)
171 for (Iterator it
= components
.iterator(); it
.hasNext(); )
173 Map rdn
= (Map
) it
.next();
174 if (rdn
.containsKey(oid
))
175 return (String
) rdn
.get(oid
);
180 public String
getComponent(OID oid
, int rdn
)
184 return (String
) ((Map
) components
.get(rdn
)).get(oid
);
187 public void putComponent(OID oid
, String value
)
189 currentRdn
.put(oid
, value
);
192 public void putComponent(String name
, String value
)
194 name
= name
.trim().toLowerCase();
195 if (name
.equals("cn"))
196 putComponent(CN
, value
);
197 else if (name
.equals("c"))
198 putComponent(C
, value
);
199 else if (name
.equals("l"))
200 putComponent(L
, value
);
201 else if (name
.equals("street"))
202 putComponent(STREET
, value
);
203 else if (name
.equals("st"))
204 putComponent(ST
, value
);
205 else if (name
.equals("t"))
206 putComponent(T
, value
);
207 else if (name
.equals("dnq"))
208 putComponent(DNQ
, value
);
209 else if (name
.equals("name"))
210 putComponent(NAME
, value
);
211 else if (name
.equals("givenname"))
212 putComponent(GIVENNAME
, value
);
213 else if (name
.equals("initials"))
214 putComponent(INITIALS
, value
);
215 else if (name
.equals("generation"))
216 putComponent(GENERATION
, value
);
217 else if (name
.equals("email"))
218 putComponent(EMAIL
, value
);
219 else if (name
.equals("dc"))
220 putComponent(DC
, value
);
221 else if (name
.equals("uid"))
222 putComponent(UID
, value
);
223 else if (name
.equals("o"))
224 putComponent(O
, value
);
225 else if (name
.equals("ou"))
226 putComponent(OU
, 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();
335 ArrayList name
= new ArrayList(components
.size());
336 for (Iterator it
= components
.iterator(); it
.hasNext(); )
338 Map m
= (Map
) it
.next();
342 Set rdn
= new HashSet();
343 for (Iterator it2
= m
.entrySet().iterator(); it2
.hasNext(); )
345 Map
.Entry e
= (Map
.Entry
) it2
.next();
346 ArrayList atav
= new ArrayList(2);
347 atav
.add(new DERValue(DER
.OBJECT_IDENTIFIER
, e
.getKey()));
348 atav
.add(new DERValue(DER
.UTF8_STRING
, e
.getValue()));
349 rdn
.add(new DERValue(DER
.SEQUENCE
|DER
.CONSTRUCTED
, atav
));
351 name
.add(new DERValue(DER
.SET
|DER
.CONSTRUCTED
, rdn
));
353 DERValue val
= new DERValue(DER
.SEQUENCE
|DER
.CONSTRUCTED
, name
);
354 return (byte[]) (encoded
= val
.getEncoded()).clone();
358 // -------------------------------------------------------------------------
362 private void parseString(String str
) throws IOException
364 Reader in
= new StringReader(str
);
367 String key
= readAttributeType(in
);
370 String value
= readAttributeValue(in
);
371 putComponent(key
, value
);
373 newRelativeDistinguishedName();
378 private String
readAttributeType(Reader in
) throws IOException
380 StringBuffer buf
= new StringBuffer();
382 while ((ch
= in
.read()) != '=')
386 if (buf
.length() > 0)
387 throw new EOFException();
391 throw new IOException("Invalid char: " + (char) ch
);
392 if (Character
.isLetterOrDigit((char) ch
) || ch
== '-' || ch
== '.')
393 buf
.append((char) ch
);
395 throw new IOException("Invalid char: " + (char) ch
);
397 return buf
.toString();
400 private String
readAttributeValue(Reader in
) throws IOException
402 StringBuffer buf
= new StringBuffer();
409 if (('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
410 || Character
.isDigit((char) ch
))
411 buf
.append((char) ch
);
412 else if (ch
== '+' || ch
== ',')
415 String hex
= buf
.toString();
416 return new String(Util
.toByteArray(hex
));
419 throw new IOException("illegal character: " + (char) ch
);
433 throw new EOFException();
434 if (('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
435 || Character
.isDigit((char) ch
))
437 int i
= Character
.digit((char) ch
, 16) << 4;
439 if (!(('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
440 || Character
.isDigit((char) ch
)))
441 throw new IOException("illegal hex char");
442 i
|= Character
.digit((char) ch
, 16);
443 buf
.append((char) i
);
446 buf
.append((char) ch
);
449 buf
.append((char) ch
);
452 if (sep
!= '+' || sep
!= ',')
453 throw new IOException("illegal character: " + (char) ch
);
454 return buf
.toString();
465 return buf
.toString();
469 throw new EOFException();
470 if (('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
471 || Character
.isDigit((char) ch
))
473 int i
= Character
.digit((char) ch
, 16) << 4;
475 if (!(('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
476 || Character
.isDigit((char) ch
)))
477 throw new IOException("illegal hex char");
478 i
|= Character
.digit((char) ch
, 16);
479 buf
.append((char) i
);
482 buf
.append((char) ch
);
489 throw new IOException("illegal character: " + (char) ch
);
491 throw new EOFException();
493 buf
.append((char) ch
);
496 return buf
.toString();
502 private void parseDer(DERReader der
) throws IOException
504 DERValue name
= der
.read();
505 if (!name
.isConstructed())
506 throw new IOException("malformed Name");
507 encoded
= name
.getEncoded();
509 while (len
< name
.getLength())
511 DERValue rdn
= der
.read();
512 if (!rdn
.isConstructed())
513 throw new IOException("badly formed RDNSequence");
515 while (len2
< rdn
.getLength())
517 DERValue atav
= der
.read();
518 if (!atav
.isConstructed())
519 throw new IOException("badly formed AttributeTypeAndValue");
520 DERValue val
= der
.read();
521 if (val
.getTag() != DER
.OBJECT_IDENTIFIER
)
522 throw new IOException("badly formed AttributeTypeAndValue");
523 OID oid
= (OID
) val
.getValue();
525 if (!(val
.getValue() instanceof String
))
526 throw new IOException("badly formed AttributeTypeAndValue");
527 String value
= (String
) val
.getValue();
528 putComponent(oid
, value
);
529 len2
+= atav
.getEncodedLength();
531 len
+= rdn
.getEncodedLength();
532 if (len
< name
.getLength())
533 newRelativeDistinguishedName();
538 private static String
compressWS(String str
)
540 StringBuffer buf
= new StringBuffer();
542 for (int i
= 0; i
< str
.length(); i
++)
544 char c
= str
.charAt(i
);
545 if (Character
.isWhitespace(c
))
547 if (!Character
.isWhitespace(lastChar
))
554 return buf
.toString().trim();