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