1 /* base64x-1.1.12 (c) 2012-2017 Kenji Urushima | kjur.github.com/jsrsasign/license
  2  */
  3 /*
  4  * base64x.js - Base64url and supplementary functions for Tom Wu's base64.js library
  5  *
  6  * version: 1.1.12 (2017-Jun-03)
  7  *
  8  * Copyright (c) 2012-2017 Kenji Urushima (kenji.urushima@gmail.com)
  9  *
 10  * This software is licensed under the terms of the MIT License.
 11  * https://kjur.github.io/jsjws/license/
 12  *
 13  * The above copyright and license notice shall be 
 14  * included in all copies or substantial portions of the Software.
 15  *
 16  * DEPENDS ON:
 17  *   - base64.js - Tom Wu's Base64 library
 18  */
 19 
 20 /**
 21  * @fileOverview
 22  * @name base64x-1.1.js
 23  * @author Kenji Urushima kenji.urushima@gmail.com
 24  * @version jsrsasign 7.2.1 base64x 1.1.12 (2017-Jun-03)
 25  * @since jsrsasign 2.1
 26  * @license <a href="https://kjur.github.io/jsrsasign/license/">MIT License</a>
 27  */
 28 
 29 var KJUR;
 30 if (typeof KJUR == "undefined" || !KJUR) KJUR = {};
 31 if (typeof KJUR.lang == "undefined" || !KJUR.lang) KJUR.lang = {};
 32 
 33 /**
 34  * String and its utility class <br/>
 35  * This class provides some static utility methods for string.
 36  * @class String and its utility class
 37  * @author Kenji Urushima
 38  * @version 1.0 (2016-Aug-05)
 39  * @since base64x 1.1.7 jsrsasign 5.0.13
 40  * @description
 41  * <br/>
 42  * This class provides static methods for string utility.
 43  * <dl>
 44  * <dt><b>STRING TYPE CHECKERS</b>
 45  * <dd>
 46  * <ul>
 47  * <li>{@link KJUR.lang.String.isInteger} - check whether argument is an integer</li>
 48  * <li>{@link KJUR.lang.String.isHex} - check whether argument is a hexadecimal string</li>
 49  * <li>{@link KJUR.lang.String.isBase64} - check whether argument is a Base64 encoded string</li>
 50  * <li>{@link KJUR.lang.String.isBase64URL} - check whether argument is a Base64URL encoded string</li>
 51  * <li>{@link KJUR.lang.String.isIntegerArray} - check whether argument is an array of integers</li>
 52  * </ul>
 53  * </dl>
 54  */
 55 KJUR.lang.String = function() {};
 56 
 57 /**
 58  * Base64URL and supplementary functions for Tom Wu's base64.js library.<br/>
 59  * This class is just provide information about global functions
 60  * defined in 'base64x.js'. The 'base64x.js' script file provides
 61  * global functions for converting following data each other.
 62  * <ul>
 63  * <li>(ASCII) String</li>
 64  * <li>UTF8 String including CJK, Latin and other characters</li>
 65  * <li>byte array</li>
 66  * <li>hexadecimal encoded String</li>
 67  * <li>Full URIComponent encoded String (such like "%69%94")</li>
 68  * <li>Base64 encoded String</li>
 69  * <li>Base64URL encoded String</li>
 70  * </ul>
 71  * All functions in 'base64x.js' are defined in {@link global__} and not
 72  * in this class.
 73  * 
 74  * @class Base64URL and supplementary functions for Tom Wu's base64.js library
 75  * @author Kenji Urushima
 76  * @version 1.1 (07 May 2012)
 77  * @requires base64.js
 78  * @see <a href="https://kjur.github.io/jsjws/">'jwjws'(JWS JavaScript Library) home page https://kjur.github.io/jsjws/</a>
 79  * @see <a href="https://kjur.github.io/jsrsasigns/">'jwrsasign'(RSA Sign JavaScript Library) home page https://kjur.github.io/jsrsasign/</a>
 80  */
 81 function Base64x() {
 82 }
 83 
 84 // ==== string / byte array ================================
 85 /**
 86  * convert a string to an array of character codes
 87  * @name stoBA
 88  * @function
 89  * @param {String} s
 90  * @return {Array of Numbers} 
 91  */
 92 function stoBA(s) {
 93     var a = new Array();
 94     for (var i = 0; i < s.length; i++) {
 95 	a[i] = s.charCodeAt(i);
 96     }
 97     return a;
 98 }
 99 
