1 /*! pkcs5pkey-1.0.6.js (c) 2013-2014 Kenji Urushima | kjur.github.com/jsrsasign/license
  2  */
  3 /*
  4  * pkcs5pkey.js - reading passcode protected PKCS#5 PEM formatted RSA private key
  5  *
  6  * Copyright (c) 2013-2014 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  * @fileOverview
 16  * @name pkcs5pkey-1.0.js
 17  * @author Kenji Urushima kenji.urushima@gmail.com
 18  * @version pkcs5pkey 1.0.6 (2014-Apr-16)
 19  * @since jsrsasign 2.0.0
 20  * @license <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a>
 21  */
 22 
 23 /**
 24  * @name PKCS5PKEY
 25  * @class class for PKCS#5 and PKCS#8 private key 
 26  * @deprecated Since jsrsasign 4.1.3. Please use KEYUTIL class.
 27  * @description 
 28  * <br/>
 29  * {@link PKCS5PKEY} class has following features:
 30  * <ul>
 31  * <li>read and parse PEM formatted encrypted PKCS#5 private key
 32  * <li>generate PEM formatted encrypted PKCS#5 private key
 33  * <li>read and parse PEM formatted plain PKCS#8 private key
 34  * <li>read and parse PEM formatted encrypted PKCS#8 private key by PBKDF2/HmacSHA1/3DES
 35  * </ul>
 36  * Currently supports only RSA private key and
 37  * following symmetric key algorithms to protect private key.
 38  * <ul>
 39  * <li>DES-EDE3-CBC</li>
 40  * <li>AES-256-CBC</li>
 41  * <li>AES-192-CBC</li>
 42  * <li>AES-128-CBC</li>
 43  * </ul>
 44  * 
 45  * <h5>METHOD SUMMARY</h5>
 46  * <dl>
 47  * <dt><b>PKCS8 PRIVATE KEY METHODS</b><dd>
 48  * <ul>
 49  * <li>{@link PKCS5PKEY.getRSAKeyFromPlainPKCS8PEM} - convert plain PKCS8 PEM to RSAKey object</li>
 50  * <li>{@link PKCS5PKEY.getRSAKeyFromPlainPKCS8Hex} - convert plain PKCS8 hexadecimal data to RSAKey object</li>
 51  * <li>{@link PKCS5PKEY.getRSAKeyFromEncryptedPKCS8PEM} - convert encrypted PKCS8 PEM to RSAKey object</li>
 52  * <li>{@link PKCS5PKEY.getPlainPKCS8HexFromEncryptedPKCS8PEM} - convert encrypted PKCS8 PEM to plain PKCS8 Hex</li>
 53  * </ul>
 54  * <dt><b>PKCS5 PRIVATE KEY METHODS</b><dd>
 55  * <ul>
 56  * <li>{@link PKCS5PKEY.getRSAKeyFromEncryptedPKCS5PEM} - convert encrypted PKCS5 PEM to RSAKey object</li>
 57  * <li>{@link PKCS5PKEY.getEncryptedPKCS5PEMFromRSAKey} - convert RSAKey object to encryped PKCS5 PEM</li>
 58  * <li>{@link PKCS5PKEY.newEncryptedPKCS5PEM} - generate RSAKey and its encrypted PKCS5 PEM</li>
 59  * </ul>
 60  * <dt><b>PKCS8 PUBLIC KEY METHODS</b><dd>
 61  * <ul>
 62  * <li>{@link PKCS5PKEY.getKeyFromPublicPKCS8PEM} - convert encrypted PKCS8 PEM to RSAKey/ECDSA object</li>
 63  * <li>{@link PKCS5PKEY.getKeyFromPublicPKCS8Hex} - convert encrypted PKCS8 Hex to RSAKey/ECDSA object</li>
 64  * <li>{@link PKCS5PKEY.getRSAKeyFromPublicPKCS8PEM} - convert encrypted PKCS8 PEM to RSAKey object</li>
 65  * <li>{@link PKCS5PKEY.getRSAKeyFromPublicPKCS8Hex} - convert encrypted PKCS8 Hex to RSAKey object</li>
 66  * </ul>
 67  * <dt><b>UTITILIY METHODS</b><dd>
 68  * <ul>
 69  * <li>{@link PKCS5PKEY.getHexFromPEM} - convert PEM string to hexadecimal data</li>
 70  * <li>{@link PKCS5PKEY.getDecryptedKeyHexByKeyIV} - decrypt key by sharedKey and IV</li>
 71  * </ul>
 72  * </dl>
 73  * 
 74  * @example
 75  * Here is an example of PEM formatted encrypted PKCS#5 private key.
 76  * -----BEGIN RSA PRIVATE KEY-----
 77  * Proc-Type: 4,ENCRYPTED
 78  * DEK-Info: AES-256-CBC,40555967F759530864FE022E257DE34E
 79  *
 80  * jV7uXajRw4cccDaliagcqiLOiQEUCe19l761pXRxzgQP+DH4rCi12T4puTdZyy6l
 81  *          ...(snip)...
 82  * qxLS+BASmyGm4DME6m+kltZ12LXwPgNU6+d+XQ4NXSA=
 83  *-----END RSA PRIVATE KEY-----
 84  */
 85 var PKCS5PKEY = function() {
 86     // *****************************************************************
 87     // *** PRIVATE PROPERTIES AND METHODS *******************************
 88     // *****************************************************************
 89     // shared key decryption ------------------------------------------
 90     var decryptAES = function(dataHex, keyHex, ivHex) {
 91         return decryptGeneral(CryptoJS.AES, dataHex, keyHex, ivHex);
 92     };
 93 
 94     var decrypt3DES = function(dataHex, keyHex, ivHex) {
 95         return decryptGeneral(CryptoJS.TripleDES, dataHex, keyHex, ivHex);
 96     };
 97 
 98     var decryptGeneral = function(f, dataHex, keyHex, ivHex) {
 99     var data = CryptoJS.enc.Hex.parse(dataHex);
100     var key = CryptoJS.enc.Hex.parse(keyHex);
101     var iv = CryptoJS.enc.Hex.parse(ivHex);
102     var encrypted = {};
103     encrypted.key = key;
104     encrypted.iv = iv;
105     encrypted.ciphertext = data;
106     var decrypted = f.decrypt(encrypted, key, { iv: iv });
107     return CryptoJS.enc.Hex.stringify(decrypted);
108     };
109 
110     // shared key decryption ------------------------------------------
111     var encryptAES = function(dataHex, keyHex, ivHex) {
112         return encryptGeneral(CryptoJS.AES, dataHex, keyHex, ivHex);
113     };
114 
115     var encrypt3DES = function(dataHex, keyHex, ivHex) {
116         return encryptGeneral(CryptoJS.TripleDES, dataHex, keyHex, ivHex);
117     };
118 
119     var encryptGeneral = function(f, dataHex, keyHex, ivHex) {
120     var data = CryptoJS.enc.Hex.parse(dataHex);
121     var key = CryptoJS.enc.Hex.parse(keyHex);
122     var iv = CryptoJS.enc.Hex.parse(ivHex);
123     var msg = {};
124     var encryptedHex = f.encrypt(data, key, { iv: iv });
125         var encryptedWA = CryptoJS.enc.Hex.parse(encryptedHex.toString());
126         var encryptedB64 = CryptoJS.enc.Base64.stringify(encryptedWA);
127         return encryptedB64;
128     };
129 
130     // other methods and properties ----------------------------------------
131     var ALGLIST = {
132     'AES-256-CBC': { 'proc': decryptAES, 'eproc': encryptAES, keylen: 32, ivlen: 16 },
133     'AES-192-CBC': { 'proc': decryptAES, 'eproc': encryptAES, keylen: 24, ivlen: 16 },
134     'AES-128-CBC': { 'proc': decryptAES, 'eproc': encryptAES, keylen: 16, ivlen: 16 },
135     'DES-EDE3-CBC': { 'proc': decrypt3DES, 'eproc': encrypt3DES, keylen: 24, ivlen: 8 }
136     };
137 
138     var getFuncByName = function(algName) {
139         return ALGLIST[algName]['proc'];
140     };
141 
142     var _generateIvSaltHex = function(numBytes) {
143         var wa = CryptoJS.lib.WordArray.random(numBytes);
144         var hex = CryptoJS.enc.Hex.stringify(wa);
145         return hex;
146     };
147 
148     var _parsePKCS5PEM = function(sPKCS5PEM) {
149         var info = {};
150         if (sPKCS5PEM.match(new RegExp("DEK-Info: ([^,]+),([0-9A-Fa-f]+)", "m"))) {
151             info.cipher = RegExp.$1;
152             info.ivsalt = RegExp.$2;
153         }
154         if (sPKCS5PEM.match(new RegExp("-----BEGIN ([A-Z]+) PRIVATE KEY-----"))) {
155             info.type = RegExp.$1;
156         }
157         var i1 = -1;
158         var lenNEWLINE = 0;
159         if (sPKCS5PEM.indexOf("\r\n\r\n") != -1) {
160             i1 = sPKCS5PEM.indexOf("\r\n\r\n");
161             lenNEWLINE = 2;
162         }
163         if (sPKCS5PEM.indexOf("\n\n") != -1) {
164             i1 = sPKCS5PEM.indexOf("\n\n");
165             lenNEWLINE = 1;
166         }
167         var i2 = sPKCS5PEM.indexOf("-----END");
168         if (i1 != -1 && i2 != -1) {
169             var s = sPKCS5PEM.substring(i1 + lenNEWLINE * 2, i2 - lenNEWLINE);
170             s = s.replace(/\s+/g, '');
171             info.data = s;
172         }
173         return info;
174     };
175 
176     var _getKeyAndUnusedIvByPasscodeAndIvsalt = function(algName, passcode, ivsaltHex) {
177         //alert("ivsaltHex(2) = " + ivsaltHex);
178         var saltHex = ivsaltHex.substring(0, 16);
179         //alert("salt = " + saltHex);
180         
181         var salt = CryptoJS.enc.Hex.parse(saltHex);
182         var data = CryptoJS.enc.Utf8.parse(passcode);
183         //alert("salt = " + salt);
184         //alert("data = " + data);
185 
186         var nRequiredBytes = ALGLIST[algName]['keylen'] + ALGLIST[algName]['ivlen'];
187         var hHexValueJoined = '';
188         var hLastValue = null;
189         //alert("nRequiredBytes = " + nRequiredBytes);
190         for (;;) {
191             var h = CryptoJS.algo.MD5.create();
192             if (hLastValue != null) {
193                 h.update(hLastValue);
194             }
195             h.update(data);
196             h.update(salt);
197             hLastValue = h.finalize();
198             hHexValueJoined = hHexValueJoined + CryptoJS.enc.Hex.stringify(hLastValue);
199             //alert("joined = " + hHexValueJoined);
200             if (hHexValueJoined.length >= nRequiredBytes * 2) {
201                 break;
202             }
203         }
204         var result = {};
205         result.keyhex = hHexValueJoined.substr(0, ALGLIST[algName]['keylen'] * 2);
206         result.ivhex = hHexValueJoined.substr(ALGLIST[algName]['keylen'] * 2, ALGLIST[algName]['ivlen'] * 2);
207         return result;
208     };
209 
210     /*
211      * @param {String} privateKeyB64 base64 string of encrypted private key
212      * @param {String} sharedKeyAlgName algorithm name of shared key encryption
213      * @param {String} sharedKeyHex hexadecimal string of shared key to encrypt
214      * @param {String} ivsaltHex hexadecimal string of IV and salt
215      * @param {String} hexadecimal string of decrypted private key
216      */
217     var _decryptKeyB64 = function(privateKeyB64, sharedKeyAlgName, sharedKeyHex, ivsaltHex) {
218         var privateKeyWA = CryptoJS.enc.Base64.parse(privateKeyB64);
219         var privateKeyHex = CryptoJS.enc.Hex.stringify(privateKeyWA);
220         var f = ALGLIST[sharedKeyAlgName]['proc'];
221         var decryptedKeyHex = f(privateKeyHex, sharedKeyHex, ivsaltHex);
222         return decryptedKeyHex;
223     };
224     
225     /*
226      * @param {String} privateKeyHex hexadecimal string of private key
227      * @param {String} sharedKeyAlgName algorithm name of shared key encryption
228      * @param {String} sharedKeyHex hexadecimal string of shared key to encrypt
229      * @param {String} ivsaltHex hexadecimal string of IV and salt
230      * @param {String} base64 string of encrypted private key
231      */
232     var _encryptKeyHex = function(privateKeyHex, sharedKeyAlgName, sharedKeyHex, ivsaltHex) {
233         var f = ALGLIST[sharedKeyAlgName]['eproc'];
234         var encryptedKeyB64 = f(privateKeyHex, sharedKeyHex, ivsaltHex);
235         return encryptedKeyB64;
236     };
237 
238     // *****************************************************************
239     // *** PUBLIC PROPERTIES AND METHODS *******************************
240     // *****************************************************************
241     return {
242         // -- UTILITY METHODS ------------------------------------------
243         /**
244          * decrypt private key by shared key
245          * @name version
246          * @memberOf PKCS5PKEY
247          * @property {String} version
248          * @description version string of PKCS5PKEY class
249          */
250         version: "1.0.5",
251 
252         /**
253          * get hexacedimal string of PEM format
254          * @name getHexFromPEM
255          * @memberOf PKCS5PKEY
256          * @function
257          * @param {String} sPEM PEM formatted string
258          * @param {String} sHead PEM header string without BEGIN/END
259          * @return {String} hexadecimal string data of PEM contents
260          * @since pkcs5pkey 1.0.5
261          */
262         getHexFromPEM: function(sPEM, sHead) {
263             var s = sPEM;
264             if (s.indexOf("BEGIN " + sHead) == -1) {
265                 throw "can't find PEM header: " + sHead;
266             }
267             s = s.replace("-----BEGIN " + sHead + "-----", "");
268             s = s.replace("-----END " + sHead + "-----", "");
269             var sB64 = s.replace(/\s+/g, '');
270             var dataHex = b64tohex(sB64);
271             return dataHex;
272         },
273 
274         /**
275          * decrypt private key by shared key
276          * @name getDecryptedKeyHexByKeyIV
277          * @memberOf PKCS5PKEY
278          * @function
279          * @param {String} encryptedKeyHex hexadecimal string of encrypted private key
280          * @param {String} algName name of symmetric key algorithm (ex. 'DES-EBE3-CBC')
281          * @param {String} sharedKeyHex hexadecimal string of symmetric key
282          * @param {String} ivHex hexadecimal string of initial vector(IV).
283          * @return {String} hexadecimal string of decrypted privated key
284          */
285         getDecryptedKeyHexByKeyIV: function(encryptedKeyHex, algName, sharedKeyHex, ivHex) {
286             var f1 = getFuncByName(algName);
287             return f1(encryptedKeyHex, sharedKeyHex, ivHex);
288         },
289 
290         /**
291          * parse PEM formatted passcode protected PKCS#5 private key
292          * @name parsePKCS5PEM
293          * @memberOf PKCS5PKEY
294          * @function
295          * @param {String} sEncryptedPEM PEM formatted protected passcode protected PKCS#5 private key
296          * @return {Hash} hash of key information
297          * @description
298          * Resulted hash has following attributes.
299          * <ul>
300          * <li>cipher - symmetric key algorithm name (ex. 'DES-EBE3-CBC', 'AES-256-CBC')</li>
301          * <li>ivsalt - IV used for decrypt. Its heading 8 bytes will be used for passcode salt.</li>
302          * <li>type - asymmetric key algorithm name of private key described in PEM header.</li>
303          * <li>data - base64 encoded encrypted private key.</li>
304          * </ul>
305          *
306          */
307         parsePKCS5PEM: function(sPKCS5PEM) {
308             return _parsePKCS5PEM(sPKCS5PEM);
309         },
310 
311         /**
312          * the same function as OpenSSL EVP_BytsToKey to generate shared key and IV
313          * @name getKeyAndUnusedIvByPasscodeAndIvsalt
314          * @memberOf PKCS5PKEY
315          * @function
316          * @param {String} algName name of symmetric key algorithm (ex. 'DES-EBE3-CBC')
317          * @param {String} passcode passcode to decrypt private key (ex. 'password')
318          * @param {String} hexadecimal string of IV. heading 8 bytes will be used for passcode salt
319          * @return {Hash} hash of key and unused IV (ex. {keyhex:2fe3..., ivhex:3fad..})
320          */
321         getKeyAndUnusedIvByPasscodeAndIvsalt: function(algName, passcode, ivsaltHex) {
322             return _getKeyAndUnusedIvByPasscodeAndIvsalt(algName, passcode, ivsaltHex);
323         },
324 
325         decryptKeyB64: function(privateKeyB64, sharedKeyAlgName, sharedKeyHex, ivsaltHex) {
326             return _decryptKeyB64(privateKeyB64, sharedKeyAlgName, sharedKeyHex, ivsaltHex);
327         },
328 
329         /**
330          * decrypt PEM formatted protected PKCS#5 private key with passcode
331          * @name getDecryptedKeyHex
332          * @memberOf PKCS5PKEY
333          * @function
334          * @param {String} sEncryptedPEM PEM formatted protected passcode protected PKCS#5 private key
335          * @param {String} passcode passcode to decrypt private key (ex. 'password')
336          * @return {String} hexadecimal string of decrypted RSA priavte key
337          */
338         getDecryptedKeyHex: function(sEncryptedPEM, passcode) {
339             // 1. parse pem
340             var info = _parsePKCS5PEM(sEncryptedPEM);
341             var publicKeyAlgName = info.type;
342             var sharedKeyAlgName = info.cipher;
343             var ivsaltHex = info.ivsalt;
344             var privateKeyB64 = info.data;
345             //alert("ivsaltHex = " + ivsaltHex);
346 
347             // 2. generate shared key
348             var sharedKeyInfo = _getKeyAndUnusedIvByPasscodeAndIvsalt(sharedKeyAlgName, passcode, ivsaltHex);
349             var sharedKeyHex = sharedKeyInfo.keyhex;
350             //alert("sharedKeyHex = " + sharedKeyHex);
351 
352             // 3. decrypt private key
353             var decryptedKey = _decryptKeyB64(privateKeyB64, sharedKeyAlgName, sharedKeyHex, ivsaltHex);
354             return decryptedKey;
355         },
356 
357         /**
358          * read PEM formatted encrypted PKCS#5 private key and returns RSAKey object
359          * @name getRSAKeyFromEncryptedPKCS5PEM
360          * @memberOf PKCS5PKEY
361          * @function
362          * @param {String} sEncryptedP5PEM PEM formatted encrypted PKCS#5 private key
363          * @param {String} passcode passcode to decrypt private key
364          * @return {RSAKey} loaded RSAKey object of RSA private key
365          * @since pkcs5pkey 1.0.2
366          */
367         getRSAKeyFromEncryptedPKCS5PEM: function(sEncryptedP5PEM, passcode) {
368             var hPKey = this.getDecryptedKeyHex(sEncryptedP5PEM, passcode);
369             var rsaKey = new RSAKey();
370             rsaKey.readPrivateKeyFromASN1HexString(hPKey);
371             return rsaKey;
372         },
373 
374         /**
375          * get PEM formatted encrypted PKCS#5 private key from hexadecimal string of plain private key
376          * @name getEryptedPKCS5PEMFromPrvKeyHex
377          * @memberOf PKCS5PKEY
378          * @function
379          * @param {String} hPrvKey hexadecimal string of plain private key
380          * @param {String} passcode pass code to protect private key (ex. password)
381          * @param {String} sharedKeyAlgName algorithm name to protect private key (ex. AES-256-CBC)
382          * @param {String} ivsaltHex hexadecimal string of IV and salt
383          * @return {String} string of PEM formatted encrypted PKCS#5 private key
384          * @since pkcs5pkey 1.0.2
385          * @description
386          * <br/>
387          * generate PEM formatted encrypted PKCS#5 private key by hexadecimal string encoded
388          * ASN.1 object of plain RSA private key.
389          * Following arguments can be omitted.
390          * <ul>
391          * <li>alg - AES-256-CBC will be used if omitted.</li>
392          * <li>ivsaltHex - automatically generate IV and salt which length depends on algorithm</li>
393          * </ul>
394          * @example
395          * var pem = 
396          *   PKCS5PKEY.getEryptedPKCS5PEMFromPrvKeyHex(plainKeyHex, "password");
397          * var pem2 = 
398          *   PKCS5PKEY.getEryptedPKCS5PEMFromPrvKeyHex(plainKeyHex, "password", "AES-128-CBC");
399          * var pem3 = 
400          *   PKCS5PKEY.getEryptedPKCS5PEMFromPrvKeyHex(plainKeyHex, "password", "AES-128-CBC", "1f3d02...");
401          */
402         getEryptedPKCS5PEMFromPrvKeyHex: function(hPrvKey, passcode, sharedKeyAlgName, ivsaltHex) {
403             var sPEM = "";
404 
405             // 1. set sharedKeyAlgName if undefined (default AES-256-CBC)
406             if (typeof sharedKeyAlgName == "undefined" || sharedKeyAlgName == null) {
407                 sharedKeyAlgName = "AES-256-CBC";
408             }
409             if (typeof ALGLIST[sharedKeyAlgName] == "undefined")
410                 throw "PKCS5PKEY unsupported algorithm: " + sharedKeyAlgName;
411 
412             // 2. set ivsaltHex if undefined
413             if (typeof ivsaltHex == "undefined" || ivsaltHex == null) {
414                 var ivlen = ALGLIST[sharedKeyAlgName]['ivlen'];
415                 var randIV = _generateIvSaltHex(ivlen);
416                 ivsaltHex = randIV.toUpperCase();
417             }
418 
419             // 3. get shared key
420             //alert("ivsalthex=" + ivsaltHex);
421             var sharedKeyInfo = _getKeyAndUnusedIvByPasscodeAndIvsalt(sharedKeyAlgName, passcode, ivsaltHex);
422             var sharedKeyHex = sharedKeyInfo.keyhex;
423             // alert("sharedKeyHex = " + sharedKeyHex);
424 
425             // 3. get encrypted Key in Base64
426             var encryptedKeyB64 = _encryptKeyHex(hPrvKey, sharedKeyAlgName, sharedKeyHex, ivsaltHex);
427 
428             var pemBody = encryptedKeyB64.replace(/(.{64})/g, "$1\r\n");
429             var sPEM = "-----BEGIN RSA PRIVATE KEY-----\r\n";
430             sPEM += "Proc-Type: 4,ENCRYPTED\r\n";
431             sPEM += "DEK-Info: " + sharedKeyAlgName + "," + ivsaltHex + "\r\n";
432             sPEM += "\r\n";
433             sPEM += pemBody;
434             sPEM += "\r\n-----END RSA PRIVATE KEY-----\r\n";
435             
436             return sPEM;
437         },
438 
439         /**
440          * get PEM formatted encrypted PKCS#5 private key from RSAKey object of private key
441          * @name getEryptedPKCS5PEMFromRSAKey
442          * @memberOf PKCS5PKEY
443          * @function
444          * @param {RSAKey} pKey RSAKey object of private key
445          * @param {String} passcode pass code to protect private key (ex. password)
446          * @param {String} alg algorithm name to protect private key (default AES-256-CBC)
447          * @param {String} ivsaltHex hexadecimal string of IV and salt (default generated random IV)
448          * @return {String} string of PEM formatted encrypted PKCS#5 private key
449          * @since pkcs5pkey 1.0.2
450          * @description
451          * <br/>
452          * generate PEM formatted encrypted PKCS#5 private key by
453          * {@link RSAKey} object of RSA private key and passcode.
454          * Following argument can be omitted.
455          * <ul>
456          * <li>alg - AES-256-CBC will be used if omitted.</li>
457          * <li>ivsaltHex - automatically generate IV and salt which length depends on algorithm</li>
458          * </ul>
459          * @example
460          * var pkey = new RSAKey();
461          * pkey.generate(1024, '10001'); // generate 1024bit RSA private key with public exponent 'x010001'
462          * var pem = PKCS5PKEY.getEryptedPKCS5PEMFromRSAKey(pkey, "password");
463          */
464         getEryptedPKCS5PEMFromRSAKey: function(pKey, passcode, alg, ivsaltHex) {
465             var version = new KJUR.asn1.DERInteger({'int': 0});
466             var n = new KJUR.asn1.DERInteger({'bigint': pKey.n});
467             var e = new KJUR.asn1.DERInteger({'int': pKey.e});
468             var d = new KJUR.asn1.DERInteger({'bigint': pKey.d});
469             var p = new KJUR.asn1.DERInteger({'bigint': pKey.p});
470             var q = new KJUR.asn1.DERInteger({'bigint': pKey.q});
471             var dmp1 = new KJUR.asn1.DERInteger({'bigint': pKey.dmp1});
472             var dmq1 = new KJUR.asn1.DERInteger({'bigint': pKey.dmq1});
473             var coeff = new KJUR.asn1.DERInteger({'bigint': pKey.coeff});
474             var seq = new KJUR.asn1.DERSequence({'array': [version, n, e, d, p, q, dmp1, dmq1, coeff]});
475             var hex = seq.getEncodedHex();
476             return this.getEryptedPKCS5PEMFromPrvKeyHex(hex, passcode, alg, ivsaltHex);
477         },
478 
479         /**
480          * generate RSAKey and PEM formatted encrypted PKCS#5 private key
481          * @name newEncryptedPKCS5PEM
482          * @memberOf PKCS5PKEY
483          * @function
484          * @param {String} passcode pass code to protect private key (ex. password)
485          * @param {Integer} keyLen key bit length of RSA key to be generated. (default 1024)
486          * @param {String} hPublicExponent hexadecimal string of public exponent (default 10001)
487          * @param {String} alg shared key algorithm to encrypt private key (default AES-258-CBC)
488          * @return {String} string of PEM formatted encrypted PKCS#5 private key
489          * @since pkcs5pkey 1.0.2
490          * @example
491          * var pem1 = PKCS5PKEY.newEncryptedPKCS5PEM("password");           // RSA1024bit/10001/AES-256-CBC
492          * var pem2 = PKCS5PKEY.newEncryptedPKCS5PEM("password", 512);      // RSA 512bit/10001/AES-256-CBC
493          * var pem3 = PKCS5PKEY.newEncryptedPKCS5PEM("password", 512, '3'); // RSA 512bit/    3/AES-256-CBC
494          */
495         newEncryptedPKCS5PEM: function(passcode, keyLen, hPublicExponent, alg) {
496             if (typeof keyLen == "undefined" || keyLen == null) {
497                 keyLen = 1024;
498             }
499             if (typeof hPublicExponent == "undefined" || hPublicExponent == null) {
500                 hPublicExponent = '10001';
501             }
502             var pKey = new RSAKey();
503             pKey.generate(keyLen, hPublicExponent);
504             var pem = null;
505             if (typeof alg == "undefined" || alg == null) {
506                 pem = this.getEncryptedPKCS5PEMFromRSAKey(pkey, passcode);
507             } else {
508                 pem = this.getEncryptedPKCS5PEMFromRSAKey(pkey, passcode, alg);
509             }
510             return pem;
511         },
512 
513         // === PKCS8 ===============================================================
514 
515         /**
516          * read PEM formatted unencrypted PKCS#8 private key and returns RSAKey object
517          * @name getRSAKeyFromPlainPKCS8PEM
518          * @memberOf PKCS5PKEY
519          * @function
520          * @param {String} pkcs8PEM PEM formatted unencrypted PKCS#8 private key
521          * @return {RSAKey} loaded RSAKey object of RSA private key
522          * @since pkcs5pkey 1.0.1
523          */
524         getRSAKeyFromPlainPKCS8PEM: function(pkcs8PEM) {
525             if (pkcs8PEM.match(/ENCRYPTED/))
526                 throw "pem shall be not ENCRYPTED";
527             var prvKeyHex = this.getHexFromPEM(pkcs8PEM, "PRIVATE KEY");
528             var rsaKey = this.getRSAKeyFromPlainPKCS8Hex(prvKeyHex);
529             return rsaKey;
530         },
531 
532         /**
533          * provide hexadecimal string of unencrypted PKCS#8 private key and returns RSAKey object
534          * @name getRSAKeyFromPlainPKCS8Hex
535          * @memberOf PKCS5PKEY
536          * @function
537          * @param {String} prvKeyHex hexadecimal string of unencrypted PKCS#8 private key
538          * @return {RSAKey} loaded RSAKey object of RSA private key
539          * @since pkcs5pkey 1.0.3
540          */
541         getRSAKeyFromPlainPKCS8Hex: function(prvKeyHex) {
542             var a1 = ASN1HEX.getPosArrayOfChildren_AtObj(prvKeyHex, 0);
543             if (a1.length != 3)
544                 throw "outer DERSequence shall have 3 elements: " + a1.length;
545             var algIdTLV =ASN1HEX.getHexOfTLV_AtObj(prvKeyHex, a1[1]);
546             if (algIdTLV != "300d06092a864886f70d0101010500") // AlgId rsaEncryption
547                 throw "PKCS8 AlgorithmIdentifier is not rsaEnc: " + algIdTLV;
548             var algIdTLV = ASN1HEX.getHexOfTLV_AtObj(prvKeyHex, a1[1]);
549             var octetStr = ASN1HEX.getHexOfTLV_AtObj(prvKeyHex, a1[2]);
550             var p5KeyHex = ASN1HEX.getHexOfV_AtObj(octetStr, 0);
551             //alert(p5KeyHex);
552             var rsaKey = new RSAKey();
553             rsaKey.readPrivateKeyFromASN1HexString(p5KeyHex);
554             return rsaKey;
555         },
556 
557         /**
558          * generate PBKDF2 key hexstring with specified passcode and information
559          * @name parseHexOfEncryptedPKCS8
560          * @memberOf PKCS5PKEY
561          * @function
562          * @param {String} passcode passcode to decrypto private key
563          * @return {Array} info associative array of PKCS#8 parameters
564          * @since pkcs5pkey 1.0.3
565          * @description
566          * The associative array which is returned by this method has following properties:
567          * <ul>
568          * <li>info.pbkdf2Salt - hexadecimal string of PBKDF2 salt</li>
569          * <li>info.pkbdf2Iter - iteration count</li>
570          * <li>info.ciphertext - hexadecimal string of encrypted private key</li>
571          * <li>info.encryptionSchemeAlg - encryption algorithm name (currently TripleDES only)</li>
572          * <li>info.encryptionSchemeIV - initial vector for encryption algorithm</li>
573          * </ul>
574          * Currently, this method only supports PKCS#5v2.0 with PBES2/PBDKF2 of HmacSHA1 and TripleDES.
575          * <ul>
576          * <li>keyDerivationFunc = pkcs5PBKDF2 with HmacSHA1</li>
577          * <li>encryptionScheme = des-EDE3-CBC(i.e. TripleDES</li>
578          * </ul>
579          * @example
580          * // to convert plain PKCS#5 private key to encrypted PKCS#8 private
581          * // key with PBKDF2 with TripleDES
582          * % openssl pkcs8 -in plain_p5.pem -topk8 -v2 -des3 -out encrypted_p8.pem
583          */
584         parseHexOfEncryptedPKCS8: function(sHEX) {
585             var info = {};
586         
587             var a0 = ASN1HEX.getPosArrayOfChildren_AtObj(sHEX, 0);
588             if (a0.length != 2)
589                 throw "malformed format: SEQUENCE(0).items != 2: " + a0.length;
590 
591             // 1. ciphertext
592             info.ciphertext = ASN1HEX.getHexOfV_AtObj(sHEX, a0[1]);
593 
594             // 2. pkcs5PBES2
595             var a0_0 = ASN1HEX.getPosArrayOfChildren_AtObj(sHEX, a0[0]); 
596             if (a0_0.length != 2)
597                 throw "malformed format: SEQUENCE(0.0).items != 2: " + a0_0.length;
598 
599             // 2.1 check if pkcs5PBES2(1 2 840 113549 1 5 13)
600             if (ASN1HEX.getHexOfV_AtObj(sHEX, a0_0[0]) != "2a864886f70d01050d")
601                 throw "this only supports pkcs5PBES2";
602 
603             // 2.2 pkcs5PBES2 param
604             var a0_0_1 = ASN1HEX.getPosArrayOfChildren_AtObj(sHEX, a0_0[1]); 
605             if (a0_0.length != 2)
606                 throw "malformed format: SEQUENCE(0.0.1).items != 2: " + a0_0_1.length;
607 
608             // 2.2.1 encryptionScheme
609             var a0_0_1_1 = ASN1HEX.getPosArrayOfChildren_AtObj(sHEX, a0_0_1[1]); 
610             if (a0_0_1_1.length != 2)
611                 throw "malformed format: SEQUENCE(0.0.1.1).items != 2: " + a0_0_1_1.length;
612             if (ASN1HEX.getHexOfV_AtObj(sHEX, a0_0_1_1[0]) != "2a864886f70d0307")
613                 throw "this only supports TripleDES";
614             info.encryptionSchemeAlg = "TripleDES";
615 
616             // 2.2.1.1 IV of encryptionScheme
617             info.encryptionSchemeIV = ASN1HEX.getHexOfV_AtObj(sHEX, a0_0_1_1[1]);
618 
619             // 2.2.2 keyDerivationFunc
620             var a0_0_1_0 = ASN1HEX.getPosArrayOfChildren_AtObj(sHEX, a0_0_1[0]); 
621             if (a0_0_1_0.length != 2)
622                 throw "malformed format: SEQUENCE(0.0.1.0).items != 2: " + a0_0_1_0.length;
623             if (ASN1HEX.getHexOfV_AtObj(sHEX, a0_0_1_0[0]) != "2a864886f70d01050c")
624                 throw "this only supports pkcs5PBKDF2";
625             
626             // 2.2.2.1 pkcs5PBKDF2 param
627             var a0_0_1_0_1 = ASN1HEX.getPosArrayOfChildren_AtObj(sHEX, a0_0_1_0[1]); 
628             if (a0_0_1_0_1.length < 2)
629                 throw "malformed format: SEQUENCE(0.0.1.0.1).items < 2: " + a0_0_1_0_1.length;
630 
631             // 2.2.2.1.1 PBKDF2 salt
632             info.pbkdf2Salt = ASN1HEX.getHexOfV_AtObj(sHEX, a0_0_1_0_1[0]);
633 
634             // 2.2.2.1.2 PBKDF2 iter
635             var iterNumHex = ASN1HEX.getHexOfV_AtObj(sHEX, a0_0_1_0_1[1]);
636             try {
637                 info.pbkdf2Iter = parseInt(iterNumHex, 16);
638             } catch(ex) {
639                 throw "malformed format pbkdf2Iter: " + iterNumHex;
640             }
641 
642             return info;
643         },
644 
645         /**
646          * generate PBKDF2 key hexstring with specified passcode and information
647          * @name getPBKDF2KeyHexFromParam
648          * @memberOf PKCS5PKEY
649          * @function
650          * @param {Array} info result of {@link parseHexOfEncryptedPKCS8} which has preference of PKCS#8 file
651          * @param {String} passcode passcode to decrypto private key
652          * @return {String} hexadecimal string of PBKDF2 key
653          * @since pkcs5pkey 1.0.3
654          * @description
655          * As for info, this uses following properties:
656          * <ul>
657          * <li>info.pbkdf2Salt - hexadecimal string of PBKDF2 salt</li>
658          * <li>info.pkbdf2Iter - iteration count</li>
659          * </ul>
660          * Currently, this method only supports PKCS#5v2.0 with PBES2/PBDKF2 of HmacSHA1 and TripleDES.
661          * <ul>
662          * <li>keyDerivationFunc = pkcs5PBKDF2 with HmacSHA1</li>
663          * <li>encryptionScheme = des-EDE3-CBC(i.e. TripleDES</li>
664          * </ul>
665          * @example
666          * // to convert plain PKCS#5 private key to encrypted PKCS#8 private
667          * // key with PBKDF2 with TripleDES
668          * % openssl pkcs8 -in plain_p5.pem -topk8 -v2 -des3 -out encrypted_p8.pem
669          */
670         getPBKDF2KeyHexFromParam: function(info, passcode) {
671             var pbkdf2SaltWS = CryptoJS.enc.Hex.parse(info.pbkdf2Salt);
672             var pbkdf2Iter = info.pbkdf2Iter;
673             var pbkdf2KeyWS = CryptoJS.PBKDF2(passcode, 
674                                               pbkdf2SaltWS, 
675                                               { keySize: 192/32, iterations: pbkdf2Iter });
676             var pbkdf2KeyHex = CryptoJS.enc.Hex.stringify(pbkdf2KeyWS);
677             return pbkdf2KeyHex;
678         },
679 
680         /**
681          * read PEM formatted encrypted PKCS#8 private key and returns hexadecimal string of plain PKCS#8 private key
682          * @name getPlainPKCS8HexFromEncryptedPKCS8PEM
683          * @memberOf PKCS5PKEY
684          * @function
685          * @param {String} pkcs8PEM PEM formatted encrypted PKCS#8 private key
686          * @param {String} passcode passcode to decrypto private key
687          * @return {String} hexadecimal string of plain PKCS#8 private key
688          * @since pkcs5pkey 1.0.3
689          * @description
690          * Currently, this method only supports PKCS#5v2.0 with PBES2/PBDKF2 of HmacSHA1 and TripleDES.
691          * <ul>
692          * <li>keyDerivationFunc = pkcs5PBKDF2 with HmacSHA1</li>
693          * <li>encryptionScheme = des-EDE3-CBC(i.e. TripleDES</li>
694          * </ul>
695          * @example
696          * // to convert plain PKCS#5 private key to encrypted PKCS#8 private
697          * // key with PBKDF2 with TripleDES
698          * % openssl pkcs8 -in plain_p5.pem -topk8 -v2 -des3 -out encrypted_p8.pem
699          */
700         getPlainPKCS8HexFromEncryptedPKCS8PEM: function(pkcs8PEM, passcode) {
701             // 1. derHex - PKCS#8 private key encrypted by PBKDF2
702             var derHex = this.getHexFromPEM(pkcs8PEM, "ENCRYPTED PRIVATE KEY");
703             // 2. info - PKCS#5 PBES info
704             var info = this.parseHexOfEncryptedPKCS8(derHex);
705             // 3. hKey - PBKDF2 key
706             var pbkdf2KeyHex = PKCS5PKEY.getPBKDF2KeyHexFromParam(info, passcode);
707             // 4. decrypt ciphertext by PBKDF2 key
708             var encrypted = {};
709             encrypted.ciphertext = CryptoJS.enc.Hex.parse(info.ciphertext);
710             var pbkdf2KeyWS = CryptoJS.enc.Hex.parse(pbkdf2KeyHex);
711             var des3IVWS = CryptoJS.enc.Hex.parse(info.encryptionSchemeIV);
712             var decWS = CryptoJS.TripleDES.decrypt(encrypted, pbkdf2KeyWS, { iv: des3IVWS });
713             var decHex = CryptoJS.enc.Hex.stringify(decWS);
714             return decHex;
715         },
716 
717         /**
718          * read PEM formatted encrypted PKCS#8 private key and returns RSAKey object
719          * @name getRSAKeyFromEncryptedPKCS8PEM
720          * @memberOf PKCS5PKEY
721          * @function
722          * @param {String} pkcs8PEM PEM formatted encrypted PKCS#8 private key
723          * @param {String} passcode passcode to decrypto private key
724          * @return {RSAKey} loaded RSAKey object of RSA private key
725          * @since pkcs5pkey 1.0.3
726          * @description
727          * Currently, this method only supports PKCS#5v2.0 with PBES2/PBDKF2 of HmacSHA1 and TripleDES.
728          * <ul>
729          * <li>keyDerivationFunc = pkcs5PBKDF2 with HmacSHA1</li>
730          * <li>encryptionScheme = des-EDE3-CBC(i.e. TripleDES</li>
731          * </ul>
732          * @example
733          * // to convert plain PKCS#5 private key to encrypted PKCS#8 private
734          * // key with PBKDF2 with TripleDES
735          * % openssl pkcs8 -in plain_p5.pem -topk8 -v2 -des3 -out encrypted_p8.pem
736          */
737         getRSAKeyFromEncryptedPKCS8PEM: function(pkcs8PEM, passcode) {
738             var prvKeyHex = this.getPlainPKCS8HexFromEncryptedPKCS8PEM(pkcs8PEM, passcode);
739             var rsaKey = this.getRSAKeyFromPlainPKCS8Hex(prvKeyHex);
740             return rsaKey;
741         },
742 
743         /**
744          * get RSAKey/ECDSA private key object from encrypted PEM PKCS#8 private key
745          * @name getKeyFromEncryptedPKCS8PEM
746          * @memberOf PKCS5PKEY
747          * @function
748          * @param {String} pkcs8PEM string of PEM formatted PKCS#8 private key
749          * @param {String} passcode passcode string to decrypt key
750          * @return {Object} RSAKey or KJUR.crypto.ECDSA private key object
751          * @since pkcs5pkey 1.0.5
752          */
753         getKeyFromEncryptedPKCS8PEM: function(pkcs8PEM, passcode) {
754             var prvKeyHex = this.getPlainPKCS8HexFromEncryptedPKCS8PEM(pkcs8PEM, passcode);
755             var key = this.getKeyFromPlainPrivatePKCS8Hex(prvKeyHex);
756             return key;
757         },
758 
759         /**
760          * parse hexadecimal string of plain PKCS#8 private key
761          * @name parsePlainPrivatePKCS8Hex
762          * @memberOf PKCS5PKEY
763          * @function
764          * @param {String} pkcs8PrvHex hexadecimal string of PKCS#8 plain private key
765          * @return {Array} associative array of parsed key
766          * @since pkcs5pkey 1.0.5
767          * @description
768          * Resulted associative array has following properties:
769          * <ul>
770          * <li>algoid - hexadecimal string of OID of asymmetric key algorithm</li>
771          * <li>algparam - hexadecimal string of OID of ECC curve name or null</li>
772          * <li>keyidx - string starting index of key in pkcs8PrvHex</li>
773          * </ul>
774          */
775         parsePlainPrivatePKCS8Hex: function(pkcs8PrvHex) {
776             var result = {};
777             result.algparam = null;
778 
779             // 1. sequence
780             if (pkcs8PrvHex.substr(0, 2) != "30")
781                 throw "malformed plain PKCS8 private key(code:001)"; // not sequence
782 
783             var a1 = ASN1HEX.getPosArrayOfChildren_AtObj(pkcs8PrvHex, 0);
784             if (a1.length != 3)
785                 throw "malformed plain PKCS8 private key(code:002)";
786 
787             // 2. AlgID
788             if (pkcs8PrvHex.substr(a1[1], 2) != "30")
789                 throw "malformed PKCS8 private key(code:003)"; // AlgId not sequence
790 
791             var a2 = ASN1HEX.getPosArrayOfChildren_AtObj(pkcs8PrvHex, a1[1]);
792             if (a2.length != 2)
793                 throw "malformed PKCS8 private key(code:004)"; // AlgId not have two elements
794 
795             // 2.1. AlgID OID
796             if (pkcs8PrvHex.substr(a2[0], 2) != "06")
797                 throw "malformed PKCS8 private key(code:005)"; // AlgId.oid is not OID
798 
799             result.algoid = ASN1HEX.getHexOfV_AtObj(pkcs8PrvHex, a2[0]);
800 
801             // 2.2. AlgID param
802             if (pkcs8PrvHex.substr(a2[1], 2) == "06") {
803                 result.algparam = ASN1HEX.getHexOfV_AtObj(pkcs8PrvHex, a2[1]);
804             }
805 
806             // 3. Key index
807             if (pkcs8PrvHex.substr(a1[2], 2) != "04")
808                 throw "malformed PKCS8 private key(code:006)"; // not octet string
809 
810             result.keyidx = ASN1HEX.getStartPosOfV_AtObj(pkcs8PrvHex, a1[2]);
811 
812             return result;
813         },
814 
815         /**
816          * get RSAKey/ECDSA private key object from PEM plain PEM PKCS#8 private key
817          * @name getKeyFromPlainPrivatePKCS8PEM
818          * @memberOf PKCS5PKEY
819          * @function
820          * @param {String} pkcs8PEM string of plain PEM formatted PKCS#8 private key
821          * @return {Object} RSAKey or KJUR.crypto.ECDSA private key object
822          * @since pkcs5pkey 1.0.5
823          */
824         getKeyFromPlainPrivatePKCS8PEM: function(prvKeyPEM) {
825             var prvKeyHex = this.getHexFromPEM(prvKeyPEM, "PRIVATE KEY");
826             var key = this.getKeyFromPlainPrivatePKCS8Hex(prvKeyHex);
827             return key;
828         },
829 
830         /**
831          * get RSAKey/ECDSA private key object from HEX plain PEM PKCS#8 private key
832          * @name getKeyFromPlainPrivatePKCS8Hex
833          * @memberOf PKCS5PKEY
834          * @function
835          * @param {String} prvKeyHex hexadecimal string of plain PKCS#8 private key
836          * @return {Object} RSAKey or KJUR.crypto.ECDSA private key object
837          * @since pkcs5pkey 1.0.5
838          */
839         getKeyFromPlainPrivatePKCS8Hex: function(prvKeyHex) {
840             var p8 = this.parsePlainPrivatePKCS8Hex(prvKeyHex);
841             
842             if (p8.algoid == "2a864886f70d010101") { // RSA
843                 this.parsePrivateRawRSAKeyHexAtObj(prvKeyHex, p8);
844                 var k = p8.key;
845                 var key = new RSAKey();
846                 key.setPrivateEx(k.n, k.e, k.d, k.p, k.q, k.dp, k.dq, k.co);
847                 return key;
848             } else if (p8.algoid == "2a8648ce3d0201") { // ECC
849                 this.parsePrivateRawECKeyHexAtObj(prvKeyHex, p8);
850                 if (KJUR.crypto.OID.oidhex2name[p8.algparam] === undefined)
851                     throw "KJUR.crypto.OID.oidhex2name undefined: " + p8.algparam;
852                 var curveName = KJUR.crypto.OID.oidhex2name[p8.algparam];
853                 var key = new KJUR.crypto.ECDSA({'curve': curveName, 'prv': p8.key});
854                 return key;
855             } else {
856                 throw "unsupported private key algorithm";
857             }
858         },
859 
860         // === PKCS8 RSA Public Key ================================================
861         /**
862          * read PEM formatted PKCS#8 public key and returns RSAKey object
863          * @name getRSAKeyFromPublicPKCS8PEM
864          * @memberOf PKCS5PKEY
865          * @function
866          * @param {String} pkcs8PubPEM PEM formatted PKCS#8 public key
867          * @return {RSAKey} loaded RSAKey object of RSA public key
868          * @since pkcs5pkey 1.0.4
869          */
870         getRSAKeyFromPublicPKCS8PEM: function(pkcs8PubPEM) {
871             var pubKeyHex = this.getHexFromPEM(pkcs8PubPEM, "PUBLIC KEY");
872             var rsaKey = this.getRSAKeyFromPublicPKCS8Hex(pubKeyHex);
873             return rsaKey;
874         },
875 
876         /**
877          * get RSAKey/ECDSA public key object from PEM PKCS#8 public key
878          * @name getKeyFromPublicPKCS8PEM
879          * @memberOf PKCS5PKEY
880          * @function
881          * @param {String} pkcsPub8PEM string of PEM formatted PKCS#8 public key
882          * @return {Object} RSAKey or KJUR.crypto.ECDSA private key object
883          * @since pkcs5pkey 1.0.5
884          */
885         getKeyFromPublicPKCS8PEM: function(pkcs8PubPEM) {
886             var pubKeyHex = this.getHexFromPEM(pkcs8PubPEM, "PUBLIC KEY");
887             var key = this.getKeyFromPublicPKCS8Hex(pubKeyHex);
888             return key;
889         },
890 
891         /**
892          * get RSAKey/ECDSA public key object from hexadecimal string of PKCS#8 public key
893          * @name getKeyFromPublicPKCS8Hex
894          * @memberOf PKCS5PKEY
895          * @function
896          * @param {String} pkcsPub8Hex hexadecimal string of PKCS#8 public key
897          * @return {Object} RSAKey or KJUR.crypto.ECDSA private key object
898          * @since pkcs5pkey 1.0.5
899          */
900         getKeyFromPublicPKCS8Hex: function(pkcs8PubHex) {
901             var p8 = this.parsePublicPKCS8Hex(pkcs8PubHex);
902             
903             if (p8.algoid == "2a864886f70d010101") { // RSA
904                 var aRSA = this.parsePublicRawRSAKeyHex(p8.key);
905                 var key = new RSAKey();
906                 key.setPublic(aRSA.n, aRSA.e);
907                 return key;
908             } else if (p8.algoid == "2a8648ce3d0201") { // ECC
909                 if (KJUR.crypto.OID.oidhex2name[p8.algparam] === undefined)
910                     throw "KJUR.crypto.OID.oidhex2name undefined: " + p8.algparam;
911                 var curveName = KJUR.crypto.OID.oidhex2name[p8.algparam];
912                 var key = new KJUR.crypto.ECDSA({'curve': curveName, 'pub': p8.key});
913                 return key;
914             } else {
915                 throw "unsupported public key algorithm";
916             }
917         },
918 
919         /**
920          * parse hexadecimal string of plain PKCS#8 private key
921          * @name parsePublicRawRSAKeyHex
922          * @memberOf PKCS5PKEY
923          * @function
924          * @param {String} pubRawRSAHex hexadecimal string of ASN.1 encoded PKCS#8 public key
925          * @return {Array} associative array of parsed key
926          * @since pkcs5pkey 1.0.5
927          * @description
928          * Resulted associative array has following properties:
929          * <ul>
930          * <li>n - hexadecimal string of public key
931          * <li>e - hexadecimal string of public exponent
932          * </ul>
933          */
934         parsePublicRawRSAKeyHex: function(pubRawRSAHex) {
935             var result = {};
936             
937             // 1. Sequence
938             if (pubRawRSAHex.substr(0, 2) != "30")
939                 throw "malformed RSA key(code:001)"; // not sequence
940             
941             var a1 = ASN1HEX.getPosArrayOfChildren_AtObj(pubRawRSAHex, 0);
942             if (a1.length != 2)
943                 throw "malformed RSA key(code:002)"; // not 2 items in seq
944 
945             // 2. public key "N"
946             if (pubRawRSAHex.substr(a1[0], 2) != "02")
947                 throw "malformed RSA key(code:003)"; // 1st item is not integer
948 
949             result.n = ASN1HEX.getHexOfV_AtObj(pubRawRSAHex, a1[0]);
950 
951             // 3. public key "E"
952             if (pubRawRSAHex.substr(a1[1], 2) != "02")
953                 throw "malformed RSA key(code:004)"; // 2nd item is not integer
954 
955             result.e = ASN1HEX.getHexOfV_AtObj(pubRawRSAHex, a1[1]);
956 
957             return result;
958         },
959 
960         /**
961          * parse hexadecimal string of RSA private key
962          * @name parsePrivateRawRSAKeyHexAtObj
963          * @memberOf PKCS5PKEY
964          * @function
965          * @param {String} pkcs8PrvHex hexadecimal string of PKCS#8 private key concluding RSA private key
966          * @return {Array} info associative array to add parsed RSA private key information
967          * @since pkcs5pkey 1.0.5
968          * @description
969          * Following properties are added to associative array 'info'
970          * <ul>
971          * <li>n - hexadecimal string of public key
972          * <li>e - hexadecimal string of public exponent
973          * <li>d - hexadecimal string of private key
974          * <li>p - hexadecimal string
975          * <li>q - hexadecimal string
976          * <li>dp - hexadecimal string
977          * <li>dq - hexadecimal string
978          * <li>co - hexadecimal string
979          * </ul>
980          */
981         parsePrivateRawRSAKeyHexAtObj: function(pkcs8PrvHex, info) {
982             var keyIdx = info.keyidx;
983             
984             // 1. sequence
985             if (pkcs8PrvHex.substr(keyIdx, 2) != "30")
986                 throw "malformed RSA private key(code:001)"; // not sequence
987 
988             var a1 = ASN1HEX.getPosArrayOfChildren_AtObj(pkcs8PrvHex, keyIdx);
989             if (a1.length != 9)
990                 throw "malformed RSA private key(code:002)"; // not sequence
991 
992             // 2. RSA key
993             info.key = {};
994             info.key.n = ASN1HEX.getHexOfV_AtObj(pkcs8PrvHex, a1[1]);
995             info.key.e = ASN1HEX.getHexOfV_AtObj(pkcs8PrvHex, a1[2]);
996             info.key.d = ASN1HEX.getHexOfV_AtObj(pkcs8PrvHex, a1[3]);
997             info.key.p = ASN1HEX.getHexOfV_AtObj(pkcs8PrvHex, a1[4]);
998             info.key.q = ASN1HEX.getHexOfV_AtObj(pkcs8PrvHex, a1[5]);
999             info.key.dp = ASN1HEX.getHexOfV_AtObj(pkcs8PrvHex, a1[6]);
1000             info.key.dq = ASN1HEX.getHexOfV_AtObj(pkcs8PrvHex, a1[7]);
1001             info.key.co = ASN1HEX.getHexOfV_AtObj(pkcs8PrvHex, a1[8]);
1002         },
1003 
1004         /**
1005          * parse hexadecimal string of ECC private key
1006          * @name parsePrivateRawECKeyHexAtObj
1007          * @memberOf PKCS5PKEY
1008          * @function
1009          * @param {String} pkcs8PrvHex hexadecimal string of PKCS#8 private key concluding EC private key
1010          * @return {Array} info associative array to add parsed ECC private key information
1011          * @since pkcs5pkey 1.0.5
1012          * @description
1013          * Following properties are added to associative array 'info'
1014          * <ul>
1015          * <li>key - hexadecimal string of ECC private key
1016          * </ul>
1017          */
1018         parsePrivateRawECKeyHexAtObj: function(pkcs8PrvHex, info) {
1019             var keyIdx = info.keyidx;
1020             
1021             // 1. sequence
1022             if (pkcs8PrvHex.substr(keyIdx, 2) != "30")
1023                 throw "malformed ECC private key(code:001)"; // not sequence
1024 
1025             var a1 = ASN1HEX.getPosArrayOfChildren_AtObj(pkcs8PrvHex, keyIdx);
1026             if (a1.length != 3)
1027                 throw "malformed ECC private key(code:002)"; // not sequence
1028 
1029             // 2. EC private key
1030             if (pkcs8PrvHex.substr(a1[1], 2) != "04")
1031                 throw "malformed ECC private key(code:003)"; // not octetstring
1032 
1033             info.key = ASN1HEX.getHexOfV_AtObj(pkcs8PrvHex, a1[1]);
1034         },
1035 
1036         /**
1037          * parse hexadecimal string of PKCS#8 public key
1038          * @name parsePublicPKCS8Hex
1039          * @memberOf PKCS5PKEY
1040          * @function
1041          * @param {String} pkcs8PubHex hexadecimal string of PKCS#8 public key
1042          * @return {Hash} hash of key information
1043          * @description
1044          * Resulted hash has following attributes.
1045          * <ul>
1046          * <li>algoid - hexadecimal string of OID of asymmetric key algorithm</li>
1047          * <li>algparam - hexadecimal string of OID of ECC curve name or null</li>
1048          * <li>key - hexadecimal string of public key</li>
1049          * </ul>
1050          */
1051         parsePublicPKCS8Hex: function(pkcs8PubHex) {
1052             var result = {};
1053             result.algparam = null;
1054 
1055             // 1. AlgID and Key bit string
1056             var a1 = ASN1HEX.getPosArrayOfChildren_AtObj(pkcs8PubHex, 0);
1057             if (a1.length != 2)
1058                 throw "outer DERSequence shall have 2 elements: " + a1.length;
1059 
1060             // 2. AlgID
1061             var idxAlgIdTLV = a1[0];
1062             if (pkcs8PubHex.substr(idxAlgIdTLV, 2) != "30")
1063                 throw "malformed PKCS8 public key(code:001)"; // AlgId not sequence
1064 
1065             var a2 = ASN1HEX.getPosArrayOfChildren_AtObj(pkcs8PubHex, idxAlgIdTLV);
1066             if (a2.length != 2)
1067                 throw "malformed PKCS8 public key(code:002)"; // AlgId not have two elements
1068 
1069             // 2.1. AlgID OID
1070             if (pkcs8PubHex.substr(a2[0], 2) != "06")
1071                 throw "malformed PKCS8 public key(code:003)"; // AlgId.oid is not OID
1072 
1073             result.algoid = ASN1HEX.getHexOfV_AtObj(pkcs8PubHex, a2[0]);
1074 
1075             // 2.2. AlgID param
1076             if (pkcs8PubHex.substr(a2[1], 2) == "06") {
1077                 result.algparam = ASN1HEX.getHexOfV_AtObj(pkcs8PubHex, a2[1]);
1078             }
1079 
1080             // 3. Key
1081             if (pkcs8PubHex.substr(a1[1], 2) != "03")
1082                 throw "malformed PKCS8 public key(code:004)"; // Key is not bit string
1083 
1084             result.key = ASN1HEX.getHexOfV_AtObj(pkcs8PubHex, a1[1]).substr(2);
1085             
1086             // 4. return result assoc array
1087             return result;
1088         },
1089 
1090         /**
1091          * provide hexadecimal string of unencrypted PKCS#8 private key and returns RSAKey object
1092          * @name getRSAKeyFromPublicPKCS8Hex
1093          * @memberOf PKCS5PKEY
1094          * @function
1095          * @param {String} pkcs8PubHex hexadecimal string of unencrypted PKCS#8 public key
1096          * @return {RSAKey} loaded RSAKey object of RSA public key
1097          * @since pkcs5pkey 1.0.4
1098          */
1099         getRSAKeyFromPublicPKCS8Hex: function(pkcs8PubHex) {
1100             var a1 = ASN1HEX.getPosArrayOfChildren_AtObj(pkcs8PubHex, 0);
1101             if (a1.length != 2)
1102                 throw "outer DERSequence shall have 2 elements: " + a1.length;
1103 
1104             var algIdTLV =ASN1HEX.getHexOfTLV_AtObj(pkcs8PubHex, a1[0]);
1105             if (algIdTLV != "300d06092a864886f70d0101010500") // AlgId rsaEncryption
1106                 throw "PKCS8 AlgorithmId is not rsaEncryption";
1107             
1108             if (pkcs8PubHex.substr(a1[1], 2) != "03")
1109                 throw "PKCS8 Public Key is not BITSTRING encapslated.";
1110 
1111             var idxPub = ASN1HEX.getStartPosOfV_AtObj(pkcs8PubHex, a1[1]) + 2; // 2 for unused bit
1112             
1113             if (pkcs8PubHex.substr(idxPub, 2) != "30")
1114                 throw "PKCS8 Public Key is not SEQUENCE.";
1115 
1116             var a2 = ASN1HEX.getPosArrayOfChildren_AtObj(pkcs8PubHex, idxPub);
1117             if (a2.length != 2)
1118                 throw "inner DERSequence shall have 2 elements: " + a2.length;
1119 
1120             if (pkcs8PubHex.substr(a2[0], 2) != "02") 
1121                 throw "N is not ASN.1 INTEGER";
1122             if (pkcs8PubHex.substr(a2[1], 2) != "02") 
1123                 throw "E is not ASN.1 INTEGER";
1124             
1125             var hN = ASN1HEX.getHexOfV_AtObj(pkcs8PubHex, a2[0]);
1126             var hE = ASN1HEX.getHexOfV_AtObj(pkcs8PubHex, a2[1]);
1127 
1128             var pubKey = new RSAKey();
1129             pubKey.setPublic(hN, hE);
1130             
1131             return pubKey;
1132         },
1133     };
1134 }();
1135