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