1 /*
2  * @(#) $Id: DefASN1PullParser.java,v 1.4 2003/07/08 08:13:52 pankaj Exp $
3  *
4  * Copyright (c) 2002-03 by Pankaj Kumar (http://www.pankaj-k.net). 
5  * All rights reserved.
6  *
7  * The license governing the use of this file can be found in the 
8  * root directory of the containing software.
9  */
10package org.jstk.asn1;
11
12import java.io.*;
13import java.util.*;
14import org.jstk.pem.*;
15import org.jstk.JSTKOptions;
16
17public class DefASN1PullParser implements ASN1PullParser {
18    private InputStream is;
19    private int curTagNumber = 0;
20    private byte curTagClass = 0x00;
21    private byte consMask = ASN1Type.PRIMITIVE;
22    private int curLength = 0;
23    private int curOffset = 0;
24    private int startOffset = 0;
25    private int curDepth = 0;
26    private int curHLength = 0;
27    private byte[] curContent = null;
28
29    private boolean prevFlag = false;
30    private int curEvent;
31
32    static class STypeObj {
33        int remainingLen;
34        int type;
35        STypeObj(int len, int type){
36            this.remainingLen = len;
37            this.type = type;
38        }
39    };
40
41    private Stack activeSTypes = new Stack();
42
43    public int next() throws ASN1PullParserException, IOException {
44        if (prevFlag){
45            prevFlag = false;
46            return curEvent;
47        }
48
49        curContent = null;
50        // Check if a SET or SEQ has ended
51        STypeObj activeSTypeObj = null;
52        if (!activeSTypes.empty()){
53            activeSTypeObj = (STypeObj)activeSTypes.peek();
54            if (activeSTypeObj.remainingLen == 0){
55                activeSTypes.pop();
56                --curDepth;
57                //activeSTypeObj = null;
58//System.out.println("SEQ or SET ended. activeSTypeObj.type = " + activeSTypeObj.type);
59                curEvent = (activeSTypeObj.type == SEQ ? END_SEQ : END_SET);
60                return curEvent;
61            }
62        }
63
64        if (is.available() == 0){   // End of file reached.
65//System.out.println("End of File");
66            curEvent = EOF;
67            return (curEvent);
68        }
69
70        startOffset = curOffset;
71//System.out.println("startOffset = " + startOffset);
72        // Get the tag number
73        byte curOctet = read(is);   // First octet.
74        curTagClass = (byte)(curOctet & CLASSBITS);
75        curTagNumber = (int)(curOctet & TAGBITS);
76        consMask = (byte)(curOctet & (byte)ASN1Type.CONSTRUCTED);
77//System.out.println("curTagClass = " + curTagClass + ", curTagNumber = " + curTagNumber);
78
79        // Check for high tag number format.
80        if (curTagNumber == TAGBITS){   // bits 5-1 are 1s ==> high tag number format
81System.out.println("High tag number format");
82            curTagNumber = 0;
83            int noOctets = 0;
84            do {
85                curOctet = (byte)is.read();
86                curTagNumber = 128*curTagNumber + (curOctet & 0x7f);
87                ++noOctets;
88                if (noOctets > 4)
89                    throw new ASN1PullParserException("tag number more than 4 octets");
90            } while ((curOctet & 0x80) == 0x80); // There is a possibility of a runaway loop.
91        }
92
93        // Get the length
94        curOctet = read(is);
95        if ((curOctet & 0x80) == 0){    // short form
96            curLength = curOctet & 0x7f;
97        } else {    // long form
98            int noOctets = curOctet & 0x7f;
99            if (noOctets > 4)
00                throw new ASN1PullParserException("length more than 4 octets");
01            curLength = 0;
02            for (int i = 0; i < noOctets; i++){
03                curOctet = read(is);
04                curLength = 256*curLength + (curOctet & 0xff);
05            }
06        }
07
08        curHLength = curOffset - startOffset;
09//System.out.println("hl = " + hl + ", curLength = " + curLength);
10        if (activeSTypeObj != null){
11            activeSTypeObj.remainingLen -= (curHLength + curLength);
12        }
13        curEvent = curTagNumber;
14        if (curTagNumber == SET){
15            activeSTypes.push(new STypeObj(curLength, SET));
16            ++curDepth;
17            curEvent = START_SET;
18        } else if (curTagNumber == SEQ){
19            activeSTypes.push(new STypeObj(curLength, SEQ));
20            ++curDepth;
21            curEvent = START_SEQ;
22        } else if (consMask == ASN1Type.CONSTRUCTED){
23            if (activeSTypeObj != null){
24                activeSTypeObj.remainingLen += curLength;
25            }
26        }
27
28        if (consMask == ASN1Type.PRIMITIVE)
29            curContent = read(is, curLength);
30        return curEvent;
31    }
32
33    public void prev() throws ASN1PullParserException {
34        prevFlag = true;
35    }
36
37    public int getOffset(){
38        return startOffset;
39    }
40
41    public int getDepth(){
42        return curDepth;
43    }
44
45    public int getHLength(){
46        return curHLength;
47    }
48
49    public int getLength(){
50        return curLength;
51    }
52
53    public int getType(){
54        return curTagNumber;
55    }
56
57    public int getTagNumber(){
58        return curTagNumber;
59    }
60
61    public byte getTagClass(){
62        return curTagClass;
63    }
64
65    public byte getConsMask(){
66        return consMask;
67    }
68
69    public String getTypeString(){
70        if ((consMask == ASN1Type.CONSTRUCTED) && (curTagNumber != SEQ) && (curTagNumber != SET)){
71            return "cons: [" + curTagNumber + "]";
72        }
73        switch (curTagNumber){
74            case BOOLEAN: return "prim: BOOLEAN";
75            case INTEGER: return "prim: INTEGER";
76            case BIT_STRING: return "prim: BIT STRING";
77            case OCTET_STRING: return "prim: OCTET STRING";
78            case NULL: return "prim: NULL";
79            case OID: return "prim: OBJECT";
80            case SEQ: return "cons: SEQUENCE";
81            case SET: return "cons: SET";
82            case PrintableString: return "prim: PRINTABLESTRING";
83            case T61String: return "prim: T61STRING";
84            case IA5String: return "prim: IA5STRING";
85            case UTCTime: return "prim: UTCTIME";
86            default: return "cons: " + curTagNumber;
87        }
88    }
89
90    public byte[] getContent(){
91        return curContent;
92    }
93    public int getInteger(){
94        return 0;
95    }
96    public void setInput(java.io.InputStream is){
97        this.is = is;
98        curOffset = 0;
99    }
00
01    public byte read(InputStream is) throws IOException {
02        int curByte = is.read();
03        if (curByte == -1)
04            throw new IOException("unexpected end of file");
05        ++curOffset;
06        return (byte)curByte;
07    }
08
09    public byte[] read(InputStream is, int nbytes) throws IOException {
10        byte[] bytes = new byte[nbytes];
11        int n = is.read(bytes);
12        if (n < nbytes)
13            throw new IOException("unexpected end of file");
14        curOffset += nbytes;
15        return bytes;
16    }
17
18    private static String formatInt(int num, int size){
19        String numS = Integer.toString(num);
20        return formatString(numS, size, false);
21    }
22
23    private static String formatString(String s, int size, boolean leftJustify){
24        StringBuffer sb = new StringBuffer();
25        if (s.length() >= size)
26            return s;
27        if (leftJustify)
28            sb.append(s);
29        for (int i = size - s.length(); i > 0; i--)
30            sb.append(" ");
31        if (!leftJustify)
32            sb.append(s);
33        return sb.toString();
34    }
35
36    public static void printParsed(InputStream is) throws IOException, ASN1PullParserException {
37        DefASN1PullParser parser = new DefASN1PullParser();
38        parser.setInput(is);
39        parser.printParsed(System.out);
40    }
41
42    public void printParsed(PrintStream ps) throws IOException, ASN1PullParserException {
43        int event;
44        while ((event =next()) != EOF){
45            if (event == END_SEQ || event == END_SET)
46                continue;
47
48            StringBuffer sb = new StringBuffer();
49            sb.append(formatInt(getOffset(), 5));
50            sb.append(":d=");
51            sb.append(formatInt(getDepth(), 1));
52            sb.append("  hl=");
53            sb.append(formatInt(getHLength(), 1));
54            sb.append(" l=");
55            sb.append(formatInt(getLength(), 4));
56            sb.append(" ");
57            sb.append(formatString(getTypeString(), 24, true));
58
59            if (event == OID){
60                sb.append(":");
61                ASN1Oid oid = new ASN1Oid();
62                oid.setValue(getContent());
63                sb.append(formatString(OidMap.getName(oid.toString()), 28, true));
64            } else if (event == PrintableString){
65                sb.append(":");
66                ASN1PrintableString aps = new ASN1PrintableString();
67                aps.setValue(getContent());
68                sb.append(formatString(aps.toString(), 28, true));
69            } else if (event == INTEGER){
70                sb.append(":");
71                ASN1Integer ai = new ASN1Integer();
72                ai.setValue(getContent());
73                sb.append(formatString(ai.toString(), 28, true));
74            } else if (event == BOOLEAN){
75                sb.append(":");
76                ASN1Boolean ab = new ASN1Boolean();
77                ab.setValue(getContent());
78                sb.append(formatString(ab.toString(), 28, true));
79            }
80            ps.println(sb.toString());
81        }
82    }
83
84    public static ASN1PullParser getInstance(byte[] bytes){
85        ASN1PullParser parser = new DefASN1PullParser();
86        parser.setInput(new ByteArrayInputStream(bytes));
87        return parser;
88    }
89
90    public static ASN1PullParser getInstance(InputStream is){
91        ASN1PullParser parser = new DefASN1PullParser();
92        parser.setInput(is);
93        return parser;
94    }
95
96    public static void main(String[] args) throws Exception {
97        if (args.length < 1 || "help".equalsIgnoreCase(args[0]) || "-help".equalsIgnoreCase(args[0])){
98            System.out.println("Usage::asn1parse <infile> [-encode <outfile>]");
99            return;
00        }
01        String file = args[0];
02        InputStream is = PEMData.getDERInputStream(file);
03        printParsed(is);
04
05        JSTKOptions opts = new JSTKOptions();
06        opts.parse(args, 1);
07        String outfile = opts.get("encode");
08        if (outfile != null){
09            System.out.println("Writing the data in DER format to file: " + outfile);
10            FileOutputStream fos = new FileOutputStream(outfile);
11            is = PEMData.getDERInputStream(file);
12            DefASN1PullParser parser = new DefASN1PullParser();
13            parser.setInput(is);
14
15            ASN1Any any = new ASN1Any();
16            any.decode(parser);
17            byte[] encoded = any.encode();
18            fos.write(encoded);
19            fos.close();
20        }
21    }
22
23}