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