100 /**
101  * convert an array of character codes to a string
102  * @name BAtos
103  * @function
104  * @param {Array of Numbers} a array of character codes
105  * @return {String} s
106  */
107 function BAtos(a) {
108     var s = "";
109     for (var i = 0; i < a.length; i++) {
110 	s = s + String.fromCharCode(a[i]);
111     }
112     return s;
113 }
114 
115 // ==== byte array / hex ================================
116 /**
117  * convert an array of bytes(Number) to hexadecimal string.<br/>
118  * @name BAtohex
119  * @function
120  * @param {Array of Numbers} a array of bytes
121  * @return {String} hexadecimal string
122  */
123 function BAtohex(a) {
124     var s = "";
125     for (var i = 0; i < a.length; i++) {
126 	var hex1 = a[i].toString(16);
127 	if (hex1.length == 1) hex1 = "0" + hex1;
128 	s = s + hex1;
129     }
130     return s;
131 }
132 
133 // ==== string / hex ================================
134 /**
135  * convert a ASCII string to a hexadecimal string of ASCII codes.<br/>
136  * NOTE: This can't be used for non ASCII characters.
137  * @name stohex
138  * @function
139  * @param {s} s ASCII string
140  * @return {String} hexadecimal string
141  */
142 function stohex(s) {
143     return BAtohex(stoBA(s));
144 }
145 
146 // ==== string / base64 ================================
147 /**
148  * convert a ASCII string to a Base64 encoded string.<br/>
149  * NOTE: This can't be used for non ASCII characters.
150  * @name stob64
151  * @function
152  * @param {s} s ASCII string
153  * @return {String} Base64 encoded string
154  */
155 function stob64(s) {
156     return hex2b64(stohex(s));
157 }
158 
159 // ==== string / base64url ================================
160 /**
161  * convert a ASCII string to a Base64URL encoded string.<br/>
162  * NOTE: This can't be used for non ASCII characters.
163  * @name stob64u
164  * @function
165  * @param {s} s ASCII string
166  * @return {String} Base64URL encoded string
167  */
168 function stob64u(s) {
169     return b64tob64u(hex2b64(stohex(s)));
170 }
171 
172 /**
173  * convert a Base64URL encoded string to a ASCII string.<br/>
174  * NOTE: This can't be used for Base64URL encoded non ASCII characters.
175  * @name b64utos
176  * @function
177  * @param {s} s Base64URL encoded string
178  * @return {String} ASCII string
179  */
180 function b64utos(s) {
181     return BAtos(b64toBA(b64utob64(s)));
182 }
183 
184 // ==== base64 / base64url ================================
185 /**
186  * convert a Base64 encoded string to a Base64URL encoded string.<br/>
187  * @name b64tob64u
188  * @function
189  * @param {String} s Base64 encoded string
190  * @return {String} Base64URL encoded string
191  * @example
192  * b64tob64u("ab+c3f/==") → "ab-c3f_"
193  */
194 function b64tob64u(s) {
195     s = s.replace(/\=/g, "");
196     s = s.replace(/\+/g, "-");
197     s = s.replace(/\//g, "_");
198     return s;
199 }
200 
201 /**
202  * convert a Base64URL encoded string to a Base64 encoded string.<br/>
203  * @name b64utob64
204  * @function
205  * @param {String} s Base64URL encoded string
206  * @return {String} Base64 encoded string
207  * @example
208  * b64utob64("ab-c3f_") → "ab+c3f/=="
209  */
210 function b64utob64(s) {
211     if (s.length % 4 == 2) s = s + "==";
212     else if (s.length % 4 == 3) s = s + "=";
213     s = s.replace(/-/g, "+");
214     s = s.replace(/_/g, "/");
215     return s;
216 }
217 
218 // ==== hex / base64url ================================
219 /**
220  * convert a hexadecimal string to a Base64URL encoded string.<br/>
221  * @name hextob64u
222  * @function
223  * @param {String} s hexadecimal string
224  * @return {String} Base64URL encoded string
225  * @description
226  * convert a hexadecimal string to a Base64URL encoded string.
227  * NOTE: If leading "0" is omitted and odd number length for
228  * hexadecimal leading "0" is automatically added.
229  */
230 function hextob64u(s) {
231     if (s.length % 2 == 1) s = "0" + s;
232     return b64tob64u(hex2b64(s));
233 }
234 
235 /**
236  * convert a Base64URL encoded string to a hexadecimal string.<br/>
237  * @name b64utohex
238  * @function
239  * @param {String} s Base64URL encoded string
240  * @return {String} hexadecimal string
241  */
242 function b64utohex(s) {
243     return b64tohex(b64utob64(s));
244 }
245 
246 // ==== utf8 / base64url ================================
247 
248 /**
249  * convert a UTF-8 encoded string including CJK or Latin to a Base64URL encoded string.<br/>
250  * @name utf8tob64u
251  * @function
252  * @param {String} s UTF-8 encoded string
253  * @return {String} Base64URL encoded string
254  * @since 1.1
255  */
256 
257 /**
258  * convert a Base64URL encoded string to a UTF-8 encoded string including CJK or Latin.<br/>
259  * @name b64utoutf8
260  * @function
261  * @param {String} s Base64URL encoded string
262  * @return {String} UTF-8 encoded string
263  * @since 1.1
264  */
265 
266 var utf8tob64u, b64utoutf8;
267 
268 if (typeof Buffer === 'function') {
269   utf8tob64u = function (s) {
270     return b64tob64u(new Buffer(s, 'utf8').toString('base64'));
271   };
272 
273   b64utoutf8 = function (s) {
274     return new Buffer(b64utob64(s), 'base64').toString('utf8');
275   };
276 } else {
277   utf8tob64u = function (s) {
278     return hextob64u(uricmptohex(encodeURIComponentAll(s)));
279   };
280 
281   b64utoutf8 = function (s) {
282     return decodeURIComponent(hextouricmp(b64utohex(s)));
283   };
284 }
285 
286 // ==== utf8 / base64url ================================
287 /**
288  * convert a UTF-8 encoded string including CJK or Latin to a Base64 encoded string.<br/>
289  * @name utf8tob64
290  * @function
291  * @param {String} s UTF-8 encoded string
292  * @return {String} Base64 encoded string
293  * @since 1.1.1
294  */
295 function utf8tob64(s) {
296   return hex2b64(uricmptohex(encodeURIComponentAll(s)));
297 }
298 
299 /**
300  * convert a Base64 encoded string to a UTF-8 encoded string including CJK or Latin.<br/>
301  * @name b64toutf8
302  * @function
303  * @param {String} s Base64 encoded string
304  * @return {String} UTF-8 encoded string
305  * @since 1.1.1
306  */
307 function b64toutf8(s) {
308   return decodeURIComponent(hextouricmp(b64tohex(s)));
309 }
310 
311 // ==== utf8 / hex ================================
312 /**
313  * convert a UTF-8 encoded string including CJK or Latin to a hexadecimal encoded string.<br/>
314  * @name utf8tohex
315  * @function
316  * @param {String} s UTF-8 encoded string
317  * @return {String} hexadecimal encoded string
318  * @since 1.1.1
319  */
320 function utf8tohex(s) {
321   return uricmptohex(encodeURIComponentAll(s));
322 }
323 
324 /**
325  * convert a hexadecimal encoded string to a UTF-8 encoded string including CJK or Latin.<br/>
326  * Note that when input is improper hexadecimal string as UTF-8 string, this function returns
327  * 'null'.
328  * @name hextoutf8
329  * @function
330  * @param {String} s hexadecimal encoded string
331  * @return {String} UTF-8 encoded string or null
332  * @since 1.1.1
333  */
334 function hextoutf8(s) {
335   return decodeURIComponent(hextouricmp(s));
336 }
337 
338 /**
339  * convert a hexadecimal encoded string to raw string including non printable characters.<br/>
340  * @name hextorstr
341  * @function
342  * @param {String} s hexadecimal encoded string
343  * @return {String} raw string
344  * @since 1.1.2
345  * @example
346  * hextorstr("610061") → "a\x00a"
347  */
348 function hextorstr(sHex) {
349     var s = "";
350     for (var i = 0; i < sHex.length - 1; i += 2) {
351         s += String.fromCharCode(parseInt(sHex.substr(i, 2), 16));
352     }
353     return s;
354 }
355 
356 /**
357  * convert a raw string including non printable characters to hexadecimal encoded string.<br/>
358  * @name rstrtohex
359  * @function
360  * @param {String} s raw string
361  * @return {String} hexadecimal encoded string
362  * @since 1.1.2
363  * @example
364  * rstrtohex("a\x00a") → "610061"
365  */
366 function rstrtohex(s) {
367     var result = "";
368     for (var i = 0; i < s.length; i++) {
369         result += ("0" + s.charCodeAt(i).toString(16)).slice(-2);
370     }
371     return result;
372 }
373 
374 // ==== hex / b64nl =======================================
375 
376 /**
377  * convert a hexadecimal string to Base64 encoded string<br/>
378  * @name hextob64
379  * @function
380  * @param {String} s hexadecimal string
381  * @return {String} resulted Base64 encoded string
382  * @since base64x 1.1.3
383  * @description
384  * This function converts from a hexadecimal string to Base64 encoded
385  * string without new lines.
386  * @example
387  * hextob64("616161") → "YWFh"
388  */
389 function hextob64(s) {
390     return hex2b64(s);
391 }
392 
393 /**
394  * convert a hexadecimal string to Base64 encoded string with new lines<br/>
395  * @name hextob64nl
396  * @function
397  * @param {String} s hexadecimal string
398  * @return {String} resulted Base64 encoded string with new lines
399  * @since base64x 1.1.3
400  * @description
401  * This function converts from a hexadecimal string to Base64 encoded
402  * string with new lines for each 64 characters. This is useful for
403  * PEM encoded file.
404  * @example
405  * hextob64nl("123456789012345678901234567890123456789012345678901234567890")
406  * →
407  * MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4 // new line
408  * OTAxMjM0NTY3ODkwCg==
409  */
410 function hextob64nl(s) {
411     var b64 = hextob64(s);
412     var b64nl = b64.replace(/(.{64})/g, "$1\r\n");
413     b64nl = b64nl.replace(/\r\n$/, '');
414     return b64nl;
415 }
416 
417 /**
418  * convert a Base64 encoded string with new lines to a hexadecimal string<br/>
419  * @name b64nltohex
420  * @function
421  * @param {String} s Base64 encoded string with new lines
422  * @return {String} hexadecimal string
423  * @since base64x 1.1.3
424  * @description
425  * This function converts from a Base64 encoded
426  * string with new lines to a hexadecimal string.
427  * This is useful to handle PEM encoded file.
428  * This function removes any non-Base64 characters (i.e. not 0-9,A-Z,a-z,\,+,=)
429  * including new line.
430  * @example
431  * hextob64nl(
432  * "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4\r\n" +
433  * "OTAxMjM0NTY3ODkwCg==\r\n")
434  * →
435  * "123456789012345678901234567890123456789012345678901234567890"
436  */
437 function b64nltohex(s) {
438     var b64 = s.replace(/[^0-9A-Za-z\/+=]*/g, '');
439     var hex = b64tohex(b64);
440     return hex;
441 } 
442 
443 // ==== hex / pem =========================================
444 
445 /**
446  * get PEM string from hexadecimal data and header string
447  * @name hextopem
448  * @function
449  * @param {String} dataHex hexadecimal string of PEM body
450  * @param {String} pemHeader PEM header string (ex. 'RSA PRIVATE KEY')
451  * @return {String} PEM formatted string of input data
452  * @since jsrasign 7.2.1 base64x 1.1.12
453  * @description
454  * This function converts a hexadecimal string to a PEM string with
455  * a specified header. Its line break will be CRLF("\r\n").
456  * @example
457  * hextopem('616161', 'RSA PRIVATE KEY') →
458  * -----BEGIN PRIVATE KEY-----
459  * YWFh
460  * -----END PRIVATE KEY-----
461  */
462 function hextopem(dataHex, pemHeader) {
463     var pemBody = hextob64nl(dataHex);
464     return "-----BEGIN " + pemHeader + "-----\r\n" + 
465         pemBody + 
466         "\r\n-----END " + pemHeader + "-----\r\n";
467 }
468 
469 /**
470  * get hexacedimal string from PEM format data<br/>
471  * @name pemtohex
472  * @function
473  * @param {String} s PEM formatted string
474  * @param {String} sHead PEM header string without BEGIN/END(OPTION)
475  * @return {String} hexadecimal string data of PEM contents
476  * @since jsrsasign 7.2.1 base64x 1.1.12
477  * @description
478  * This static method gets a hexacedimal string of contents 
479  * from PEM format data. You can explicitly specify PEM header 
480  * by sHead argument. 
481  * Any space characters such as white space or new line
482  * will be omitted.<br/>
483  * NOTE: Now {@link KEYUTIL.getHexFromPEM} and {@link X509.pemToHex}
484  * have been deprecated since jsrsasign 7.2.1. 
485  * Please use this method instead.
486  * @example
487  * pemtohex("-----BEGIN PUBLIC KEY...") → "3082..."
488  * pemtohex("-----BEGIN CERTIFICATE...", "CERTIFICATE") → "3082..."
489  * pemtohex(" \r\n-----BEGIN DSA PRIVATE KEY...") → "3082..."
490  */
491 function pemtohex(s, sHead) {
492     if (s.indexOf("-----BEGIN ") == -1)
493         throw "can't find PEM header: " + sHead;
494 
495     if (sHead !== undefined) {
496         s = s.replace("-----BEGIN " + sHead + "-----", "");
497         s = s.replace("-----END " + sHead + "-----", "");
498     } else {
499         s = s.replace(/-----BEGIN [^-]+-----/, '');
500         s = s.replace(/-----END [^-]+-----/, '');
501     }
502     return b64nltohex(s);
503 }
504 
505 // ==== hex / ArrayBuffer =================================
506 
507 /**
508  * convert a hexadecimal string to an ArrayBuffer<br/>
509  * @name hextoArrayBuffer
510  * @function
511  * @param {String} hex hexadecimal string
512  * @return {ArrayBuffer} ArrayBuffer
513  * @since jsrsasign 6.1.4 base64x 1.1.8
514  * @description
515  * This function converts from a hexadecimal string to an ArrayBuffer.
516  * @example
517  * hextoArrayBuffer("fffa01") → ArrayBuffer of [255, 250, 1]
518  */
519 function hextoArrayBuffer(hex) {
520     if (hex.length % 2 != 0) throw "input is not even length";
521     if (hex.match(/^[0-9A-Fa-f]+$/) == null) throw "input is not hexadecimal";
522 
523     var buffer = new ArrayBuffer(hex.length / 2);
524     var view = new DataView(buffer);
525 
526     for (var i = 0; i < hex.length / 2; i++) {
527 	view.setUint8(i, parseInt(hex.substr(i * 2, 2), 16));
528     }
529 
530     return buffer;
531 }
532 
533 // ==== ArrayBuffer / hex =================================
534 
535 /**
536  * convert an ArrayBuffer to a hexadecimal string<br/>
537  * @name ArrayBuffertohex
538  * @function
539  * @param {ArrayBuffer} buffer ArrayBuffer
540  * @return {String} hexadecimal string
541  * @since jsrsasign 6.1.4 base64x 1.1.8
542  * @description
543  * This function converts from an ArrayBuffer to a hexadecimal string.
544  * @example
545  * var buffer = new ArrayBuffer(3);
546  * var view = new DataView(buffer);
547  * view.setUint8(0, 0xfa);
548  * view.setUint8(1, 0xfb);
549  * view.setUint8(2, 0x01);
550  * ArrayBuffertohex(buffer) → "fafb01"
551  */
552 function ArrayBuffertohex(buffer) {
553     var hex = "";
554     var view = new DataView(buffer);
555 
556     for (var i = 0; i < buffer.byteLength; i++) {
557 	hex += ("00" + view.getUint8(i).toString(16)).slice(-2);
558     }
559 
560     return hex;
561 }
562 
563 // ==== zulu / int =================================
564 /**
565  * GeneralizedTime or UTCTime string to milliseconds from Unix origin<br>
566  * @name zulutomsec
567  * @function
568  * @param {String} s GeneralizedTime or UTCTime string (ex. 20170412235959.384Z)
569  * @return {Number} milliseconds from Unix origin time (i.e. Jan 1, 1970 0:00:00 UTC)
570  * @since jsrsasign 7.1.3 base64x 1.1.9
571  * @description
572  * This function converts from GeneralizedTime string (i.e. YYYYMMDDHHmmSSZ) or
573  * UTCTime string (i.e. YYMMDDHHmmSSZ) to milliseconds from Unix origin time
574  * (i.e. Jan 1 1970 0:00:00 UTC). 
575  * Argument string may have fraction of seconds and
576  * its length is one or more digits such as "20170410235959.1234567Z".
577  * As for UTCTime, if year "YY" is equal or less than 49 then it is 20YY.
578  * If year "YY" is equal or greater than 50 then it is 19YY.
579  * @example
580  * zulutomsec(  "071231235959Z")       → 1199145599000 #Mon, 31 Dec 2007 23:59:59 GMT
581  * zulutomsec(  "071231235959.1Z")     → 1199145599100 #Mon, 31 Dec 2007 23:59:59 GMT
582  * zulutomsec(  "071231235959.12345Z") → 1199145599123 #Mon, 31 Dec 2007 23:59:59 GMT
583  * zulutomsec("20071231235959Z")       → 1199145599000 #Mon, 31 Dec 2007 23:59:59 GMT
584  * zulutomsec(  "931231235959Z")       → -410227201000 #Mon, 31 Dec 1956 23:59:59 GMT
585  */
586 function zulutomsec(s) {
587     var year, month, day, hour, min, sec, msec, d;
588     var sYear, sFrac, sMsec, matchResult;
589 
590     matchResult = s.match(/^(\d{2}|\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(|\.\d+)Z$/);
591 
592     if (matchResult) {
593         sYear = matchResult[1];
594 	year = parseInt(sYear);
595         if (sYear.length === 2) {
596 	    if (50 <= year && year < 100) {
597 		year = 1900 + year;
598 	    } else if (0 <= year && year < 50) {
599 		year = 2000 + year;
600 	    }
601 	}
602 	month = parseInt(matchResult[2]) - 1;
603 	day = parseInt(matchResult[3]);
604 	hour = parseInt(matchResult[4]);
605 	min = parseInt(matchResult[5]);
606 	sec = parseInt(matchResult[6]);
607 	msec = 0;
608 
609 	sFrac = matchResult[7];
610 	if (sFrac !== "") {
611 	    sMsec = (sFrac.substr(1) + "00").substr(0, 3); // .12 -> 012
612 	    msec = parseInt(sMsec);
613 	}
614 	return Date.UTC(year, month, day, hour, min, sec, msec);
615     }
616     throw "unsupported zulu format: " + s;
617 }
618 
619 /**
620  * GeneralizedTime or UTCTime string to seconds from Unix origin<br>
621  * @name zulutosec
622  * @function
623  * @param {String} s GeneralizedTime or UTCTime string (ex. 20170412235959.384Z)
624  * @return {Number} seconds from Unix origin time (i.e. Jan 1, 1970 0:00:00 UTC)
625  * @since jsrsasign 7.1.3 base64x 1.1.9
626  * @description
627  * This function converts from GeneralizedTime string (i.e. YYYYMMDDHHmmSSZ) or
628  * UTCTime string (i.e. YYMMDDHHmmSSZ) to seconds from Unix origin time
629  * (i.e. Jan 1 1970 0:00:00 UTC). Argument string may have fraction of seconds 
630  * however result value will be omitted.
631  * As for UTCTime, if year "YY" is equal or less than 49 then it is 20YY.
632  * If year "YY" is equal or greater than 50 then it is 19YY.
633  * @example
634  * zulutosec(  "071231235959Z")       → 1199145599 #Mon, 31 Dec 2007 23:59:59 GMT
635  * zulutosec(  "071231235959.1Z")     → 1199145599 #Mon, 31 Dec 2007 23:59:59 GMT
636  * zulutosec("20071231235959Z")       → 1199145599 #Mon, 31 Dec 2007 23:59:59 GMT
637  */
638 function zulutosec(s) {
639     var msec = zulutomsec(s);
640     return ~~(msec / 1000);
641 }
642 
643 // ==== zulu / Date =================================
644 
645 /**
646  * GeneralizedTime or UTCTime string to Date object<br>
647  * @name zulutodate
648  * @function
649  * @param {String} s GeneralizedTime or UTCTime string (ex. 20170412235959.384Z)
650  * @return {Date} Date object for specified time
651  * @since jsrsasign 7.1.3 base64x 1.1.9
652  * @description
653  * This function converts from GeneralizedTime string (i.e. YYYYMMDDHHmmSSZ) or
654  * UTCTime string (i.e. YYMMDDHHmmSSZ) to Date object.
655  * Argument string may have fraction of seconds and
656  * its length is one or more digits such as "20170410235959.1234567Z".
657  * As for UTCTime, if year "YY" is equal or less than 49 then it is 20YY.
658  * If year "YY" is equal or greater than 50 then it is 19YY.
659  * @example
660  * zulutodate(  "071231235959Z").toUTCString()   → "Mon, 31 Dec 2007 23:59:59 GMT"
661  * zulutodate(  "071231235959.1Z").toUTCString() → "Mon, 31 Dec 2007 23:59:59 GMT"
662  * zulutodate("20071231235959Z").toUTCString()   → "Mon, 31 Dec 2007 23:59:59 GMT"
663  * zulutodate(  "071231235959.34").getMilliseconds() → 340
664  */
665 function zulutodate(s) {
666     return new Date(zulutomsec(s));
667 }
668 
669 // ==== Date / zulu =================================
670 
671 /**
672  * Date object to zulu time string<br>
673  * @name datetozulu
674  * @function
675  * @param {Date} d Date object for specified time
676  * @param {Boolean} flagUTCTime if this is true year will be YY otherwise YYYY
677  * @param {Boolean} flagMilli if this is true result concludes milliseconds
678  * @return {String} GeneralizedTime or UTCTime string (ex. 20170412235959.384Z)
679  * @since jsrsasign 7.2.0 base64x 1.1.11
680  * @description
681  * This function converts from Date object to GeneralizedTime string (i.e. YYYYMMDDHHmmSSZ) or
682  * UTCTime string (i.e. YYMMDDHHmmSSZ).
683  * As for UTCTime, if year "YY" is equal or less than 49 then it is 20YY.
684  * If year "YY" is equal or greater than 50 then it is 19YY.
685  * If flagMilli is true its result concludes milliseconds such like
686  * "20170520235959.42Z". 
687  * @example
688  * d = new Date(Date.UTC(2017,4,20,23,59,59,670));
689  * datetozulu(d) → "20170520235959Z"
690  * datetozulu(d, true) → "170520235959Z"
691  * datetozulu(d, false, true) → "20170520235959.67Z"
692  */
693 function datetozulu(d, flagUTCTime, flagMilli) {
694     var s;
695     var year = d.getUTCFullYear();
696     if (flagUTCTime) {
697 	if (year < 1950 || 2049 < year) 
698 	    throw "not proper year for UTCTime: " + year;
699 	s = ("" + year).slice(-2);
700     } else {
701 	s = ("000" + year).slice(-4);
702     }
703     s += ("0" + (d.getUTCMonth() + 1)).slice(-2);
704     s += ("0" + d.getUTCDate()).slice(-2);
705     s += ("0" + d.getUTCHours()).slice(-2);
706     s += ("0" + d.getUTCMinutes()).slice(-2);
707     s += ("0" + d.getUTCSeconds()).slice(-2);
708     if (flagMilli) {
709 	var milli = d.getUTCMilliseconds();
710 	if (milli !== 0) {
711 	    milli = ("00" + milli).slice(-3);
712 	    milli = milli.replace(/0+$/g, "");
713 	    s += "." + milli;
714 	}
715     }
716     s += "Z";
717     return s;
718 }
719 
720 // ==== URIComponent / hex ================================
721 /**
722  * convert a URLComponent string such like "%67%68" to a hexadecimal string.<br/>
723  * @name uricmptohex
724  * @function
725  * @param {String} s URIComponent string such like "%67%68"
726  * @return {String} hexadecimal string
727  * @since 1.1
728  */
729 function uricmptohex(s) {
730   return s.replace(/%/g, "");
731 }
732 
733 /**
734  * convert a hexadecimal string to a URLComponent string such like "%67%68".<br/>
735  * @name hextouricmp
736  * @function
737  * @param {String} s hexadecimal string
738  * @return {String} URIComponent string such like "%67%68"
739  * @since 1.1
740  */
741 function hextouricmp(s) {
742   return s.replace(/(..)/g, "%$1");
743 }
744 
745 // ==== URIComponent ================================
746 /**
747  * convert UTFa hexadecimal string to a URLComponent string such like "%67%68".<br/>
748  * Note that these "<code>0-9A-Za-z!'()*-._~</code>" characters will not
749  * converted to "%xx" format by builtin 'encodeURIComponent()' function.
750  * However this 'encodeURIComponentAll()' function will convert 
751  * all of characters into "%xx" format.
752  * @name encodeURIComponentAll
753  * @function
754  * @param {String} s hexadecimal string
755  * @return {String} URIComponent string such like "%67%68"
756  * @since 1.1
757  */
758 function encodeURIComponentAll(u8) {
759   var s = encodeURIComponent(u8);
760   var s2 = "";
761   for (var i = 0; i < s.length; i++) {
762     if (s[i] == "%") {
763       s2 = s2 + s.substr(i, 3);
764       i = i + 2;
765     } else {
766       s2 = s2 + "%" + stohex(s[i]);
767     }
768   }
769   return s2;
770 }
771 
772 // ==== new lines ================================
773 /**
774  * convert all DOS new line("\r\n") to UNIX new line("\n") in 
775  * a String "s".
776  * @name newline_toUnix
777  * @function
778  * @param {String} s string 
779  * @return {String} converted string
780  */
781 function newline_toUnix(s) {
782     s = s.replace(/\r\n/mg, "\n");
783     return s;
784 }
785 
786 /**
787  * convert all UNIX new line("\r\n") to DOS new line("\n") in 
788  * a String "s".
789  * @name newline_toDos
790  * @function
791  * @param {String} s string 
792  * @return {String} converted string
793  */
794 function newline_toDos(s) {
795     s = s.replace(/\r\n/mg, "\n");
796     s = s.replace(/\n/mg, "\r\n");
797     return s;
798 }
799 
800 // ==== string type checker ===================
801 
802 /**
803  * check whether a string is an integer string or not<br/>
804  * @name isInteger
805  * @memberOf KJUR.lang.String
806  * @function
807  * @static
808  * @param {String} s input string
809  * @return {Boolean} true if a string "s" is an integer string otherwise false
810  * @since base64x 1.1.7 jsrsasign 5.0.13
811  * @example
812  * KJUR.lang.String.isInteger("12345") → true
813  * KJUR.lang.String.isInteger("123ab") → false
814  */
815 KJUR.lang.String.isInteger = function(s) {
816     if (s.match(/^[0-9]+$/)) {
817 	return true;
818     } else if (s.match(/^-[0-9]+$/)) {
819 	return true;
820     } else {
821 	return false;
822     }
823 };
824 
825 /**
826  * check whether a string is an hexadecimal string or not<br/>
827  * @name isHex
828  * @memberOf KJUR.lang.String
829  * @function
830  * @static
831  * @param {String} s input string
832  * @return {Boolean} true if a string "s" is an hexadecimal string otherwise false
833  * @since base64x 1.1.7 jsrsasign 5.0.13
834  * @example
835  * KJUR.lang.String.isHex("1234") → true
836  * KJUR.lang.String.isHex("12ab") → true
837  * KJUR.lang.String.isHex("12AB") → true
838  * KJUR.lang.String.isHex("12ZY") → false
839  * KJUR.lang.String.isHex("121") → false -- odd length
840  */
841 KJUR.lang.String.isHex = function(s) {
842     if (s.length % 2 == 0 &&
843 	(s.match(/^[0-9a-f]+$/) || s.match(/^[0-9A-F]+$/))) {
844 	return true;
845     } else {
846 	return false;
847     }
848 };
849 
850 /**
851  * check whether a string is a base64 encoded string or not<br/>
852  * Input string can conclude new lines or space characters.
853  * @name isBase64
854  * @memberOf KJUR.lang.String
855  * @function
856  * @static
857  * @param {String} s input string
858  * @return {Boolean} true if a string "s" is a base64 encoded string otherwise false
859  * @since base64x 1.1.7 jsrsasign 5.0.13
860  * @example
861  * KJUR.lang.String.isBase64("YWE=") → true
862  * KJUR.lang.String.isBase64("YW_=") → false
863  * KJUR.lang.String.isBase64("YWE") → false -- length shall be multiples of 4
864  */
865 KJUR.lang.String.isBase64 = function(s) {
866     s = s.replace(/\s+/g, "");
867     if (s.match(/^[0-9A-Za-z+\/]+={0,3}$/) && s.length % 4 == 0) {
868 	return true;
869     } else {
870 	return false;
871     }
872 };
873 
874 /**
875  * check whether a string is a base64url encoded string or not<br/>
876  * Input string can conclude new lines or space characters.
877  * @name isBase64URL
878  * @memberOf KJUR.lang.String
879  * @function
880  * @static
881  * @param {String} s input string
882  * @return {Boolean} true if a string "s" is a base64url encoded string otherwise false
883  * @since base64x 1.1.7 jsrsasign 5.0.13
884  * @example
885  * KJUR.lang.String.isBase64URL("YWE") → true
886  * KJUR.lang.String.isBase64URL("YW-") → true
887  * KJUR.lang.String.isBase64URL("YW+") → false
888  */
889 KJUR.lang.String.isBase64URL = function(s) {
890     if (s.match(/[+/=]/)) return false;
891     s = b64utob64(s);
892     return KJUR.lang.String.isBase64(s);
893 };
894 
895 /**
896  * check whether a string is a string of integer array or not<br/>
897  * Input string can conclude new lines or space characters.
898  * @name isIntegerArray
899  * @memberOf KJUR.lang.String
900  * @function
901  * @static
902  * @param {String} s input string
903  * @return {Boolean} true if a string "s" is a string of integer array otherwise false
904  * @since base64x 1.1.7 jsrsasign 5.0.13
905  * @example
906  * KJUR.lang.String.isIntegerArray("[1,2,3]") → true
907  * KJUR.lang.String.isIntegerArray("  [1, 2, 3  ] ") → true
908  * KJUR.lang.String.isIntegerArray("[a,2]") → false
909  */
910 KJUR.lang.String.isIntegerArray = function(s) {
911     s = s.replace(/\s+/g, "");
912     if (s.match(/^\[[0-9,]+\]$/)) {
913 	return true;
914     } else {
915 	return false;
916     }
917 };
918 
919 // ==== others ================================
920 
921 /**
922  * canonicalize hexadecimal string of positive integer<br/>
923  * @name hextoposhex
924  * @function
925  * @param {String} s hexadecimal string 
926  * @return {String} canonicalized hexadecimal string of positive integer
927  * @since base64x 1.1.10 jsrsasign 7.1.4
928  * @description
929  * This method canonicalize a hexadecimal string of positive integer
930  * for two's complement representation.
931  * Canonicalized hexadecimal string of positive integer will be:
932  * <ul>
933  * <li>Its length is always even.</li>
934  * <li>If odd length it will be padded with leading zero.<li>
935  * <li>If it is even length and its first character is "8" or greater,
936  * it will be padded with "00" to make it positive integer.</li>
937  * </ul>
938  * @example
939  * hextoposhex("abcd") → "00abcd"
940  * hextoposhex("1234") → "1234"
941  * hextoposhex("12345") → "012345"
942  */
943 function hextoposhex(s) {
944     if (s.length % 2 == 1) return "0" + s;
945     if (s.substr(0, 1) > "7") return "00" + s;
946     return s;
947 }
948 
949 /**
950  * convert string of integer array to hexadecimal string.<br/>
951  * @name intarystrtohex
952  * @function
953  * @param {String} s string of integer array
954  * @return {String} hexadecimal string
955  * @since base64x 1.1.6 jsrsasign 5.0.2
956  * @throws "malformed integer array string: *" for wrong input
957  * @description
958  * This function converts a string of JavaScript integer array to
959  * a hexadecimal string. Each integer value shall be in a range 
960  * from 0 to 255 otherwise it raise exception. Input string can
961  * have extra space or newline string so that they will be ignored.
962  * 
963  * @example
964  * intarystrtohex(" [123, 34, 101, 34, 58] ")
965  * → 7b2265223a (i.e. '{"e":' as string)
966  */
967 function intarystrtohex(s) {
968   s = s.replace(/^\s*\[\s*/, '');
969   s = s.replace(/\s*\]\s*$/, '');
970   s = s.replace(/\s*/g, '');
971   try {
972     var hex = s.split(/,/).map(function(element, index, array) {
973       var i = parseInt(element);
974       if (i < 0 || 255 < i) throw "integer not in range 0-255";
975       var hI = ("00" + i.toString(16)).slice(-2);
976       return hI;
977     }).join('');
978     return hex;
979   } catch(ex) {
980     throw "malformed integer array string: " + ex;
981   }
982 }
983 
984 /**
985  * find index of string where two string differs
986  * @name strdiffidx
987  * @function
988  * @param {String} s1 string to compare
989  * @param {String} s2 string to compare
990  * @return {Number} string index of where character differs. Return -1 if same.
991  * @since jsrsasign 4.9.0 base64x 1.1.5
992  * @example
993  * strdiffidx("abcdefg", "abcd4fg") -> 4
994  * strdiffidx("abcdefg", "abcdefg") -> -1
995  * strdiffidx("abcdefg", "abcdef") -> 6
996  * strdiffidx("abcdefgh", "abcdef") -> 6
997  */
998 var strdiffidx = function(s1, s2) {
999     var n = s1.length;
1000     if (s1.length > s2.length) n = s2.length;
1001     for (var i = 0; i < n; i++) {
1002 	if (s1.charCodeAt(i) != s2.charCodeAt(i)) return i;
1003     }
1004     if (s1.length != s2.length) return n;
1005     return -1; // same
1006 };
1007 
1008 
1009