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:
68: public static final OID CN = new OID("2.5.4.3");
69: public static final OID C = new OID("2.5.4.6");
70: public static final OID L = new OID("2.5.4.7");
71: public static final OID ST = new OID("2.5.4.8");
72: public static final OID STREET = new OID("2.5.4.9");
73: public static final OID O = new OID("2.5.4.10");
74: public static final OID OU = new OID("2.5.4.11");
75: public static final OID T = new OID("2.5.4.12");
76: public static final OID DNQ = new OID("2.5.4.46");
77: public static final OID NAME = new OID("2.5.4.41");
78: public static final OID GIVENNAME = new OID("2.5.4.42");
79: public static final OID INITIALS = new OID("2.5.4.43");
80: public static final OID GENERATION = new OID("2.5.4.44");
81: public static final OID EMAIL = new OID("1.2.840.113549.1.9.1");
82: public static final OID DC = new OID("0.9.2342.19200300.100.1.25");
83: public static final OID UID = new OID("0.9.2342.19200300.100.1.1");
84:
85: private List components;
86: private Map currentRdn;
87: private boolean fixed;
88: private String stringRep;
89: private byte[] encoded;
90:
91:
92:
93:
94: public X500DistinguishedName()
95: {
96: components = new LinkedList();
97: currentRdn = new LinkedHashMap();
98: components.add(currentRdn);
99: }
100:
101: public X500DistinguishedName(String name)
102: {
103: this();
104: try
105: {
106: parseString(name);
107: }
108: catch (IOException ioe)
109: {
110: throw new IllegalArgumentException(ioe.toString());
111: }
112: }
113:
114: public X500DistinguishedName(byte[] encoded) throws IOException
115: {
116: this();
117: parseDer(new DERReader(encoded));
118: }
119:
120: public X500DistinguishedName(InputStream encoded) throws IOException
121: {
122: this();
123: parseDer(new DERReader(encoded));
124: }
125:
126:
127:
128:
129: public String getName()
130: {
131: return toString();
132: }
133:
134: public void newRelativeDistinguishedName()
135: {
136: if (fixed || currentRdn.isEmpty()) return;
137: currentRdn = new LinkedHashMap();
138: components.add(currentRdn);
139: }
140:
141: public int size()
142: {
143: return components.size();
144: }
145:
146: public int countComponents()
147: {
148: int count = 0;
149: for (Iterator it = components.iterator(); it.hasNext(); )
150: {
151: count += ((Map) it.next()).size();
152: }
153: return count;
154: }
155:
156: public boolean containsComponent(OID oid, String value)
157: {
158: for (Iterator it = components.iterator(); it.hasNext(); )
159: {
160: Map rdn = (Map) it.next();
161: String s = (String) rdn.get(oid);
162: if (s == null)
163: continue;
164: if (compressWS(value).equalsIgnoreCase(compressWS(s)))
165: return true;
166: }
167: return false;
168: }
169:
170: public String getComponent(OID oid)
171: {
172: for (Iterator it = components.iterator(); it.hasNext(); )
173: {
174: Map rdn = (Map) it.next();
175: if (rdn.containsKey(oid))
176: return (String) rdn.get(oid);
177: }
178: return null;
179: }
180:
181: public String getComponent(OID oid, int rdn)
182: {
183: if (rdn >= size())
184: return null;
185: return (String) ((Map) components.get(rdn)).get(oid);
186: }
187:
188: public void putComponent(OID oid, String value)
189: {
190: currentRdn.put(oid, value);
191: }
192:
193: public void putComponent(String name, String value)
194: {
195: name = name.trim().toLowerCase();
196: if (name.equals("cn"))
197: putComponent(CN, value);
198: else if (name.equals("c"))
199: putComponent(C, value);
200: else if (name.equals("l"))
201: putComponent(L, value);
202: else if (name.equals("street"))
203: putComponent(STREET, value);
204: else if (name.equals("st"))
205: putComponent(ST, value);
206: else if (name.equals("t"))
207: putComponent(T, value);
208: else if (name.equals("dnq"))
209: putComponent(DNQ, value);
210: else if (name.equals("name"))
211: putComponent(NAME, value);
212: else if (name.equals("givenname"))
213: putComponent(GIVENNAME, value);
214: else if (name.equals("initials"))
215: putComponent(INITIALS, value);
216: else if (name.equals("generation"))
217: putComponent(GENERATION, value);
218: else if (name.equals("email"))
219: putComponent(EMAIL, value);
220: else if (name.equals("dc"))
221: putComponent(DC, value);
222: else if (name.equals("uid"))
223: putComponent(UID, value);
224: else
225: putComponent(new OID(name), value);
226: }
227:
228: public void setUnmodifiable()
229: {
230: if (fixed) return;
231: fixed = true;
232: List newComps = new ArrayList(components.size());
233: for (Iterator it = components.iterator(); it.hasNext(); )
234: {
235: Map rdn = (Map) it.next();
236: rdn = Collections.unmodifiableMap(rdn);
237: newComps.add(rdn);
238: }
239: components = Collections.unmodifiableList(newComps);
240: currentRdn = Collections.EMPTY_MAP;
241: }
242:
243: public int hashCode()
244: {
245: int sum = 0;
246: for (Iterator it = components.iterator(); it.hasNext(); )
247: {
248: Map m = (Map) it.next();
249: for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
250: {
251: Map.Entry e = (Map.Entry) it2.next();
252: sum += e.getKey().hashCode();
253: sum += e.getValue().hashCode();
254: }
255: }
256: return sum;
257: }
258:
259: public boolean equals(Object o)
260: {
261: if (!(o instanceof X500DistinguishedName))
262: return false;
263: if (size() != ((X500DistinguishedName) o).size())
264: return false;
265: for (int i = 0; i < size(); i++)
266: {
267: Map m = (Map) components.get(i);
268: for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
269: {
270: Map.Entry e = (Map.Entry) it2.next();
271: OID oid = (OID) e.getKey();
272: String v1 = (String) e.getValue();
273: String v2 = ((X500DistinguishedName) o).getComponent(oid, i);
274: if (!compressWS(v1).equalsIgnoreCase(compressWS(v2)))
275: return false;
276: }
277: }
278: return true;
279: }
280:
281: public String toString()
282: {
283: if (fixed && stringRep != null)
284: return stringRep;
285: StringBuffer str = new StringBuffer();
286: for (Iterator it = components.iterator(); it.hasNext(); )
287: {
288: Map m = (Map) it.next();
289: for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
290: {
291: Map.Entry entry = (Map.Entry) it2.next();
292: OID oid = (OID) entry.getKey();
293: String value = (String) entry.getValue();
294: if (oid.equals(CN))
295: str.append("CN");
296: else if (oid.equals(C))
297: str.append("C");
298: else if (oid.equals(L))
299: str.append("L");
300: else if (oid.equals(ST))
301: str.append("ST");
302: else if (oid.equals(STREET))
303: str.append("STREET");
304: else if (oid.equals(O))
305: str.append("O");
306: else if (oid.equals(OU))
307: str.append("OU");
308: else if (oid.equals(T))
309: str.append("T");
310: else if (oid.equals(DNQ))
311: str.append("DNQ");
312: else if (oid.equals(NAME))
313: str.append("NAME");
314: else
315: str.append(oid.toString());
316: str.append('=');
317: str.append(value);
318: if (it2.hasNext())
319: str.append("+");
320: }
321: if (it.hasNext())
322: str.append(',');
323: }
324: return (stringRep = str.toString());
325: }
326:
327: public byte[] getDer()
328: {
329: if (fixed && encoded != null)
330: return (byte[]) encoded.clone();
331: ArrayList name = new ArrayList(components.size());
332: for (Iterator it = components.iterator(); it.hasNext(); )
333: {
334: Map m = (Map) it.next();
335: if (m.isEmpty())
336: continue;
337: Set rdn = new HashSet();
338: for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); )
339: {
340: Map.Entry e = (Map.Entry) it.next();
341: ArrayList atav = new ArrayList(2);
342: atav.add(new DERValue(DER.OBJECT_IDENTIFIER, e.getKey()));
343: atav.add(new DERValue(DER.UTF8_STRING, e.getValue()));
344: rdn.add(new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, atav));
345: }
346: name.add(new DERValue(DER.SET|DER.CONSTRUCTED, rdn));
347: }
348: DERValue val = new DERValue(DER.SEQUENCE|DER.CONSTRUCTED, name);
349: return (byte[]) (encoded = val.getEncoded()).clone();
350: }
351:
352:
353:
354:
355: private int sep;
356:
357: private void parseString(String str) throws IOException
358: {
359: Reader in = new StringReader(str);
360: while (true)
361: {
362: String key = readAttributeType(in);
363: if (key == null)
364: break;
365: String value = readAttributeValue(in);
366: putComponent(key, value);
367: if (sep == ',')
368: newRelativeDistinguishedName();
369: }
370: setUnmodifiable();
371: }
372:
373: private String readAttributeType(Reader in) throws IOException
374: {
375: StringBuffer buf = new StringBuffer();
376: int ch;
377: while ((ch = in.read()) != '=')
378: {
379: if (ch == -1)
380: {
381: if (buf.length() > 0)
382: throw new EOFException();
383: return null;
384: }
385: if (ch > 127)
386: throw new IOException("Invalid char: " + (char) ch);
387: if (Character.isLetterOrDigit((char) ch) || ch == '-' || ch == '.')
388: buf.append((char) ch);
389: else
390: throw new IOException("Invalid char: " + (char) ch);
391: }
392: return buf.toString();
393: }
394:
395: private String readAttributeValue(Reader in) throws IOException
396: {
397: StringBuffer buf = new StringBuffer();
398: int ch = in.read();
399: if (ch == '#')
400: {
401: while (true)
402: {
403: ch = in.read();
404: if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
405: || Character.isDigit((char) ch))
406: buf.append((char) ch);
407: else if (ch == '+' || ch == ',')
408: {
409: sep = ch;
410: String hex = buf.toString();
411: return new String(Util.toByteArray(hex));
412: }
413: else
414: throw new IOException("illegal character: " + (char) ch);
415: }
416: }
417: else if (ch == '"')
418: {
419: while (true)
420: {
421: ch = in.read();
422: if (ch == '"')
423: break;
424: else if (ch == '\\')
425: {
426: ch = in.read();
427: if (ch == -1)
428: throw new EOFException();
429: if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
430: || Character.isDigit((char) ch))
431: {
432: int i = Character.digit((char) ch, 16) << 4;
433: ch = in.read();
434: if (!(('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
435: || Character.isDigit((char) ch)))
436: throw new IOException("illegal hex char");
437: i |= Character.digit((char) ch, 16);
438: buf.append((char) i);
439: }
440: else
441: buf.append((char) ch);
442: }
443: else
444: buf.append((char) ch);
445: }
446: sep = in.read();
447: if (sep != '+' || sep != ',')
448: throw new IOException("illegal character: " + (char) ch);
449: return buf.toString();
450: }
451: else
452: {
453: while (true)
454: {
455: switch (ch)
456: {
457: case '+':
458: case ',':
459: sep = ch;
460: return buf.toString();
461: case '\\':
462: ch = in.read();
463: if (ch == -1)
464: throw new EOFException();
465: if (('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
466: || Character.isDigit((char) ch))
467: {
468: int i = Character.digit((char) ch, 16) << 4;
469: ch = in.read();
470: if (!(('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F')
471: || Character.isDigit((char) ch)))
472: throw new IOException("illegal hex char");
473: i |= Character.digit((char) ch, 16);
474: buf.append((char) i);
475: }
476: else
477: buf.append((char) ch);
478: break;
479: case '=':
480: case '<':
481: case '>':
482: case '#':
483: case ';':
484: throw new IOException("illegal character: " + (char) ch);
485: case -1:
486: throw new EOFException();
487: default:
488: buf.append((char) ch);
489: }
490: }
491: }
492: }
493:
494: private void parseDer(DERReader der) throws IOException
495: {
496: DERValue name = der.read();
497: if (!name.isConstructed())
498: throw new IOException("malformed Name");
499: encoded = name.getEncoded();
500: int len = 0;
501: while (len < name.getLength())
502: {
503: DERValue rdn = der.read();
504: if (!rdn.isConstructed())
505: throw new IOException("badly formed RDNSequence");
506: int len2 = 0;
507: while (len2 < rdn.getLength())
508: {
509: DERValue atav = der.read();
510: if (!atav.isConstructed())
511: throw new IOException("badly formed AttributeTypeAndValue");
512: DERValue val = der.read();
513: if (val.getTag() != DER.OBJECT_IDENTIFIER)
514: throw new IOException("badly formed AttributeTypeAndValue");
515: OID oid = (OID) val.getValue();
516: val = der.read();
517: if (!(val.getValue() instanceof String))
518: throw new IOException("badly formed AttributeTypeAndValue");
519: String value = (String) val.getValue();
520: putComponent(oid, value);
521: len2 += atav.getEncodedLength();
522: }
523: len += rdn.getEncodedLength();
524: if (len < name.getLength())
525: newRelativeDistinguishedName();
526: }
527: setUnmodifiable();
528: }
529:
530: private static String compressWS(String str)
531: {
532: StringBuffer buf = new StringBuffer();
533: char lastChar = 0;
534: for (int i = 0; i < str.length(); i++)
535: {
536: char c = str.charAt(i);
537: if (Character.isWhitespace(c))
538: {
539: if (!Character.isWhitespace(lastChar))
540: buf.append(' ');
541: }
542: else
543: buf.append(c);
544: lastChar = c;
545: }
546: return buf.toString().trim();
547: }
548: }