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