1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45:
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55: import ;
56: import ;
57: import ;
58: import ;
59: import ;
60: import ;
61:
62: public class X500DistinguishedName implements Principal
63: {
64:
65:
66:
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");
83:
84: private List components;
85: private Map currentRdn;
86: private boolean fixed;
87: private String stringRep;
88: private byte[] encoded;
89:
90:
91:
92:
93: public X500DistinguishedName()
94: {
95: components = new LinkedList();
96: currentRdn = new LinkedHashMap();
97: components.add(currentRdn);
98: }
99:
100: public X500DistinguishedName(String name)
101: {
102: this();
103: try
104: {
105: parseString(name);
106: }
107: catch (IOException ioe)
108: {
109: throw new IllegalArgumentException(ioe.toString());
110: }
111: }
112:
113: public X500DistinguishedName(byte[] encoded) throws IOException
114: {
115: this();
116: parseDer(new DERReader(encoded));
117: }
118:
119: public X500DistinguishedName(InputStream encoded) throws IOException
120: {
121: this();
122: parseDer(new DERReader(encoded));
123: }
124:
125:
126:
127:
128: public String getName()
129: {
130: return toString();
131: }
132:
133: public void newRelativeDistinguishedName()
134: {
135: if (fixed || currentRdn.isEmpty()) return;
136: currentRdn = new LinkedHashMap();
137: components.add(currentRdn);
138: }
139:
140: public int size()
141: {
142: return components.size();
143: }
144:
145: public int countComponents()
146: {
147: int count = 0;
148: for (Iterator it = components.iterator(); it.hasNext(); )
149: {
150: count += ((Map) it.next()).size();
151: }
152: return count;
153: }
154:
155: public boolean containsComponent(OID oid, String value)
156: {
157: for (Iterator it = components.iterator(); it.hasNext(); )
158: {
159: Map rdn = (Map) it.next();
160: String s = (String) rdn.get(oid);
161: if (s == null)
162: continue;
163: if (compressWS(value).equalsIgnoreCase(compressWS(s)))
164: return true;
165: }
166: return false;
167: }
168:
169: public String getComponent(OID oid)
170: {
171: for (Iterator it = components.iterator(); it.hasNext(); )
172: {
173: Map rdn = (Map) it.next();
174: if (rdn.containsKey(oid))
175: return (String) rdn.get(oid);
176: }
177: return null;
178: }
179:
180: public String getComponent(OID oid, int rdn)
181: {
182: if (rdn >= size())
183: return null;
184: return (String) ((Map) components.get(rdn)).get(oid);
185: }
186:
187: public void putComponent(OID oid, String value)
188: {
189: currentRdn.put(oid, value);
190: }
191:
192: public void putComponent(String name, String value)
193: {
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);
227: else
228: putComponent(new OID(name), value);
229: }
230:
231: public void setUnmodifiable()
232: {
233: if (fixed) return;
234: fixed = true;
235: List newComps = new ArrayList(components.size());
236: for (Iterator it = components.iterator(); it.hasNext(); )
237: {
238: Map rdn = (Map) it.next();
239: rdn = Collections.unmodifiableMap(rdn);
240: newComps.add(rdn);
241: }
242: components = Collections.unmodifiableList(newComps);
243: currentRdn = Collections.EMPTY_MAP;
244: }
245:
246: public int hashCode()
247: {
248: int sum = 0;
249: for (Iterator it = components.iterator(); it.hasNext(); )
250: {
251: Map m = (Map) it.next();
252: for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
253: {
254: Map.Entry e = (Map.Entry) it2.next();
255: sum += e.getKey().hashCode();
256: sum += e.getValue().hashCode();
257: }
258: }
259: return sum;
260: }
261:
262: public boolean equals(Object o)
263: {
264: if (!(o instanceof X500DistinguishedName))
265: return false;
266: if (size() != ((X500DistinguishedName) o).size())
267: return false;
268: for (int i = 0; i < size(); i++)
269: {
270: Map m = (Map) components.get(i);
271: for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
272: {
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)))
278: return false;
279: }
280: }
281: return true;
282: }
283:
284: public String toString()
285: {
286: if (fixed && stringRep != null)
287: return stringRep;
288: StringBuffer str = new StringBuffer();
289: for (Iterator it = components.iterator(); it.hasNext(); )
290: {
291: Map m = (Map) it.next();
292: for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
293: {
294: Map.Entry entry = (Map.Entry) it2.next();
295: OID oid = (OID) entry.getKey();
296: String value = (String) entry.getValue();
297: if (oid.equals(CN))
298: str.append("CN");
299: else if (oid.equals(C))
300: str.append("C");
301: else if (oid.equals(L))
302: str.append("L");
303: else if (oid.equals(ST))
304: str.append("ST");
305: else if (oid.equals(STREET))
306: str.append("STREET");
307: else if (oid.equals(O))
308: str.append("O");
309: else if (oid.equals(OU))
310: str.append("OU");
311: else if (oid.equals(T))
312: str.append("T");
313: else if (oid.equals(DNQ))
314: str.append("DNQ");
315: else if (oid.equals(NAME))
316: str.append("NAME");
317: else
318: str.append(oid.toString());
319: str.append('=');
320: str.append(value);
321: if (it2.hasNext())
322: str.append("+");
323: }
324: if (it.hasNext())
325: str.append(',');
326: }
327: return (stringRep = str.toString());
328: }
329:
330: public byte[] getDer()
331: {
332: if (fixed && encoded != null)
333: return (byte[]) encoded.clone();
334:
335: ArrayList name = new ArrayList(components.size());
336: for (Iterator it = components.iterator(); it.hasNext(); )
337: {
338: Map m = (Map) it.next();
339: if (m.isEmpty())
340: continue;
341:
342: Set rdn = new HashSet();
343: for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
344: {
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));
350: }
351: name.add(new DERValue(DER.SET|DER.CONSTRUCTED, rdn));
352: }
353: DERValue val = new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, name);
354: return (byte[]) (encoded = val.getEncoded()).clone();
355: }
356:
357:
358:
359:
360: private int sep;
361:
362: private void parseString(String str) throws IOException
363: {
364: Reader in = new StringReader(str);
365: while (true)
366: {
367: String key = readAttributeType(in);
368: if (key == null)
369: break;
370: String value = readAttributeValue(in);
371: putComponent(key, value);
372: if (sep == ',')
373: newRelativeDistinguishedName();
374: }
375: setUnmodifiable();
376: }
377:
378: private String readAttributeType(Reader in) throws IOException
379: {
380: StringBuffer buf = new StringBuffer();
381: int ch;
382: while ((ch = in.read()) != '=')
383: {
384: if (ch == -1)
385: {
386: if (buf.length() > 0)
387: throw new EOFException();
388: return null;
389: }
390: if (ch > 127)
391: throw new IOException("Invalid char: " + (char) ch);
392: if (Character.isLetterOrDigit((char) ch) || ch == '-' || ch == '.')
393: buf.append((char) ch);
394: else
395: throw new IOException("Invalid char: " + (char) ch);
396: }
397: return buf.toString();
398: }
399:
400: private String readAttributeValue(Reader in) throws IOException
401: {
402: StringBuffer buf = new StringBuffer();
403: int ch = in.read();
404: if (ch == '#')
405: {
406: while (true)
407: {
408: ch = in.read();
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 == ',')
413: {
414: sep = ch;
415: String hex = buf.toString();
416: return new String(Util.toByteArray(hex));
417: }
418: else
419: throw new IOException("illegal character: " + (char) ch);
420: }
421: }
422: else if (ch == '"')
423: {
424: while (true)
425: {
426: ch = in.read();
427: if (ch == '"')
428: break;
429: else if (ch == '\\')
430: {
431: ch = in.read();
432: if (ch == -1)
433: throw new EOFException();
434: if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
435: || Character.isDigit((char) ch))
436: {
437: int i = Character.digit((char) ch, 16) << 4;
438: ch = in.read();
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);
444: }
445: else
446: buf.append((char) ch);
447: }
448: else
449: buf.append((char) ch);
450: }
451: sep = in.read();
452: if (sep != '+' || sep != ',')
453: throw new IOException("illegal character: " + (char) ch);
454: return buf.toString();
455: }
456: else
457: {
458: while (true)
459: {
460: switch (ch)
461: {
462: case '+':
463: case ',':
464: sep = ch;
465: return buf.toString();
466: case '\\':
467: ch = in.read();
468: if (ch == -1)
469: throw new EOFException();
470: if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
471: || Character.isDigit((char) ch))
472: {
473: int i = Character.digit((char) ch, 16) << 4;
474: ch = in.read();
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);
480: }
481: else
482: buf.append((char) ch);
483: break;
484: case '=':
485: case '<':
486: case '>':
487: case '#':
488: case ';':
489: throw new IOException("illegal character: " + (char) ch);
490: case -1:
491: throw new EOFException();
492: default:
493: buf.append((char) ch);
494: ch = in.read();
495: if (ch == -1)
496: return buf.toString();
497: }
498: }
499: }
500: }
501:
502: private void parseDer(DERReader der) throws IOException
503: {
504: DERValue name = der.read();
505: if (!name.isConstructed())
506: throw new IOException("malformed Name");
507: encoded = name.getEncoded();
508: int len = 0;
509: while (len < name.getLength())
510: {
511: DERValue rdn = der.read();
512: if (!rdn.isConstructed())
513: throw new IOException("badly formed RDNSequence");
514: int len2 = 0;
515: while (len2 < rdn.getLength())
516: {
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();
524: val = der.read();
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();
530: }
531: len += rdn.getEncodedLength();
532: if (len < name.getLength())
533: newRelativeDistinguishedName();
534: }
535: setUnmodifiable();
536: }
537:
538: private static String compressWS(String str)
539: {
540: StringBuffer buf = new StringBuffer();
541: char lastChar = 0;
542: for (int i = 0; i < str.length(); i++)
543: {
544: char c = str.charAt(i);
545: if (Character.isWhitespace(c))
546: {
547: if (!Character.isWhitespace(lastChar))
548: buf.append(' ');
549: }
550: else
551: buf.append(c);
552: lastChar = c;
553: }
554: return buf.toString().trim();
555: }
556: }