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