1package org.bouncycastle.asn1.util; 2 3import java.io.IOException; 4import java.util.Enumeration; 5 6import org.bouncycastle.asn1.ASN1ApplicationSpecific; 7import org.bouncycastle.asn1.ASN1Boolean; 8import org.bouncycastle.asn1.ASN1Encodable; 9import org.bouncycastle.asn1.ASN1Enumerated; 10import org.bouncycastle.asn1.ASN1GeneralizedTime; 11import org.bouncycastle.asn1.ASN1Integer; 12import org.bouncycastle.asn1.ASN1ObjectIdentifier; 13import org.bouncycastle.asn1.ASN1OctetString; 14import org.bouncycastle.asn1.ASN1Primitive; 15import org.bouncycastle.asn1.ASN1Sequence; 16import org.bouncycastle.asn1.ASN1Set; 17import org.bouncycastle.asn1.ASN1TaggedObject; 18import org.bouncycastle.asn1.ASN1UTCTime; 19import org.bouncycastle.asn1.BERApplicationSpecific; 20import org.bouncycastle.asn1.BEROctetString; 21import org.bouncycastle.asn1.BERSequence; 22import org.bouncycastle.asn1.BERSet; 23import org.bouncycastle.asn1.BERTaggedObject; 24import org.bouncycastle.asn1.BERTags; 25import org.bouncycastle.asn1.DERApplicationSpecific; 26import org.bouncycastle.asn1.DERBMPString; 27import org.bouncycastle.asn1.DERBitString; 28import org.bouncycastle.asn1.DERExternal; 29import org.bouncycastle.asn1.DERGraphicString; 30import org.bouncycastle.asn1.DERIA5String; 31import org.bouncycastle.asn1.DERNull; 32import org.bouncycastle.asn1.DERPrintableString; 33import org.bouncycastle.asn1.DERSequence; 34import org.bouncycastle.asn1.DERT61String; 35import org.bouncycastle.asn1.DERUTF8String; 36import org.bouncycastle.asn1.DERVideotexString; 37import org.bouncycastle.asn1.DERVisibleString; 38import org.bouncycastle.util.Strings; 39import org.bouncycastle.util.encoders.Hex; 40 41/** 42 * Utility class for dumping ASN.1 objects as (hopefully) human friendly strings. 43 */ 44public class ASN1Dump 45{ 46 private static final String TAB = " "; 47 private static final int SAMPLE_SIZE = 32; 48 49 /** 50 * dump a DER object as a formatted string with indentation 51 * 52 * @param obj the ASN1Primitive to be dumped out. 53 */ 54 static void _dumpAsString( 55 String indent, 56 boolean verbose, 57 ASN1Primitive obj, 58 StringBuffer buf) 59 { 60 String nl = Strings.lineSeparator(); 61 if (obj instanceof ASN1Sequence) 62 { 63 Enumeration e = ((ASN1Sequence)obj).getObjects(); 64 String tab = indent + TAB; 65 66 buf.append(indent); 67 if (obj instanceof BERSequence) 68 { 69 buf.append("BER Sequence"); 70 } 71 else if (obj instanceof DERSequence) 72 { 73 buf.append("DER Sequence"); 74 } 75 else 76 { 77 buf.append("Sequence"); 78 } 79 80 buf.append(nl); 81 82 while (e.hasMoreElements()) 83 { 84 Object o = e.nextElement(); 85 86 if (o == null || o.equals(DERNull.INSTANCE)) 87 { 88 buf.append(tab); 89 buf.append("NULL"); 90 buf.append(nl); 91 } 92 else if (o instanceof ASN1Primitive) 93 { 94 _dumpAsString(tab, verbose, (ASN1Primitive)o, buf); 95 } 96 else 97 { 98 _dumpAsString(tab, verbose, ((ASN1Encodable)o).toASN1Primitive(), buf); 99 } 100 } 101 } 102 else if (obj instanceof ASN1TaggedObject) 103 { 104 String tab = indent + TAB; 105 106 buf.append(indent); 107 if (obj instanceof BERTaggedObject) 108 { 109 buf.append("BER Tagged ["); 110 } 111 else 112 { 113 buf.append("Tagged ["); 114 } 115 116 ASN1TaggedObject o = (ASN1TaggedObject)obj; 117 118 buf.append(Integer.toString(o.getTagNo())); 119 buf.append(']'); 120 121 if (!o.isExplicit()) 122 { 123 buf.append(" IMPLICIT "); 124 } 125 126 buf.append(nl); 127 128 if (o.isEmpty()) 129 { 130 buf.append(tab); 131 buf.append("EMPTY"); 132 buf.append(nl); 133 } 134 else 135 { 136 _dumpAsString(tab, verbose, o.getObject(), buf); 137 } 138 } 139 else if (obj instanceof ASN1Set) 140 { 141 Enumeration e = ((ASN1Set)obj).getObjects(); 142 String tab = indent + TAB; 143 144 buf.append(indent); 145 146 if (obj instanceof BERSet) 147 { 148 buf.append("BER Set"); 149 } 150 else 151 { 152 buf.append("DER Set"); 153 } 154 155 buf.append(nl); 156 157 while (e.hasMoreElements()) 158 { 159 Object o = e.nextElement(); 160 161 if (o == null) 162 { 163 buf.append(tab); 164 buf.append("NULL"); 165 buf.append(nl); 166 } 167 else if (o instanceof ASN1Primitive) 168 { 169 _dumpAsString(tab, verbose, (ASN1Primitive)o, buf); 170 } 171 else 172 { 173 _dumpAsString(tab, verbose, ((ASN1Encodable)o).toASN1Primitive(), buf); 174 } 175 } 176 } 177 else if (obj instanceof ASN1OctetString) 178 { 179 ASN1OctetString oct = (ASN1OctetString)obj; 180 181 if (obj instanceof BEROctetString) 182 { 183 buf.append(indent + "BER Constructed Octet String" + "[" + oct.getOctets().length + "] "); 184 } 185 else 186 { 187 buf.append(indent + "DER Octet String" + "[" + oct.getOctets().length + "] "); 188 } 189 if (verbose) 190 { 191 buf.append(dumpBinaryDataAsString(indent, oct.getOctets())); 192 } 193 else 194 { 195 buf.append(nl); 196 } 197 } 198 else if (obj instanceof ASN1ObjectIdentifier) 199 { 200 buf.append(indent + "ObjectIdentifier(" + ((ASN1ObjectIdentifier)obj).getId() + ")" + nl); 201 } 202 else if (obj instanceof ASN1Boolean) 203 { 204 buf.append(indent + "Boolean(" + ((ASN1Boolean)obj).isTrue() + ")" + nl); 205 } 206 else if (obj instanceof ASN1Integer) 207 { 208 buf.append(indent + "Integer(" + ((ASN1Integer)obj).getValue() + ")" + nl); 209 } 210 else if (obj instanceof DERBitString) 211 { 212 DERBitString bt = (DERBitString)obj; 213 buf.append(indent + "DER Bit String" + "[" + bt.getBytes().length + ", " + bt.getPadBits() + "] "); 214 if (verbose) 215 { 216 buf.append(dumpBinaryDataAsString(indent, bt.getBytes())); 217 } 218 else 219 { 220 buf.append(nl); 221 } 222 } 223 else if (obj instanceof DERIA5String) 224 { 225 buf.append(indent + "IA5String(" + ((DERIA5String)obj).getString() + ") " + nl); 226 } 227 else if (obj instanceof DERUTF8String) 228 { 229 buf.append(indent + "UTF8String(" + ((DERUTF8String)obj).getString() + ") " + nl); 230 } 231 else if (obj instanceof DERPrintableString) 232 { 233 buf.append(indent + "PrintableString(" + ((DERPrintableString)obj).getString() + ") " + nl); 234 } 235 else if (obj instanceof DERVisibleString) 236 { 237 buf.append(indent + "VisibleString(" + ((DERVisibleString)obj).getString() + ") " + nl); 238 } 239 else if (obj instanceof DERBMPString) 240 { 241 buf.append(indent + "BMPString(" + ((DERBMPString)obj).getString() + ") " + nl); 242 } 243 else if (obj instanceof DERT61String) 244 { 245 buf.append(indent + "T61String(" + ((DERT61String)obj).getString() + ") " + nl); 246 } 247 else if (obj instanceof DERGraphicString) 248 { 249 buf.append(indent + "GraphicString(" + ((DERGraphicString)obj).getString() + ") " + nl); 250 } 251 else if (obj instanceof DERVideotexString) 252 { 253 buf.append(indent + "VideotexString(" + ((DERVideotexString)obj).getString() + ") " + nl); 254 } 255 else if (obj instanceof ASN1UTCTime) 256 { 257 buf.append(indent + "UTCTime(" + ((ASN1UTCTime)obj).getTime() + ") " + nl); 258 } 259 else if (obj instanceof ASN1GeneralizedTime) 260 { 261 buf.append(indent + "GeneralizedTime(" + ((ASN1GeneralizedTime)obj).getTime() + ") " + nl); 262 } 263 else if (obj instanceof BERApplicationSpecific) 264 { 265 buf.append(outputApplicationSpecific("BER", indent, verbose, obj, nl)); 266 } 267 else if (obj instanceof DERApplicationSpecific) 268 { 269 buf.append(outputApplicationSpecific("DER", indent, verbose, obj, nl)); 270 } 271 else if (obj instanceof ASN1Enumerated) 272 { 273 ASN1Enumerated en = (ASN1Enumerated) obj; 274 buf.append(indent + "DER Enumerated(" + en.getValue() + ")" + nl); 275 } 276 else if (obj instanceof DERExternal) 277 { 278 DERExternal ext = (DERExternal) obj; 279 buf.append(indent + "External " + nl); 280 String tab = indent + TAB; 281 if (ext.getDirectReference() != null) 282 { 283 buf.append(tab + "Direct Reference: " + ext.getDirectReference().getId() + nl); 284 } 285 if (ext.getIndirectReference() != null) 286 { 287 buf.append(tab + "Indirect Reference: " + ext.getIndirectReference().toString() + nl); 288 } 289 if (ext.getDataValueDescriptor() != null) 290 { 291 _dumpAsString(tab, verbose, ext.getDataValueDescriptor(), buf); 292 } 293 buf.append(tab + "Encoding: " + ext.getEncoding() + nl); 294 _dumpAsString(tab, verbose, ext.getExternalContent(), buf); 295 } 296 else 297 { 298 buf.append(indent + obj.toString() + nl); 299 } 300 } 301 302 private static String outputApplicationSpecific(String type, String indent, boolean verbose, ASN1Primitive obj, String nl) 303 { 304 ASN1ApplicationSpecific app = ASN1ApplicationSpecific.getInstance(obj); 305 StringBuffer buf = new StringBuffer(); 306 307 if (app.isConstructed()) 308 { 309 try 310 { 311 ASN1Sequence s = ASN1Sequence.getInstance(app.getObject(BERTags.SEQUENCE)); 312 buf.append(indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "]" + nl); 313 for (Enumeration e = s.getObjects(); e.hasMoreElements();) 314 { 315 _dumpAsString(indent + TAB, verbose, (ASN1Primitive)e.nextElement(), buf); 316 } 317 } 318 catch (IOException e) 319 { 320 buf.append(e); 321 } 322 return buf.toString(); 323 } 324 325 return indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "] (" + Strings.fromByteArray(Hex.encode(app.getContents())) + ")" + nl; 326 } 327 328 /** 329 * dump out a DER object as a formatted string, in non-verbose mode. 330 * 331 * @param obj the ASN1Primitive to be dumped out. 332 * @return the resulting string. 333 */ 334 public static String dumpAsString( 335 Object obj) 336 { 337 return dumpAsString(obj, false); 338 } 339 340 /** 341 * Dump out the object as a string. 342 * 343 * @param obj the object to be dumped 344 * @param verbose if true, dump out the contents of octet and bit strings. 345 * @return the resulting string. 346 */ 347 public static String dumpAsString( 348 Object obj, 349 boolean verbose) 350 { 351 StringBuffer buf = new StringBuffer(); 352 353 if (obj instanceof ASN1Primitive) 354 { 355 _dumpAsString("", verbose, (ASN1Primitive)obj, buf); 356 } 357 else if (obj instanceof ASN1Encodable) 358 { 359 _dumpAsString("", verbose, ((ASN1Encodable)obj).toASN1Primitive(), buf); 360 } 361 else 362 { 363 return "unknown object type " + obj.toString(); 364 } 365 366 return buf.toString(); 367 } 368 369 private static String dumpBinaryDataAsString(String indent, byte[] bytes) 370 { 371 String nl = Strings.lineSeparator(); 372 StringBuffer buf = new StringBuffer(); 373 374 indent += TAB; 375 376 buf.append(nl); 377 for (int i = 0; i < bytes.length; i += SAMPLE_SIZE) 378 { 379 if (bytes.length - i > SAMPLE_SIZE) 380 { 381 buf.append(indent); 382 buf.append(Strings.fromByteArray(Hex.encode(bytes, i, SAMPLE_SIZE))); 383 buf.append(TAB); 384 buf.append(calculateAscString(bytes, i, SAMPLE_SIZE)); 385 buf.append(nl); 386 } 387 else 388 { 389 buf.append(indent); 390 buf.append(Strings.fromByteArray(Hex.encode(bytes, i, bytes.length - i))); 391 for (int j = bytes.length - i; j != SAMPLE_SIZE; j++) 392 { 393 buf.append(" "); 394 } 395 buf.append(TAB); 396 buf.append(calculateAscString(bytes, i, bytes.length - i)); 397 buf.append(nl); 398 } 399 } 400 401 return buf.toString(); 402 } 403 404 private static String calculateAscString(byte[] bytes, int off, int len) 405 { 406 StringBuffer buf = new StringBuffer(); 407 408 for (int i = off; i != off + len; i++) 409 { 410 if (bytes[i] >= ' ' && bytes[i] <= '~') 411 { 412 buf.append((char)bytes[i]); 413 } 414 } 415 416 return buf.toString(); 417 } 418} 419