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