1:
37:
38:
39: package ;
40:
41: import ;
42:
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52:
53:
84: public class CertificateURL extends Value implements Iterable<CertificateURL.URLAndOptionalHash>
85: {
86: private ByteBuffer buffer;
87:
88: public CertificateURL(final ByteBuffer buffer)
89: {
90: this.buffer = buffer;
91: }
92:
93: public CertificateURL(CertChainType type, List<URLAndOptionalHash> urls)
94: {
95: int length = 3;
96: for (URLAndOptionalHash url : urls)
97: length += url.length();
98: buffer = ByteBuffer.allocate(length);
99: buffer.put((byte) type.getValue());
100: buffer.putShort((short) (length - 1));
101: for (URLAndOptionalHash url : urls)
102: buffer.put(url.buffer());
103: buffer.rewind();
104: }
105:
106: public int length()
107: {
108: return 3 + (buffer.getShort(1) & 0xFFFF);
109: }
110:
111: public ByteBuffer buffer()
112: {
113: return (ByteBuffer) buffer.duplicate().limit(length());
114: }
115:
116: public CertChainType type()
117: {
118: switch (buffer.get(0))
119: {
120: case 0: return CertChainType.INDIVIDUAL_CERTS;
121: case 1: return CertChainType.PKIPATH;
122: }
123: throw new IllegalArgumentException("unknown certificate URL type");
124: }
125:
126: public int size()
127: {
128: int len = buffer.getShort(1) & 0xFFFF;
129: int n = 0;
130: for (int i = 3; i < len; )
131: {
132: URLAndOptionalHash u
133: = new URLAndOptionalHash((ByteBuffer) buffer.duplicate().position(i));
134: int l = u.length();
135: i += l;
136: n++;
137: }
138: return n;
139: }
140:
141: public URLAndOptionalHash get(int index)
142: {
143: int len = buffer.getShort(1) & 0xFFFF;
144: int n = 0;
145: int l = 0;
146: int i;
147: for (i = 3; i < len && n < index; )
148: {
149: URLAndOptionalHash u
150: = new URLAndOptionalHash((ByteBuffer) buffer.duplicate().position(i));
151: l = u.length();
152: i += l;
153: n++;
154: }
155: if (n < index)
156: throw new IndexOutOfBoundsException();
157: return new URLAndOptionalHash(((ByteBuffer) buffer.duplicate().position(i).limit(i+l)).slice());
158: }
159:
160: public void set(int index, URLAndOptionalHash url)
161: {
162: int len = buffer.getShort(1) & 0xFFFF;
163: int n = 0;
164: int i;
165: for (i = 3; i < len && n < index-1; )
166: {
167: URLAndOptionalHash u
168: = new URLAndOptionalHash((ByteBuffer) buffer.duplicate().position(i));
169: int l = u.length();
170: i += l;
171: n++;
172: }
173: if (n < index - 1)
174: throw new IndexOutOfBoundsException();
175: int l = url.urlLength();
176: buffer.putShort(i, (short) l);
177: ((ByteBuffer) buffer.duplicate().position(i+2)).put(url.urlBuffer());
178: buffer.put(i+l+2, (byte) (url.hashPresent() ? 1 : 0));
179: if (url.hashPresent())
180: ((ByteBuffer) buffer.duplicate().position(i+l+3)).put (url.sha1Hash());
181: }
182:
183: public void setLength(final int length)
184: {
185: if (length < 0 || length > 65535)
186: throw new IllegalArgumentException("length must be between 0 and 65535");
187: buffer.putShort(1, (short) length);
188: }
189:
190: public String toString()
191: {
192: return toString(null);
193: }
194:
195: public String toString(String prefix)
196: {
197: StringWriter str = new StringWriter();
198: PrintWriter out = new PrintWriter(str);
199: if (prefix != null) out.print(prefix);
200: out.println ("struct {");
201: if (prefix != null) out.print(prefix);
202: out.print(" type = ");
203: out.print(type());
204: out.println(";");
205: if (prefix != null) out.print(prefix);
206: out.println(" url_and_hash_list = {");
207: String subprefix = " ";
208: if (prefix != null) subprefix = prefix + subprefix;
209: for (URLAndOptionalHash url : this)
210: {
211: out.println(url.toString(subprefix));
212: }
213: if (prefix != null) out.print(prefix);
214: out.println(" };");
215: if (prefix != null) out.print(prefix);
216: out.print("} CertificateURL;");
217: return str.toString();
218: }
219:
220: public java.util.Iterator<URLAndOptionalHash> iterator()
221: {
222: return new Iterator();
223: }
224:
225: public class Iterator implements java.util.Iterator<URLAndOptionalHash>
226: {
227: private int index;
228:
229: public Iterator()
230: {
231: index = 0;
232: }
233:
234: public URLAndOptionalHash next() throws NoSuchElementException
235: {
236: try
237: {
238: return get(index++);
239: }
240: catch (IndexOutOfBoundsException ioobe)
241: {
242: throw new NoSuchElementException();
243: }
244: }
245:
246: public boolean hasNext()
247: {
248: return index < size();
249: }
250:
251: public void remove()
252: {
253: throw new UnsupportedOperationException();
254: }
255: }
256:
257: public static enum CertChainType
258: {
259: INDIVIDUAL_CERTS (0), PKIPATH (1);
260:
261: private final int value;
262:
263: private CertChainType (final int value)
264: {
265: this.value = value;
266: }
267:
268: public int getValue()
269: {
270: return value;
271: }
272: }
273:
274: public static class URLAndOptionalHash implements Builder, Constructed
275: {
276: private ByteBuffer buffer;
277:
278: public URLAndOptionalHash (final ByteBuffer buffer)
279: {
280: this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN);
281: }
282:
283: public URLAndOptionalHash(String url)
284: {
285: this(url, null);
286: }
287:
288: public URLAndOptionalHash(String url, byte[] hash)
289: {
290: if (hash != null && hash.length < 20)
291: throw new IllegalArgumentException();
292: int length = 3 + url.length();
293: if (hash != null)
294: length += 20;
295: buffer = ByteBuffer.allocate(length);
296: buffer.putShort((short) url.length());
297: Charset cs = Charset.forName("US-ASCII");
298: CharsetEncoder ascii = cs.newEncoder();
299: ascii.encode(CharBuffer.wrap(url), buffer, true);
300: buffer.put((byte) (hash != null ? 1 : 0));
301: if (hash != null)
302: buffer.put(hash, 0, 20);
303: buffer.rewind();
304: }
305:
306: public int length()
307: {
308: return ((buffer.getShort(0) & 0xFFFF)
309: + (hashPresent() ? 23 : 3));
310: }
311:
312: public ByteBuffer buffer()
313: {
314: return (ByteBuffer) buffer.duplicate().limit(length());
315: }
316:
317: public String url()
318: {
319: Charset cs = Charset.forName("ASCII");
320: return cs.decode(urlBuffer()).toString();
321: }
322:
323: public int urlLength()
324: {
325: return buffer.getShort(0) & 0xFFFF;
326: }
327:
328: public ByteBuffer urlBuffer()
329: {
330: int len = urlLength();
331: return ((ByteBuffer) buffer.duplicate().position(2).limit(2+len)).slice();
332: }
333:
334: public boolean hashPresent()
335: {
336: int i = (buffer.getShort(0) & 0xFFFF) + 2;
337: byte b = buffer.get(i);
338: if (b == 0)
339: return false;
340: if (b == 1)
341: return true;
342: throw new IllegalArgumentException("expecting 0 or 1: " + (b & 0xFF));
343: }
344:
345: public byte[] sha1Hash()
346: {
347: int i = (buffer.getShort(0) & 0xFFFF) + 2;
348: byte b = buffer.get(i);
349: if (b == 0)
350: return null;
351: byte[] buf = new byte[20];
352: ((ByteBuffer) buffer.duplicate().position(i+1)).get(buf);
353: return buf;
354: }
355:
356: public String toString()
357: {
358: return toString(null);
359: }
360:
361: public String toString(final String prefix)
362: {
363: StringWriter str = new StringWriter();
364: PrintWriter out = new PrintWriter(str);
365: if (prefix != null) out.print(prefix);
366: out.println("struct {");
367: if (prefix != null) out.print(prefix);
368: out.print(" url = ");
369: out.print(url());
370: out.println(";");
371: boolean has_hash = hashPresent();
372: if (prefix != null) out.print(prefix);
373: out.print(" hash_present = ");
374: out.print(has_hash);
375: out.println(";");
376: if (has_hash)
377: {
378: if (prefix != null) out.print(prefix);
379: out.print(" sha1Hash = ");
380: out.print(Util.toHexString(sha1Hash(), ':'));
381: out.println(";");
382: }
383: if (prefix != null) out.print(prefix);
384: out.print("} URLAndOptionalHash;");
385: return str.toString();
386: }
387: }
388: }