001package org.apache.commons.ssl.org.bouncycastle.asn1.util;
002
003import java.io.IOException;
004import java.util.Enumeration;
005
006import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Boolean;
007import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Encodable;
008import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Enumerated;
009import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1GeneralizedTime;
010import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Integer;
011import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1ObjectIdentifier;
012import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1OctetString;
013import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Primitive;
014import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Sequence;
015import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1Set;
016import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1TaggedObject;
017import org.apache.commons.ssl.org.bouncycastle.asn1.ASN1UTCTime;
018import org.apache.commons.ssl.org.bouncycastle.asn1.BERApplicationSpecific;
019import org.apache.commons.ssl.org.bouncycastle.asn1.BEROctetString;
020import org.apache.commons.ssl.org.bouncycastle.asn1.BERSequence;
021import org.apache.commons.ssl.org.bouncycastle.asn1.BERSet;
022import org.apache.commons.ssl.org.bouncycastle.asn1.BERTaggedObject;
023import org.apache.commons.ssl.org.bouncycastle.asn1.BERTags;
024import org.apache.commons.ssl.org.bouncycastle.asn1.DERApplicationSpecific;
025import org.apache.commons.ssl.org.bouncycastle.asn1.DERBMPString;
026import org.apache.commons.ssl.org.bouncycastle.asn1.DERBitString;
027import org.apache.commons.ssl.org.bouncycastle.asn1.DERExternal;
028import org.apache.commons.ssl.org.bouncycastle.asn1.DERIA5String;
029import org.apache.commons.ssl.org.bouncycastle.asn1.DERNull;
030import org.apache.commons.ssl.org.bouncycastle.asn1.DERPrintableString;
031import org.apache.commons.ssl.org.bouncycastle.asn1.DERSequence;
032import org.apache.commons.ssl.org.bouncycastle.asn1.DERT61String;
033import org.apache.commons.ssl.org.bouncycastle.asn1.DERUTF8String;
034import org.apache.commons.ssl.org.bouncycastle.asn1.DERVisibleString;
035import org.bouncycastle.util.encoders.Hex;
036
037public class ASN1Dump
038{
039    private static final String  TAB = "    ";
040    private static final int SAMPLE_SIZE = 32;
041
042    /**
043     * dump a DER object as a formatted string with indentation
044     *
045     * @param obj the ASN1Primitive to be dumped out.
046     */
047    static void _dumpAsString(
048        String      indent,
049        boolean     verbose,
050        ASN1Primitive obj,
051        StringBuffer    buf)
052    {
053        String nl = System.getProperty("line.separator");
054        if (obj instanceof ASN1Sequence)
055        {
056            Enumeration     e = ((ASN1Sequence)obj).getObjects();
057            String          tab = indent + TAB;
058
059            buf.append(indent);
060            if (obj instanceof BERSequence)
061            {
062                buf.append("BER Sequence");
063            }
064            else if (obj instanceof DERSequence)
065            {
066                buf.append("DER Sequence");
067            }
068            else
069            {
070                buf.append("Sequence");
071            }
072
073            buf.append(nl);
074
075            while (e.hasMoreElements())
076            {
077                Object  o = e.nextElement();
078
079                if (o == null || o.equals(DERNull.INSTANCE))
080                {
081                    buf.append(tab);
082                    buf.append("NULL");
083                    buf.append(nl);
084                }
085                else if (o instanceof ASN1Primitive)
086                {
087                    _dumpAsString(tab, verbose, (ASN1Primitive)o, buf);
088                }
089                else
090                {
091                    _dumpAsString(tab, verbose, ((ASN1Encodable)o).toASN1Primitive(), buf);
092                }
093            }
094        }
095        else if (obj instanceof ASN1TaggedObject)
096        {
097            String          tab = indent + TAB;
098
099            buf.append(indent);
100            if (obj instanceof BERTaggedObject)
101            {
102                buf.append("BER Tagged [");
103            }
104            else
105            {
106                buf.append("Tagged [");
107            }
108
109            ASN1TaggedObject o = (ASN1TaggedObject)obj;
110
111            buf.append(Integer.toString(o.getTagNo()));
112            buf.append(']');
113
114            if (!o.isExplicit())
115            {
116                buf.append(" IMPLICIT ");
117            }
118
119            buf.append(nl);
120
121            if (o.isEmpty())
122            {
123                buf.append(tab);
124                buf.append("EMPTY");
125                buf.append(nl);
126            }
127            else
128            {
129                _dumpAsString(tab, verbose, o.getObject(), buf);
130            }
131        }
132        else if (obj instanceof ASN1Set)
133        {
134            Enumeration     e = ((ASN1Set)obj).getObjects();
135            String          tab = indent + TAB;
136
137            buf.append(indent);
138
139            if (obj instanceof BERSet)
140            {
141                buf.append("BER Set");
142            }
143            else
144            {
145                buf.append("DER Set");
146            }
147
148            buf.append(nl);
149
150            while (e.hasMoreElements())
151            {
152                Object  o = e.nextElement();
153
154                if (o == null)
155                {
156                    buf.append(tab);
157                    buf.append("NULL");
158                    buf.append(nl);
159                }
160                else if (o instanceof ASN1Primitive)
161                {
162                    _dumpAsString(tab, verbose, (ASN1Primitive)o, buf);
163                }
164                else
165                {
166                    _dumpAsString(tab, verbose, ((ASN1Encodable)o).toASN1Primitive(), buf);
167                }
168            }
169        }
170        else if (obj instanceof ASN1OctetString)
171        {
172            ASN1OctetString oct = (ASN1OctetString)obj;
173
174            if (obj instanceof BEROctetString)
175            {
176                buf.append(indent + "BER Constructed Octet String" + "[" + oct.getOctets().length + "] ");
177            }
178            else
179            {
180                buf.append(indent + "DER Octet String" + "[" + oct.getOctets().length + "] ");
181            }
182            if (verbose)
183            {
184                buf.append(dumpBinaryDataAsString(indent, oct.getOctets()));
185            }
186            else
187            {
188                buf.append(nl);
189            }
190        }
191        else if (obj instanceof ASN1ObjectIdentifier)
192        {
193            buf.append(indent + "ObjectIdentifier(" + ((ASN1ObjectIdentifier)obj).getId() + ")" + nl);
194        }
195        else if (obj instanceof ASN1Boolean)
196        {
197            buf.append(indent + "Boolean(" + ((ASN1Boolean)obj).isTrue() + ")" + nl);
198        }
199        else if (obj instanceof ASN1Integer)
200        {
201            buf.append(indent + "Integer(" + ((ASN1Integer)obj).getValue() + ")" + nl);
202        }
203        else if (obj instanceof DERBitString)
204        {
205            DERBitString bt = (DERBitString)obj;
206            buf.append(indent + "DER Bit String" + "[" + bt.getBytes().length + ", " + bt.getPadBits() + "] ");
207            if (verbose)
208            {
209                buf.append(dumpBinaryDataAsString(indent, bt.getBytes()));
210            }
211            else
212            {
213                buf.append(nl);
214            }
215        }
216        else if (obj instanceof DERIA5String)
217        {
218            buf.append(indent + "IA5String(" + ((DERIA5String)obj).getString() + ") " + nl);
219        }
220        else if (obj instanceof DERUTF8String)
221        {
222            buf.append(indent + "UTF8String(" + ((DERUTF8String)obj).getString() + ") " + nl);
223        }
224        else if (obj instanceof DERPrintableString)
225        {
226            buf.append(indent + "PrintableString(" + ((DERPrintableString)obj).getString() + ") " + nl);
227        }
228        else if (obj instanceof DERVisibleString)
229        {
230            buf.append(indent + "VisibleString(" + ((DERVisibleString)obj).getString() + ") " + nl);
231        }
232        else if (obj instanceof DERBMPString)
233        {
234            buf.append(indent + "BMPString(" + ((DERBMPString)obj).getString() + ") " + nl);
235        }
236        else if (obj instanceof DERT61String)
237        {
238            buf.append(indent + "T61String(" + ((DERT61String)obj).getString() + ") " + nl);
239        }
240        else if (obj instanceof ASN1UTCTime)
241        {
242            buf.append(indent + "UTCTime(" + ((ASN1UTCTime)obj).getTime() + ") " + nl);
243        }
244        else if (obj instanceof ASN1GeneralizedTime)
245        {
246            buf.append(indent + "GeneralizedTime(" + ((ASN1GeneralizedTime)obj).getTime() + ") " + nl);
247        }
248        else if (obj instanceof BERApplicationSpecific)
249        {
250            buf.append(outputApplicationSpecific("BER", indent, verbose, obj, nl));
251        }
252        else if (obj instanceof DERApplicationSpecific)
253        {
254            buf.append(outputApplicationSpecific("DER", indent, verbose, obj, nl));
255        }
256        else if (obj instanceof ASN1Enumerated)
257        {
258            ASN1Enumerated en = (ASN1Enumerated) obj;
259            buf.append(indent + "DER Enumerated(" + en.getValue() + ")" + nl);
260        }
261        else if (obj instanceof DERExternal)
262        {
263            DERExternal ext = (DERExternal) obj;
264            buf.append(indent + "External " + nl);
265            String          tab = indent + TAB;
266            if (ext.getDirectReference() != null)
267            {
268                buf.append(tab + "Direct Reference: " + ext.getDirectReference().getId() + nl);
269            }
270            if (ext.getIndirectReference() != null)
271            {
272                buf.append(tab + "Indirect Reference: " + ext.getIndirectReference().toString() + nl);
273            }
274            if (ext.getDataValueDescriptor() != null)
275            {
276                _dumpAsString(tab, verbose, ext.getDataValueDescriptor(), buf);
277            }
278            buf.append(tab + "Encoding: " + ext.getEncoding() + nl);
279            _dumpAsString(tab, verbose, ext.getExternalContent(), buf);
280        }
281        else
282        {
283            buf.append(indent + obj.toString() + nl);
284        }
285    }
286    
287    private static String outputApplicationSpecific(String type, String indent, boolean verbose, ASN1Primitive obj, String nl)
288    {
289        DERApplicationSpecific app = (DERApplicationSpecific)obj;
290        StringBuffer buf = new StringBuffer();
291
292        if (app.isConstructed())
293        {
294            try
295            {
296                ASN1Sequence s = ASN1Sequence.getInstance(app.getObject(BERTags.SEQUENCE));
297                buf.append(indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "]" + nl);
298                for (Enumeration e = s.getObjects(); e.hasMoreElements();)
299                {
300                    _dumpAsString(indent + TAB, verbose, (ASN1Primitive)e.nextElement(), buf);
301                }
302            }
303            catch (IOException e)
304            {
305                buf.append(e);
306            }
307            return buf.toString();
308        }
309
310        return indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "] (" + new String(Hex.encode(app.getContents())) + ")" + nl;
311    }
312
313    /**
314     * dump out a DER object as a formatted string, in non-verbose mode.
315     *
316     * @param obj the ASN1Primitive to be dumped out.
317     * @return  the resulting string.
318     */
319    public static String dumpAsString(
320        Object   obj)
321    {
322        return dumpAsString(obj, false);
323    }
324
325    /**
326     * Dump out the object as a string.
327     *
328     * @param obj  the object to be dumped
329     * @param verbose  if true, dump out the contents of octet and bit strings.
330     * @return  the resulting string.
331     */
332    public static String dumpAsString(
333        Object   obj,
334        boolean  verbose)
335    {
336        StringBuffer buf = new StringBuffer();
337
338        if (obj instanceof ASN1Primitive)
339        {
340            _dumpAsString("", verbose, (ASN1Primitive)obj, buf);
341        }
342        else if (obj instanceof ASN1Encodable)
343        {
344            _dumpAsString("", verbose, ((ASN1Encodable)obj).toASN1Primitive(), buf);
345        }
346        else
347        {
348            return "unknown object type " + obj.toString();
349        }
350
351        return buf.toString();
352    }
353
354    private static String dumpBinaryDataAsString(String indent, byte[] bytes)
355    {
356        String nl = System.getProperty("line.separator");
357        StringBuffer buf = new StringBuffer();
358
359        indent += TAB;
360        
361        buf.append(nl);
362        for (int i = 0; i < bytes.length; i += SAMPLE_SIZE)
363        {
364            if (bytes.length - i > SAMPLE_SIZE)
365            {
366                buf.append(indent);
367                buf.append(new String(Hex.encode(bytes, i, SAMPLE_SIZE)));
368                buf.append(TAB);
369                buf.append(calculateAscString(bytes, i, SAMPLE_SIZE));
370                buf.append(nl);
371            }
372            else
373            {
374                buf.append(indent);
375                buf.append(new String(Hex.encode(bytes, i, bytes.length - i)));
376                for (int j = bytes.length - i; j != SAMPLE_SIZE; j++)
377                {
378                    buf.append("  ");
379                }
380                buf.append(TAB);
381                buf.append(calculateAscString(bytes, i, bytes.length - i));
382                buf.append(nl);
383            }
384        }
385        
386        return buf.toString();
387    }
388
389    private static String calculateAscString(byte[] bytes, int off, int len)
390    {
391        StringBuffer buf = new StringBuffer();
392
393        for (int i = off; i != off + len; i++)
394        {
395            if (bytes[i] >= ' ' && bytes[i] <= '~')
396            {
397                buf.append((char)bytes[i]);
398            }
399        }
400
401        return buf.toString();
402    }
403}