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
.lang
.CPStringBuilder
;
43 import gnu
.java
.security
.OID
;
44 import gnu
.java
.security
.der
.DER
;
45 import gnu
.java
.security
.der
.DERReader
;
46 import gnu
.java
.security
.der
.DERValue
;
48 import java
.io
.EOFException
;
49 import java
.io
.IOException
;
50 import java
.io
.InputStream
;
51 import java
.io
.Reader
;
52 import java
.io
.StringReader
;
53 import java
.security
.Principal
;
54 import java
.util
.ArrayList
;
55 import java
.util
.Collections
;
56 import java
.util
.HashSet
;
57 import java
.util
.Iterator
;
58 import java
.util
.LinkedHashMap
;
59 import java
.util
.LinkedList
;
60 import java
.util
.List
;
64 public class X500DistinguishedName
implements Principal
66 // Constants and fields.
67 // -------------------------------------------------------------------------
69 public static final OID CN
= new OID("2.5.4.3");
70 public static final OID C
= new OID("2.5.4.6");
71 public static final OID L
= new OID("2.5.4.7");
72 public static final OID ST
= new OID("2.5.4.8");
73 public static final OID STREET
= new OID("2.5.4.9");
74 public static final OID O
= new OID("2.5.4.10");
75 public static final OID OU
= new OID("2.5.4.11");
76 public static final OID T
= new OID("2.5.4.12");
77 public static final OID DNQ
= new OID("2.5.4.46");
78 public static final OID NAME
= new OID("2.5.4.41");
79 public static final OID GIVENNAME
= new OID("2.5.4.42");
80 public static final OID INITIALS
= new OID("2.5.4.43");
81 public static final OID GENERATION
= new OID("2.5.4.44");
82 public static final OID EMAIL
= new OID("1.2.840.113549.1.9.1");
83 public static final OID DC
= new OID("0.9.2342.19200300.100.1.25");
84 public static final OID UID
= new OID("0.9.2342.19200300.100.1.1");
86 private List components
;
87 private Map currentRdn
;
88 private boolean fixed
;
89 private String stringRep
;
90 private byte[] encoded
;
93 // -------------------------------------------------------------------------
95 public X500DistinguishedName()
97 components
= new LinkedList();
98 currentRdn
= new LinkedHashMap();
99 components
.add(currentRdn
);
102 public X500DistinguishedName(String name
)
109 catch (IOException ioe
)
111 throw new IllegalArgumentException(ioe
.toString());
115 public X500DistinguishedName(byte[] encoded
) throws IOException
118 parseDer(new DERReader(encoded
));
121 public X500DistinguishedName(InputStream encoded
) throws IOException
124 parseDer(new DERReader(encoded
));
128 // -------------------------------------------------------------------------
130 public String
getName()
135 public void newRelativeDistinguishedName()
137 if (fixed
|| currentRdn
.isEmpty()) return;
138 currentRdn
= new LinkedHashMap();
139 components
.add(currentRdn
);
144 return components
.size();
147 public int countComponents()
150 for (Iterator it
= components
.iterator(); it
.hasNext(); )
152 count
+= ((Map
) it
.next()).size();
157 public boolean containsComponent(OID oid
, String value
)
159 for (Iterator it
= components
.iterator(); it
.hasNext(); )
161 Map rdn
= (Map
) it
.next();
162 String s
= (String
) rdn
.get(oid
);
165 if (compressWS(value
).equalsIgnoreCase(compressWS(s
)))
171 public String
getComponent(OID oid
)
173 for (Iterator it
= components
.iterator(); it
.hasNext(); )
175 Map rdn
= (Map
) it
.next();
176 if (rdn
.containsKey(oid
))
177 return (String
) rdn
.get(oid
);
182 public String
getComponent(OID oid
, int rdn
)
186 return (String
) ((Map
) components
.get(rdn
)).get(oid
);
189 public void putComponent(OID oid
, String value
)
191 currentRdn
.put(oid
, value
);
194 public void putComponent(String name
, String value
)
196 name
= name
.trim().toLowerCase();
197 if (name
.equals("cn"))
198 putComponent(CN
, value
);
199 else if (name
.equals("c"))
200 putComponent(C
, value
);
201 else if (name
.equals("l"))
202 putComponent(L
, value
);
203 else if (name
.equals("street"))
204 putComponent(STREET
, value
);
205 else if (name
.equals("st"))
206 putComponent(ST
, value
);
207 else if (name
.equals("t"))
208 putComponent(T
, value
);
209 else if (name
.equals("dnq"))
210 putComponent(DNQ
, value
);
211 else if (name
.equals("name"))
212 putComponent(NAME
, value
);
213 else if (name
.equals("givenname"))
214 putComponent(GIVENNAME
, value
);
215 else if (name
.equals("initials"))
216 putComponent(INITIALS
, value
);
217 else if (name
.equals("generation"))
218 putComponent(GENERATION
, value
);
219 else if (name
.equals("email"))
220 putComponent(EMAIL
, value
);
221 else if (name
.equals("dc"))
222 putComponent(DC
, value
);
223 else if (name
.equals("uid"))
224 putComponent(UID
, value
);
225 else if (name
.equals("o"))
226 putComponent(O
, value
);
227 else if (name
.equals("ou"))
228 putComponent(OU
, value
);
230 putComponent(new OID(name
), value
);
233 public void setUnmodifiable()
237 List newComps
= new ArrayList(components
.size());
238 for (Iterator it
= components
.iterator(); it
.hasNext(); )
240 Map rdn
= (Map
) it
.next();
241 rdn
= Collections
.unmodifiableMap(rdn
);
244 components
= Collections
.unmodifiableList(newComps
);
245 currentRdn
= Collections
.EMPTY_MAP
;
248 public int hashCode()
251 for (Iterator it
= components
.iterator(); it
.hasNext(); )
253 Map m
= (Map
) it
.next();
254 for (Iterator it2
= m
.entrySet().iterator(); it2
.hasNext(); )
256 Map
.Entry e
= (Map
.Entry
) it2
.next();
257 sum
+= e
.getKey().hashCode();
258 sum
+= e
.getValue().hashCode();
264 public boolean equals(Object o
)
266 if (!(o
instanceof X500DistinguishedName
))
268 if (size() != ((X500DistinguishedName
) o
).size())
270 for (int i
= 0; i
< size(); i
++)
272 Map m
= (Map
) components
.get(i
);
273 for (Iterator it2
= m
.entrySet().iterator(); it2
.hasNext(); )
275 Map
.Entry e
= (Map
.Entry
) it2
.next();
276 OID oid
= (OID
) e
.getKey();
277 String v1
= (String
) e
.getValue();
278 String v2
= ((X500DistinguishedName
) o
).getComponent(oid
, i
);
279 if (!compressWS(v1
).equalsIgnoreCase(compressWS(v2
)))
286 public String
toString()
288 if (fixed
&& stringRep
!= null)
290 CPStringBuilder str
= new CPStringBuilder();
291 for (Iterator it
= components
.iterator(); it
.hasNext(); )
293 Map m
= (Map
) it
.next();
294 for (Iterator it2
= m
.entrySet().iterator(); it2
.hasNext(); )
296 Map
.Entry entry
= (Map
.Entry
) it2
.next();
297 OID oid
= (OID
) entry
.getKey();
298 String value
= (String
) entry
.getValue();
301 else if (oid
.equals(C
))
303 else if (oid
.equals(L
))
305 else if (oid
.equals(ST
))
307 else if (oid
.equals(STREET
))
308 str
.append("STREET");
309 else if (oid
.equals(O
))
311 else if (oid
.equals(OU
))
313 else if (oid
.equals(T
))
315 else if (oid
.equals(DNQ
))
317 else if (oid
.equals(NAME
))
320 str
.append(oid
.toString());
329 return (stringRep
= str
.toString());
332 public byte[] getDer()
334 if (fixed
&& encoded
!= null)
335 return (byte[]) encoded
.clone();
337 ArrayList name
= new ArrayList(components
.size());
338 for (Iterator it
= components
.iterator(); it
.hasNext(); )
340 Map m
= (Map
) it
.next();
344 Set rdn
= new HashSet();
345 for (Iterator it2
= m
.entrySet().iterator(); it2
.hasNext(); )
347 Map
.Entry e
= (Map
.Entry
) it2
.next();
348 ArrayList atav
= new ArrayList(2);
349 atav
.add(new DERValue(DER
.OBJECT_IDENTIFIER
, e
.getKey()));
350 atav
.add(new DERValue(DER
.UTF8_STRING
, e
.getValue()));
351 rdn
.add(new DERValue(DER
.SEQUENCE
|DER
.CONSTRUCTED
, atav
));
353 name
.add(new DERValue(DER
.SET
|DER
.CONSTRUCTED
, rdn
));
355 DERValue val
= new DERValue(DER
.SEQUENCE
|DER
.CONSTRUCTED
, name
);
356 return (byte[]) (encoded
= val
.getEncoded()).clone();
360 // -------------------------------------------------------------------------
364 private void parseString(String str
) throws IOException
366 Reader in
= new StringReader(str
);
369 String key
= readAttributeType(in
);
372 String value
= readAttributeValue(in
);
373 putComponent(key
, value
);
375 newRelativeDistinguishedName();
380 private String
readAttributeType(Reader in
) throws IOException
382 CPStringBuilder buf
= new CPStringBuilder();
384 while ((ch
= in
.read()) != '=')
388 if (buf
.length() > 0)
389 throw new EOFException();
393 throw new IOException("Invalid char: " + (char) ch
);
394 if (Character
.isLetterOrDigit((char) ch
) || ch
== '-' || ch
== '.')
395 buf
.append((char) ch
);
397 throw new IOException("Invalid char: " + (char) ch
);
399 return buf
.toString();
402 private String
readAttributeValue(Reader in
) throws IOException
404 CPStringBuilder buf
= new CPStringBuilder();
411 if (('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
412 || Character
.isDigit((char) ch
))
413 buf
.append((char) ch
);
414 else if (ch
== '+' || ch
== ',')
417 String hex
= buf
.toString();
418 return new String(Util
.toByteArray(hex
));
421 throw new IOException("illegal character: " + (char) ch
);
435 throw new EOFException();
436 if (('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
437 || Character
.isDigit((char) ch
))
439 int i
= Character
.digit((char) ch
, 16) << 4;
441 if (!(('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
442 || Character
.isDigit((char) ch
)))
443 throw new IOException("illegal hex char");
444 i
|= Character
.digit((char) ch
, 16);
445 buf
.append((char) i
);
448 buf
.append((char) ch
);
451 buf
.append((char) ch
);
454 if (sep
!= '+' || sep
!= ',')
455 throw new IOException("illegal character: " + (char) ch
);
456 return buf
.toString();
467 return buf
.toString();
471 throw new EOFException();
472 if (('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
473 || Character
.isDigit((char) ch
))
475 int i
= Character
.digit((char) ch
, 16) << 4;
477 if (!(('a' <= ch
&& ch
<= 'f') || ('A' <= ch
&& ch
<= 'F')
478 || Character
.isDigit((char) ch
)))
479 throw new IOException("illegal hex char");
480 i
|= Character
.digit((char) ch
, 16);
481 buf
.append((char) i
);
484 buf
.append((char) ch
);
491 throw new IOException("illegal character: " + (char) ch
);
493 throw new EOFException();
495 buf
.append((char) ch
);
498 return buf
.toString();
504 private void parseDer(DERReader der
) throws IOException
506 DERValue name
= der
.read();
507 if (!name
.isConstructed())
508 throw new IOException("malformed Name");
509 encoded
= name
.getEncoded();
511 while (len
< name
.getLength())
513 DERValue rdn
= der
.read();
514 if (!rdn
.isConstructed())
515 throw new IOException("badly formed RDNSequence");
517 while (len2
< rdn
.getLength())
519 DERValue atav
= der
.read();
520 if (!atav
.isConstructed())
521 throw new IOException("badly formed AttributeTypeAndValue");
522 DERValue val
= der
.read();
523 if (val
.getTag() != DER
.OBJECT_IDENTIFIER
)
524 throw new IOException("badly formed AttributeTypeAndValue");
525 OID oid
= (OID
) val
.getValue();
527 if (!(val
.getValue() instanceof String
))
528 throw new IOException("badly formed AttributeTypeAndValue");
529 String value
= (String
) val
.getValue();
530 putComponent(oid
, value
);
531 len2
+= atav
.getEncodedLength();
533 len
+= rdn
.getEncodedLength();
534 if (len
< name
.getLength())
535 newRelativeDistinguishedName();
540 private static String
compressWS(String str
)
542 CPStringBuilder buf
= new CPStringBuilder();
544 for (int i
= 0; i
< str
.length(); i
++)
546 char c
= str
.charAt(i
);
547 if (Character
.isWhitespace(c
))
549 if (!Character
.isWhitespace(lastChar
))
556 return buf
.toString().trim();