2 * org.ibex.der.* - By Brian Alliet
3 * Copyright (C) 2004 Brian Alliet
5 * Based on Bouncy Castle by The Legion Of The Bouncy Castle
6 * Copyright (c) 2000 The Legion Of The Bouncy Castle
7 * (http://www.bouncycastle.org)
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDER.S BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
27 package org.ibex.crypto;
28 import java.io.IOException;
30 import java.text.ParseException;
31 import java.text.SimpleDateFormat;
33 import java.math.BigInteger;
36 public static class Null {
37 final static Null instance = new Null();
38 private Null() { /* noop */ }
39 public boolean equals(Object o) { return o == this; }
42 public static class TaggedObject {
43 public final Object object;
45 public TaggedObject(int tag, Object object) { this.tag = tag; this.object = object; }
48 public static class UnknownObject {
49 public final byte[] data;
51 public UnknownObject(int tag,byte[] data) { this.tag = tag; this.data = data; }
54 public static class BitString {
55 public final int paddingBits;
56 public final byte[] data;
58 public BitString(int paddingBits, byte[] data) {
59 this.paddingBits = paddingBits;
64 public static class Exception extends java.io.IOException {
65 public Exception(String s) { super(s); }
68 public static class InputStream extends FilterInputStream {
69 private static final int MAX_OBJECT_SIZE = 4*1024*1024;
72 public int bytesLeft() { return limit < 0 ? Integer.MAX_VALUE : limit-pos; }
74 public int getPos() { return pos; }
76 // hack around gcj bug
77 public static DER.InputStream New(java.io.InputStream is) { return new DER.InputStream(is); }
78 public InputStream(java.io.InputStream is) { this(is,-1); }
79 public InputStream(java.io.InputStream is, int limit) {
84 public int read() throws IOException {
85 if(limit >= 0 && pos >= limit) return -1;
93 public int read(byte[] buf, int start, int len) throws IOException {
95 if(pos >= limit) return -1;
96 len = Math.min(len,limit-pos);
98 int n = super.read(buf,start,len);
105 protected void readFully(byte[] buf) throws IOException {
107 int left = buf.length;
109 int n = read(buf,pos,left);
110 if(n == -1) throw new EOFException();
116 protected int readByte() throws IOException {
118 if(n == -1) throw new EOFException();
123 private int readLength() throws IOException {
125 if (length < 0) throw new IOException("EOF found when length expected");
126 if (length == 0x80) return -1; // indefinite-length encoding
128 int size = length & 0x7f;
130 for (int i = 0; i < size; i++) {
132 if (next < 0) throw new IOException("EOF found reading length");
133 length = (length << 8) + next;
139 public InputStream getSequenceStream() throws IOException {
140 int tag = readByte();
141 int length = readLength();
142 if(length < 0) throw new Exception("Indefinite length objects not supported");
143 if(tag != (CONSTRUCTED|0x10)) throw new Exception("Constructed Sequence expected");
144 return new InputStream(this,length);
147 private static final int CONSTRUCTED = 0x20;
148 public Object readObject() throws IOException {
149 int tag = readByte();
150 int length = readLength();
151 if(length < 0) throw new Exception("Indefinite length objects not supported");
152 if(length > MAX_OBJECT_SIZE) throw new Exception("Object too large");
155 case 0x01: return buildBoolean(length); // Boolean
156 case 0x02: return buildInteger(length); // Integer
157 case 0x03: return buildBitString(length); // Bit String
158 case 0x04: return buildOctetString(length); // Octet String
159 case 0x05: return Null.instance; // NULL
160 case 0x06: return buildObjectIdentifier(length); // Object Identifier
162 case 0x13: // PrintableString
163 // It is incorrect to treat this as an IA5String but the T.61 standard is way too old and backwards
164 // to be worth supporting
165 case 0x14: // T.61 String
166 case 0x16: // IA5String
167 return buildIA5String(length);
168 case 0x17: return buildTime(length,false);// UTC Time
169 case 0x18: return buildTime(length,true); // Generalizd Time
171 case CONSTRUCTED | 0x10: // Constructed Sequence
172 case CONSTRUCTED | 0x11: // Constructed Set
174 return buildSequence(length);
177 if((tag & 0x80) != 0) {
178 if ((tag & 0x1f) == 0x1f) throw new Exception("Unsupported high tag ecountered");
179 // tagged object - bottom 5 bits are tag
181 return new TaggedObject(tag&0x1,((tag & CONSTRUCTED) == 0) ? (Object)Null.instance : (Object)new Vector());
182 if((tag & CONSTRUCTED) == 0)
183 return new TaggedObject(tag&0x1f,buildOctetString(length));
185 InputStream dis = new InputStream(this,length);
186 Object o = dis.readObject();
187 if(dis.bytesLeft() == 0) return new TaggedObject(tag&0x1f,o);
188 Vector v = new Vector();
190 return buildSequence(dis,v);
192 return new UnknownObject(tag,readBytes(length));
198 protected Vector buildSequence(int length) throws IOException { return buildSequence(new InputStream(this,length),new Vector()); }
199 protected Vector buildSequence(InputStream dis,Vector v) throws IOException {
201 for(;;) v.addElement(dis.readObject());
202 } catch(EOFException e) {
207 protected byte[] readBytes(int length) throws IOException {
208 byte[] buf = new byte[length];
213 protected BigInteger buildInteger(int length) throws IOException { return new BigInteger(readBytes(length)); }
216 protected String buildObjectIdentifier(int length) throws IOException {
217 byte[] bytes = readBytes(length);
218 StringBuffer objId = new StringBuffer();
220 boolean first = true;
222 for (int i = 0; i != bytes.length; i++)
224 int b = bytes[i] & 0xff;
226 value = value * 128 + (b & 0x7f);
227 if ((b & 0x80) == 0) // end of number reached
248 objId.append(Integer.toString(value));
252 return objId.toString();
255 protected String buildIA5String(int length) throws IOException {
256 byte[] buf = readBytes(length);
257 char[] buf2 = new char[buf.length];
258 for(int i=0;i<buf.length;i++) buf2[i] = (char)(buf[i]&0xff);
259 return new String(buf2);
262 private static final SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
263 protected Date buildTime(int length, boolean generalized) throws IOException {
264 String s = buildIA5String(length);
265 if(!generalized && s.length() > 0) {
266 if(s.charAt(0) < '5') s = "20" + s;
270 case 13: s = s.substring(0,12) + "00GMT+00:00"; break; // YYYYMMDDhhmmZ
271 case 15: s = s.substring(0,14) + "GMT+00:00"; break; // YYYYMMDDhhmmssZ
272 case 17: s = s.substring(0,12) + "00GMT" + s.substring(12,15) + ":" + s.substring(15,17); break; // YYYYMMDDhhmm+hh'mm'
273 case 19: s = s.substring(0,14) + "GMT" + s.substring(14, 17) + ":" + s.substring(17, 19); // YYYYMMDDhhmmss+hh'mm'
274 default: throw new Exception("Unknown time format " + s);
277 return dateF.parse(s);
278 } catch(ParseException e) {
279 throw new Exception("Coudln't parse time: " + e.getMessage());
283 protected BitString buildBitString(int length) throws IOException {
284 if(length < 1) throw new Exception("bit string too short");
285 int padding = read();
286 if(padding == -1) throw new IOException("unexpected eof");
287 return new BitString(padding,readBytes(length-1));
290 protected byte[] buildOctetString(int length) throws IOException { return readBytes(length); }
292 protected Boolean buildBoolean(int length) throws IOException {
293 byte[] bytes = readBytes(length);
294 return bytes[0] != 0 ? Boolean.TRUE : Boolean.FALSE;
298 public static void main(String[] args) throws Exception {
299 InputStream is = new InputStream(new FileInputStream(args[0]));
301 for(;;) dump(is.readObject(),"");
302 } catch(EOFException e) {
303 System.err.println("EOF");
306 public static void dump(Object o, String indent) {
307 if(o instanceof Vector) {
308 Vector v = (Vector) o;
309 System.out.println(indent + "Sequence/Set");
310 for(int i=0;i<v.size();i++) {
311 dump(v.elementAt(i),indent + i + ": ");
313 } else if(o instanceof TaggedObject){
314 dump(((TaggedObject)o).object,indent + "Tagged object: ");
315 } else if(o instanceof byte[]) {
316 System.err.println(indent + "<Byte Array>");
318 System.err.println(indent + o.toString());