1 /*! asn1hex-1.1.9.js (c) 2012-2017 Kenji Urushima | kjur.github.com/jsrsasign/license
  2  */
  3 /*
  4  * asn1hex.js - Hexadecimal represented ASN.1 string library
  5  *
  6  * Copyright (c) 2010-2017 Kenji Urushima (kenji.urushima@gmail.com)
  7  *
  8  * This software is licensed under the terms of the MIT License.
  9  * http://kjur.github.com/jsrsasign/license/
 10  *
 11  * The above copyright and license notice shall be 
 12  * included in all copies or substantial portions of the Software.
 13  */
 14 
 15 /**
 16  * @fileOverview
 17  * @name asn1hex-1.1.js
 18  * @author Kenji Urushima kenji.urushima@gmail.com
 19  * @version asn1hex 1.1.9 (2017-Jan-14)
 20  * @license <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a>
 21  */
 22 
 23 /*
 24  * MEMO:
 25  *   f('3082025b02...', 2) ... 82025b ... 3bytes
 26  *   f('020100', 2) ... 01 ... 1byte
 27  *   f('0203001...', 2) ... 03 ... 1byte
 28  *   f('02818003...', 2) ... 8180 ... 2bytes
 29  *   f('3080....0000', 2) ... 80 ... -1
 30  *
 31  *   Requirements:
 32  *   - ASN.1 type octet length MUST be 1. 
 33  *     (i.e. ASN.1 primitives like SET, SEQUENCE, INTEGER, OCTETSTRING ...)
 34  */
 35 
 36 /**
 37  * ASN.1 DER encoded hexadecimal string utility class
 38  * @name ASN1HEX
 39  * @class ASN.1 DER encoded hexadecimal string utility class
 40  * @since jsrsasign 1.1
 41  * @description
 42  * This class provides a parser for hexadecimal string of
 43  * DER encoded ASN.1 binary data.
 44  * Here are major methods of this class.
 45  * <ul>
 46  * <li><b>ACCESS BY POSITION</b>
 47  *   <ul>
 48  *   <li>{@link ASN1HEX.getHexOfTLV_AtObj} - get ASN.1 TLV at specified position</li>
 49  *   <li>{@link ASN1HEX.getHexOfV_AtObj} - get ASN.1 V at specified position</li>
 50  *   <li>{@link ASN1HEX.getHexOfL_AtObj} - get hexadecimal ASN.1 L at specified position</li>
 51  *   <li>{@link ASN1HEX.getIntOfL_AtObj} - get integer ASN.1 L at specified position</li>
 52  *   <li>{@link ASN1HEX.getStartPosOfV_AtObj} - get ASN.1 V position from its ASN.1 TLV position</li>
 53  *   </ul>
 54  * </li>
 55  * <li><b>ACCESS FOR CHILD ITEM</b>
 56  *   <ul>
 57  *   <li>{@link ASN1HEX.getNthChildIndex_AtObj} - get nth child index at specified position</li>
 58  *   <li>{@link ASN1HEX.getPosArrayOfChildren_AtObj} - get indexes of children</li>
 59  *   <li>{@link ASN1HEX.getPosOfNextSibling_AtObj} - get position of next sibling</li>
 60  *   </ul>
 61  * </li>
 62  * <li><b>ACCESS NESTED ASN.1 STRUCTURE</b>
 63  *   <ul>
 64  *   <li>{@link ASN1HEX.getVbyList} - get ASN.1 V at specified nth list index with checking expected tag</li>
 65  *   <li>{@link ASN1HEX.getDecendantHexTLVByNthList} - get ASN.1 TLV at specified list index</li>
 66  *   <li>{@link ASN1HEX.getDecendantHexVByNthList} - get ASN.1 V at specified list index</li>
 67  *   <li>{@link ASN1HEX.getDecendantIndexByNthList} - get index at specified list index</li>
 68  *   </ul>
 69  * </li>
 70  * <li><b>UTILITIES</b>
 71  *   <ul>
 72  *   <li>{@link ASN1HEX.dump} - dump ASN.1 structure</li>
 73  *   <li>{@link ASN1HEX.isASN1HEX} - check whether ASN.1 hexadecimal string or not</li>
 74  *   <li>{@link ASN1HEX.hextooidstr} - convert hexadecimal string of OID to dotted integer list</li>
 75  *   </ul>
 76  * </li>
 77  * </ul>
 78  */
 79 var ASN1HEX = new function() {
 80 };
 81 
 82 /**
 83  * get byte length for ASN.1 L(length) bytes<br/>
 84  * @name getByteLengthOfL_AtObj
 85  * @memberOf ASN1HEX
 86  * @function
 87  * @param {String} s hexadecimal string of ASN.1 DER encoded data
 88  * @param {Number} pos string index
 89  * @return byte length for ASN.1 L(length) bytes
 90  */
 91 ASN1HEX.getByteLengthOfL_AtObj = function(s, pos) {
 92     if (s.substring(pos + 2, pos + 3) != '8') return 1;
 93     var i = parseInt(s.substring(pos + 3, pos + 4));
 94     if (i == 0) return -1;             // length octet '80' indefinite length
 95     if (0 < i && i < 10) return i + 1; // including '8?' octet;
 96     return -2;                         // malformed format
 97 };
 98 
 99 /**
100  * get hexadecimal string for ASN.1 L(length) bytes<br/>
101  * @name getHexOfL_AtObj
102  * @memberOf ASN1HEX
103  * @function
104  * @param {String} s hexadecimal string of ASN.1 DER encoded data
105  * @param {Number} pos string index
106  * @return {String} hexadecimal string for ASN.1 L(length) bytes
107  */
108 ASN1HEX.getHexOfL_AtObj = function(s, pos) {
109     var len = ASN1HEX.getByteLengthOfL_AtObj(s, pos);
110     if (len < 1) return '';
111     return s.substring(pos + 2, pos + 2 + len * 2);
112 };
113 
114 /**
115  * get integer value of ASN.1 length for ASN.1 data<br/>
116  * @name getIntOfL_AtObj
117  * @memberOf ASN1HEX
118  * @function
119  * @param {String} s hexadecimal string of ASN.1 DER encoded data
120  * @param {Number} pos string index
121  * @return ASN.1 L(length) integer value
122  */
123 /*
124  getting ASN.1 length value at the position 'idx' of
125  hexa decimal string 's'.
126  f('3082025b02...', 0) ... 82025b ... ???
127  f('020100', 0) ... 01 ... 1
128  f('0203001...', 0) ... 03 ... 3
129  f('02818003...', 0) ... 8180 ... 128
130  */
131 ASN1HEX.getIntOfL_AtObj = function(s, pos) {
132     var hLength = ASN1HEX.getHexOfL_AtObj(s, pos);
133     if (hLength == '') return -1;
134     var bi;
135     if (parseInt(hLength.substring(0, 1)) < 8) {
136         bi = new BigInteger(hLength, 16);
137     } else {
138         bi = new BigInteger(hLength.substring(2), 16);
139     }
140     return bi.intValue();
141 };
142 
143 /**
144  * get ASN.1 value starting string position for ASN.1 object refered by index 'idx'.
145  * @name getStartPosOfV_AtObj
146  * @memberOf ASN1HEX
147  * @function
148  * @param {String} s hexadecimal string of ASN.1 DER encoded data
149  * @param {Number} pos string index
150  */
151 ASN1HEX.getStartPosOfV_AtObj = function(s, pos) {
152     var l_len = ASN1HEX.getByteLengthOfL_AtObj(s, pos);
153     if (l_len < 0) return l_len;
154     return pos + (l_len + 1) * 2;
155 };
156 
157 /**
158  * get hexadecimal string of ASN.1 V(value)
159  * @name getHexOfV_AtObj
160  * @memberOf ASN1HEX
161  * @function
162  * @param {String} s hexadecimal string of ASN.1 DER encoded data
163  * @param {Number} pos string index
164  * @return {String} hexadecimal string of ASN.1 value.
165  */
166 ASN1HEX.getHexOfV_AtObj = function(s, pos) {
167     var pos1 = ASN1HEX.getStartPosOfV_AtObj(s, pos);
168     var len = ASN1HEX.getIntOfL_AtObj(s, pos);
169     return s.substring(pos1, pos1 + len * 2);
170 };
171 
172 /**
173  * get hexadecimal string of ASN.1 TLV at<br/>
174  * @name getHexOfTLV_AtObj
175  * @memberOf ASN1HEX
176  * @function
177  * @param {String} s hexadecimal string of ASN.1 DER encoded data
178  * @param {Number} pos string index
179  * @return {String} hexadecimal string of ASN.1 TLV.
180  * @since asn1hex 1.1
181  */
182 ASN1HEX.getHexOfTLV_AtObj = function(s, pos) {
183     var hT = s.substr(pos, 2);
184     var hL = ASN1HEX.getHexOfL_AtObj(s, pos);
185     var hV = ASN1HEX.getHexOfV_AtObj(s, pos);
186     return hT + hL + hV;
187 };
188 
189 // ========== sibling methods ================================
190 /**
191  * get next sibling starting index for ASN.1 object string<br/>
192  * @name getPosOfNextSibling_AtObj
193  * @memberOf ASN1HEX
194  * @function
195  * @param {String} s hexadecimal string of ASN.1 DER encoded data
196  * @param {Number} pos string index
197  * @return next sibling starting index for ASN.1 object string
198  */
199 ASN1HEX.getPosOfNextSibling_AtObj = function(s, pos) {
200     var pos1 = ASN1HEX.getStartPosOfV_AtObj(s, pos);
201     var len = ASN1HEX.getIntOfL_AtObj(s, pos);
202     return pos1 + len * 2;
203 };
204 
205 // ========== children methods ===============================
206 /**
207  * get array of string indexes of child ASN.1 objects<br/>
208  * @name getPosArrayOfChildren_AtObj
209  * @memberOf ASN1HEX
210  * @function
211  * @param {String} h hexadecimal string of ASN.1 DER encoded data
212  * @param {Number} pos start string index of ASN.1 object
213  * @return {Array of Number} array of indexes for childen of ASN.1 objects
214  * @description
215  * This method returns array of integers for a concatination of ASN.1 objects
216  * in a ASN.1 value. As for BITSTRING, one byte of unusedbits is skipped.
217  * As for other ASN.1 simple types such as INTEGER, OCTET STRING or PRINTABLE STRING,
218  * it returns a array of a string index of its ASN.1 value.<br/>
219  * NOTE: Since asn1hex 1.1.7 of jsrsasign 6.1.2, Encapsulated BitString is supported.
220  * @example
221  * ASN1HEX.getPosArrayOfChildren_AtObj("0203012345", 0) ⇒ [4] // INTEGER 012345
222  * ASN1HEX.getPosArrayOfChildren_AtObj("1303616161", 0) ⇒ [4] // PrintableString aaa
223  * ASN1HEX.getPosArrayOfChildren_AtObj("030300ffff", 0) ⇒ [6] // BITSTRING ffff (unusedbits=00a)
224  * ASN1HEX.getPosArrayOfChildren_AtObj("3006020104020105", 0) ⇒ [4, 10] // SEQUENCE(INT4,INT5)
225  */
226 ASN1HEX.getPosArrayOfChildren_AtObj = function(h, pos) {
227     var a = new Array();
228     var p0 = ASN1HEX.getStartPosOfV_AtObj(h, pos);
229     if (h.substr(pos, 2) == "03") {
230 	a.push(p0 + 2); // BITSTRING value without unusedbits
231     } else {
232 	a.push(p0);
233     }
234 
235     var len = ASN1HEX.getIntOfL_AtObj(h, pos);
236     var p = p0;
237     var k = 0;
238     while (1) {
239         var pNext = ASN1HEX.getPosOfNextSibling_AtObj(h, p);
240         if (pNext == null || (pNext - p0  >= (len * 2))) break;
241         if (k >= 200) break;
242             
243         a.push(pNext);
244         p = pNext;
245             
246         k++;
247     }
248     
249     return a;
250 };
251 
252 /**
253  * get string index of nth child object of ASN.1 object refered by h, idx<br/>
254  * @name getNthChildIndex_AtObj
255  * @memberOf ASN1HEX
256  * @function
257  * @param {String} h hexadecimal string of ASN.1 DER encoded data
258  * @param {Number} idx start string index of ASN.1 object
259  * @param {Number} nth for child
260  * @return {Number} string index of nth child.
261  * @since 1.1
262  */
263 ASN1HEX.getNthChildIndex_AtObj = function(h, idx, nth) {
264     var a = ASN1HEX.getPosArrayOfChildren_AtObj(h, idx);
265     return a[nth];
266 };
267 
268 // ========== decendant methods ==============================
269 /**
270  * get string index of nth child object of ASN.1 object refered by h, idx<br/>
271  * @name getDecendantIndexByNthList
272  * @memberOf ASN1HEX
273  * @function
274  * @param {String} h hexadecimal string of ASN.1 DER encoded data
275  * @param {Number} currentIndex start string index of ASN.1 object
276  * @param {Array of Number} nthList array list of nth
277  * @return {Number} string index refered by nthList
278  * @since 1.1
279  * @example
280  * The "nthList" is a index list of structured ASN.1 object
281  * reference. Here is a sample structure and "nthList"s which
282  * refers each objects.
283  *
284  * SQUENCE               - 
285  *   SEQUENCE            - [0]
286  *     IA5STRING 000     - [0, 0]
287  *     UTF8STRING 001    - [0, 1]
288  *   SET                 - [1]
289  *     IA5STRING 010     - [1, 0]
290  *     UTF8STRING 011    - [1, 1]
291  */
292 ASN1HEX.getDecendantIndexByNthList = function(h, currentIndex, nthList) {
293     if (nthList.length == 0) {
294         return currentIndex;
295     }
296     var firstNth = nthList.shift();
297     var a = ASN1HEX.getPosArrayOfChildren_AtObj(h, currentIndex);
298     return ASN1HEX.getDecendantIndexByNthList(h, a[firstNth], nthList);
299 };
300 
301 /**
302  * get hexadecimal string of ASN.1 TLV refered by current index and nth index list.
303  * @name getDecendantHexTLVByNthList
304  * @memberOf ASN1HEX
305  * @function
306  * @param {String} h hexadecimal string of ASN.1 DER encoded data
307  * @param {Number} currentIndex start string index of ASN.1 object
308  * @param {Array of Number} nthList array list of nth
309  * @return {Number} hexadecimal string of ASN.1 TLV refered by nthList
310  * @since 1.1
311  */
312 ASN1HEX.getDecendantHexTLVByNthList = function(h, currentIndex, nthList) {
313     var idx = ASN1HEX.getDecendantIndexByNthList(h, currentIndex, nthList);
314     return ASN1HEX.getHexOfTLV_AtObj(h, idx);
315 };
316 
317 /**
318  * get hexadecimal string of ASN.1 V refered by current index and nth index list.
319  * @name getDecendantHexVByNthList
320  * @memberOf ASN1HEX
321  * @function
322  * @param {String} h hexadecimal string of ASN.1 DER encoded data
323  * @param {Number} currentIndex start string index of ASN.1 object
324  * @param {Array of Number} nthList array list of nth
325  * @return {Number} hexadecimal string of ASN.1 V refered by nthList
326  * @since 1.1
327  */
328 ASN1HEX.getDecendantHexVByNthList = function(h, currentIndex, nthList) {
329     var idx = ASN1HEX.getDecendantIndexByNthList(h, currentIndex, nthList);
330     return ASN1HEX.getHexOfV_AtObj(h, idx);
331 };
332 
333 /**
334  * get ASN.1 value by nthList<br/>
335  * @name getVbyList
336  * @memberOf ASN1HEX
337  * @function
338  * @param {String} h hexadecimal string of ASN.1 structure
339  * @param {Integer} currentIndex string index to start searching in hexadecimal string "h"
340  * @param {Array} nthList array of nth list index
341  * @param {String} checkingTag (OPTIONAL) string of expected ASN.1 tag for nthList 
342  * @description
343  * This static method is to get a ASN.1 value which specified "nthList" position
344  * with checking expected tag "checkingTag".
345  * @since asn1hex 1.1.4
346  */
347 ASN1HEX.getVbyList = function(h, currentIndex, nthList, checkingTag) {
348     var idx = ASN1HEX.getDecendantIndexByNthList(h, currentIndex, nthList);
349     if (idx === undefined) {
350         throw "can't find nthList object";
351     }
352     if (checkingTag !== undefined) {
353         if (h.substr(idx, 2) != checkingTag) {
354             throw "checking tag doesn't match: " + 
355                 h.substr(idx,2) + "!=" + checkingTag;
356         }
357     }
358     return ASN1HEX.getHexOfV_AtObj(h, idx);
359 };
360 
361 /**
362  * get OID string from hexadecimal encoded value<br/>
363  * @name hextooidstr
364  * @memberOf ASN1HEX
365  * @function
366  * @param {String} hex hexadecmal string of ASN.1 DER encoded OID value
367  * @return {String} OID string (ex. '1.2.3.4.567')
368  * @since asn1hex 1.1.5
369  */
370 ASN1HEX.hextooidstr = function(hex) {
371     var zeroPadding = function(s, len) {
372         if (s.length >= len) return s;
373         return new Array(len - s.length + 1).join('0') + s;
374     };
375 
376     var a = [];
377 
378     // a[0], a[1]
379     var hex0 = hex.substr(0, 2);
380     var i0 = parseInt(hex0, 16);
381     a[0] = new String(Math.floor(i0 / 40));
382     a[1] = new String(i0 % 40);
383 
384     // a[2]..a[n]
385    var hex1 = hex.substr(2);
386     var b = [];
387     for (var i = 0; i < hex1.length / 2; i++) {
388     b.push(parseInt(hex1.substr(i * 2, 2), 16));
389     }
390     var c = [];
391     var cbin = "";
392     for (var i = 0; i < b.length; i++) {
393         if (b[i] & 0x80) {
394             cbin = cbin + zeroPadding((b[i] & 0x7f).toString(2), 7);
395         } else {
396             cbin = cbin + zeroPadding((b[i] & 0x7f).toString(2), 7);
397             c.push(new String(parseInt(cbin, 2)));
398             cbin = "";
399         }
400     }
401 
402     var s = a.join(".");
403     if (c.length > 0) s = s + "." + c.join(".");
404     return s;
405 };
406 
407 /**
408  * get string of simple ASN.1 dump from hexadecimal ASN.1 data<br/>
409  * @name dump
410  * @memberOf ASN1HEX
411  * @function
412  * @param {Object} hexOrObj hexadecmal string of ASN.1 data or ASN1Object object
413  * @param {Array} flags associative array of flags for dump (OPTION)
414  * @param {Number} idx string index for starting dump (OPTION)
415  * @param {String} indent indent string (OPTION)
416  * @return {String} string of simple ASN.1 dump
417  * @since jsrsasign 4.8.3 asn1hex 1.1.6
418  * @description
419  * This method will get an ASN.1 dump from
420  * hexadecmal string of ASN.1 DER encoded data.
421  * Here are features:
422  * <ul>
423  * <li>ommit long hexadecimal string</li>
424  * <li>dump encapsulated OCTET STRING (good for X.509v3 extensions)</li>
425  * <li>structured/primitive context specific tag support (i.e. [0], [3] ...)</li>
426  * <li>automatic decode for implicit primitive context specific tag 
427  * (good for X.509v3 extension value)
428  *   <ul>
429  *   <li>if hex starts '68747470'(i.e. http) it is decoded as utf8 encoded string.</li>
430  *   <li>if it is in 'subjectAltName' extension value and is '[2]'(dNSName) tag
431  *   value will be encoded as utf8 string</li>
432  *   <li>otherwise it shows as hexadecimal string</li>
433  *   </ul>
434  * </li>
435  * </ul>
436  * NOTE1: Argument {@link KJUR.asn1.ASN1Object} object is supported since
437  * jsrsasign 6.2.4 asn1hex 1.0.8
438  * @example
439  * // 1) ASN.1 INTEGER
440  * ASN1HEX.dump('0203012345')
441  * ↓
442  * INTEGER 012345
443  *
444  * // 2) ASN.1 Object Identifier
445  * ASN1HEX.dump('06052b0e03021a')
446  * ↓
447  * ObjectIdentifier sha1 (1 3 14 3 2 26)
448  *
449  * // 3) ASN.1 SEQUENCE
450  * ASN1HEX.dump('3006020101020102')
451  * ↓
452  * SEQUENCE
453  *   INTEGER 01
454  *   INTEGER 02
455  *
456  * // 4) ASN.1 SEQUENCE since jsrsasign 6.2.4
457  * o = KJUR.asn1.ASN1Util.newObject({seq: [{int: 1}, {int: 2}]});
458  * ASN1HEX.dump(o)
459  * ↓
460  * SEQUENCE
461  *   INTEGER 01
462  *   INTEGER 02
463  * // 5) ASN.1 DUMP FOR X.509 CERTIFICATE
464  * ASN1HEX.dump(ASN1HEX.pemToHex(certPEM))
465  * ↓
466  * SEQUENCE
467  *   SEQUENCE
468  *     [0]
469  *       INTEGER 02
470  *     INTEGER 0c009310d206dbe337553580118ddc87
471  *     SEQUENCE
472  *       ObjectIdentifier SHA256withRSA (1 2 840 113549 1 1 11)
473  *       NULL
474  *     SEQUENCE
475  *       SET
476  *         SEQUENCE
477  *           ObjectIdentifier countryName (2 5 4 6)
478  *           PrintableString 'US'
479  *             :
480  */
481 ASN1HEX.dump = function(hexOrObj, flags, idx, indent) {
482     var hex = hexOrObj;
483     if (hexOrObj instanceof KJUR.asn1.ASN1Object)
484 	hex = hexOrObj.getEncodedHex();
485 
486     var _skipLongHex = function(hex, limitNumOctet) {
487 	if (hex.length <= limitNumOctet * 2) {
488 	    return hex;
489 	} else {
490 	    var s = hex.substr(0, limitNumOctet) + 
491 		    "..(total " + hex.length / 2 + "bytes).." +
492 		    hex.substr(hex.length - limitNumOctet, limitNumOctet);
493 	    return s;
494 	};
495     };
496 
497     if (flags === undefined) flags = { "ommit_long_octet": 32 };
498     if (idx === undefined) idx = 0;
499     if (indent === undefined) indent = "";
500     var skipLongHex = flags.ommit_long_octet;
501 
502     if (hex.substr(idx, 2) == "01") {
503 	var v = ASN1HEX.getHexOfV_AtObj(hex, idx);
504 	if (v == "00") {
505 	    return indent + "BOOLEAN FALSE\n";
506 	} else {
507 	    return indent + "BOOLEAN TRUE\n";
508 	}
509     }
510     if (hex.substr(idx, 2) == "02") {
511 	var v = ASN1HEX.getHexOfV_AtObj(hex, idx);
512 	return indent + "INTEGER " + _skipLongHex(v, skipLongHex) + "\n";
513     }
514     if (hex.substr(idx, 2) == "03") {
515 	var v = ASN1HEX.getHexOfV_AtObj(hex, idx);
516 	return indent + "BITSTRING " + _skipLongHex(v, skipLongHex) + "\n";
517     }
518     if (hex.substr(idx, 2) == "04") {
519 	var v = ASN1HEX.getHexOfV_AtObj(hex, idx);
520 	if (ASN1HEX.isASN1HEX(v)) {
521 	    var s = indent + "OCTETSTRING, encapsulates\n";
522 	    s = s + ASN1HEX.dump(v, flags, 0, indent + "  ");
523 	    return s;
524 	} else {
525 	    return indent + "OCTETSTRING " + _skipLongHex(v, skipLongHex) + "\n";
526 	}
527     }
528     if (hex.substr(idx, 2) == "05") {
529 	return indent + "NULL\n";
530     }
531     if (hex.substr(idx, 2) == "06") {
532 	var hV = ASN1HEX.getHexOfV_AtObj(hex, idx);
533         var oidDot = KJUR.asn1.ASN1Util.oidHexToInt(hV);
534         var oidName = KJUR.asn1.x509.OID.oid2name(oidDot);
535 	var oidSpc = oidDot.replace(/\./g, ' ');
536         if (oidName != '') {
537   	    return indent + "ObjectIdentifier " + oidName + " (" + oidSpc + ")\n";
538 	} else {
539   	    return indent + "ObjectIdentifier (" + oidSpc + ")\n";
540 	}
541     }
542     if (hex.substr(idx, 2) == "0c") {
543 	return indent + "UTF8String '" + hextoutf8(ASN1HEX.getHexOfV_AtObj(hex, idx)) + "'\n";
544     }
545     if (hex.substr(idx, 2) == "13") {
546 	return indent + "PrintableString '" + hextoutf8(ASN1HEX.getHexOfV_AtObj(hex, idx)) + "'\n";
547     }
548     if (hex.substr(idx, 2) == "14") {
549 	return indent + "TeletexString '" + hextoutf8(ASN1HEX.getHexOfV_AtObj(hex, idx)) + "'\n";
550     }
551     if (hex.substr(idx, 2) == "16") {
552 	return indent + "IA5String '" + hextoutf8(ASN1HEX.getHexOfV_AtObj(hex, idx)) + "'\n";
553     }
554     if (hex.substr(idx, 2) == "17") {
555 	return indent + "UTCTime " + hextoutf8(ASN1HEX.getHexOfV_AtObj(hex, idx)) + "\n";
556     }
557     if (hex.substr(idx, 2) == "18") {
558 	return indent + "GeneralizedTime " + hextoutf8(ASN1HEX.getHexOfV_AtObj(hex, idx)) + "\n";
559     }
560     if (hex.substr(idx, 2) == "30") {
561 	if (hex.substr(idx, 4) == "3000") {
562 	    return indent + "SEQUENCE {}\n";
563 	}
564 
565 	var s = indent + "SEQUENCE\n";
566 	var aIdx = ASN1HEX.getPosArrayOfChildren_AtObj(hex, idx);
567 
568 	var flagsTemp = flags;
569 	
570 	if ((aIdx.length == 2 || aIdx.length == 3) &&
571 	    hex.substr(aIdx[0], 2) == "06" &&
572 	    hex.substr(aIdx[aIdx.length - 1], 2) == "04") { // supposed X.509v3 extension
573 	    var oidHex = ASN1HEX.getHexOfV_AtObj(hex, aIdx[0]);
574 	    var oidDot = KJUR.asn1.ASN1Util.oidHexToInt(oidHex);
575 	    var oidName = KJUR.asn1.x509.OID.oid2name(oidDot);
576 
577 	    var flagsClone = JSON.parse(JSON.stringify(flags));
578 	    flagsClone.x509ExtName = oidName;
579 	    flagsTemp = flagsClone;
580 	}
581 	
582 	for (var i = 0; i < aIdx.length; i++) {
583 	    s = s + ASN1HEX.dump(hex, flagsTemp, aIdx[i], indent + "  ");
584 	}
585 	return s;
586     }
587     if (hex.substr(idx, 2) == "31") {
588 	var s = indent + "SET\n";
589 	var aIdx = ASN1HEX.getPosArrayOfChildren_AtObj(hex, idx);
590 	for (var i = 0; i < aIdx.length; i++) {
591 	    s = s + ASN1HEX.dump(hex, flags, aIdx[i], indent + "  ");
592 	}
593 	return s;
594     }
595     var tag = parseInt(hex.substr(idx, 2), 16);
596     if ((tag & 128) != 0) { // context specific 
597 	var tagNumber = tag & 31;
598 	if ((tag & 32) != 0) { // structured tag
599 	    var s = indent + "[" + tagNumber + "]\n";
600 	    var aIdx = ASN1HEX.getPosArrayOfChildren_AtObj(hex, idx);
601 	    for (var i = 0; i < aIdx.length; i++) {
602 		s = s + ASN1HEX.dump(hex, flags, aIdx[i], indent + "  ");
603 	    }
604 	    return s;
605 	} else { // primitive tag
606 	    var v = ASN1HEX.getHexOfV_AtObj(hex, idx);
607 	    if (v.substr(0, 8) == "68747470") { // http
608 		v = hextoutf8(v);
609 	    }
610 	    if (flags.x509ExtName === "subjectAltName" &&
611 		tagNumber == 2) {
612 		v = hextoutf8(v);
613 	    }
614 	    
615 	    var s = indent + "[" + tagNumber + "] " + v + "\n";
616 	    return s;
617 	}
618     }
619     return indent + "UNKNOWN(" + hex.substr(idx, 2) + ") " + 
620 	   ASN1HEX.getHexOfV_AtObj(hex, idx) + "\n";
621 };
622 
623 /**
624  * check wheather the string is ASN.1 hexadecimal string or not
625  * @name isASN1HEX
626  * @memberOf ASN1HEX
627  * @function
628  * @param {String} hex string to check whether it is hexadecmal string for ASN.1 DER or not
629  * @return {Boolean} true if it is hexadecimal string of ASN.1 data otherwise false
630  * @since jsrsasign 4.8.3 asn1hex 1.1.6
631  * @description
632  * This method checks wheather the argument 'hex' is a hexadecimal string of
633  * ASN.1 data or not.
634  * @example
635  * ASN1HEX.isASN1HEX('0203012345') → true // PROPER ASN.1 INTEGER
636  * ASN1HEX.isASN1HEX('0203012345ff') → false // TOO LONG VALUE
637  * ASN1HEX.isASN1HEX('02030123') → false // TOO SHORT VALUE
638  * ASN1HEX.isASN1HEX('fa3bcd') → false // WRONG FOR ASN.1
639  */
640 ASN1HEX.isASN1HEX = function(hex) {
641     if (hex.length % 2 == 1) return false;
642 
643     var intL = ASN1HEX.getIntOfL_AtObj(hex, 0);
644     var tV = hex.substr(0, 2);
645     var lV = ASN1HEX.getHexOfL_AtObj(hex, 0);
646     var hVLength = hex.length - tV.length - lV.length;
647     if (hVLength == intL * 2) return true;
648 
649     return false;
650 };
651 
652 /**
653  * get hexacedimal string from PEM format data<br/>
654  * @name pemToHex
655  * @memberOf ASN1HEX
656  * @function
657  * @param {String} s PEM formatted string
658  * @param {String} sHead PEM header string without BEGIN/END(OPTION)
659  * @return {String} hexadecimal string data of PEM contents
660  * @since jsrsasign 7.0.1 asn1hex 1.1.9
661  * @description
662  * This static method gets a hexacedimal string of contents 
663  * from PEM format data. You can explicitly specify PEM header 
664  * by sHead argument. 
665  * Any space characters such as white space or new line
666  * will be omitted.<br/>
667  * NOTE: Now {@link KEYUTIL.getHexFromPEM} and {@link X509.pemToHex}
668  * have been deprecated since jsrsasign 7.0.1. 
669  * Please use this method instead.
670  * @example
671  * ASN1HEX.pemToHex("-----BEGIN PUBLIC KEY...") → "3082..."
672  * ASN1HEX.pemToHex("-----BEGIN CERTIFICATE...", "CERTIFICATE") → "3082..."
673  * ASN1HEX.pemToHex(" \r\n-----BEGIN DSA PRIVATE KEY...") → "3082..."
674  */
675 ASN1HEX.pemToHex = function(s, sHead) {
676     if (s.indexOf("-----BEGIN ") == -1)
677         throw "can't find PEM header: " + sHead;
678 
679     if (sHead !== undefined) {
680         s = s.replace("-----BEGIN " + sHead + "-----", "");
681         s = s.replace("-----END " + sHead + "-----", "");
682     } else {
683         s = s.replace(/-----BEGIN [^-]+-----/, '');
684         s = s.replace(/-----END [^-]+-----/, '');
685     }
686     var sB64 = s.replace(/\s+/g, '');
687     var dataHex = b64tohex(sB64);
688     return dataHex;
689 };
690