ÿØÿà JFIF    ÿÛ „  ( %"1!%)+...383,7(-.+  -+++--++++---+-+-----+---------------+---+-++7-----ÿÀ  ß â" ÿÄ     ÿÄ H    !1AQaq"‘¡2B±ÁÑð#R“Ò Tbr‚²á3csƒ’ÂñDS¢³$CÿÄ   ÿÄ %  !1AQa"23‘ÿÚ   ? ôÿ ¨pŸªáÿ —åYõõ\?àÒü©ŠÄï¨pŸªáÿ —åYõõ\?àÓü©ŠÄá 0Ÿªáÿ Ÿå[úƒ ú®ði~TÁbqÐ8OÕpÿ ƒOò¤Oè`–RÂáœá™êi€ßÉ< FtŸI“öÌ8úDf´°å}“¾œ6  öFá°y¥jñÇh†ˆ¢ã/ÃÐ:ªcÈ "Y¡ðÑl>ÿ ”ÏËte:qž\oäŠe÷󲍷˜HT4&ÿ ÓÐü6ö®¿øþßèô Ÿ•7Ñi’•j|“ñì>b…þS?*Óôÿ ÓÐü*h¥£ír¶ü UãS炟[AÐaè[ûª•õ&õj?†Éö+EzP—WeÒírJFt ‘BŒ†Ï‡%#tE Øz ¥OÛ«!1›üä±Í™%ºÍãö]°î(–:@<‹ŒÊö×òÆt¦ãº+‡¦%ÌÁ²h´OƒJŒtMÜ>ÀÜÊw3Y´•牋4ǍýʏTì>œú=Íwhyë,¾Ôò×õ¿ßÊa»«þˆѪQ|%6ž™A õ%:øj<>É—ÿ Å_ˆCbõ¥š±ý¯Ýƒï…¶|RëócÍf溪“t.СøTÿ *Ä¿-{†çàczůŽ_–^XþŒ±miB[X±d 1,é”zEù»& î9gœf™9Ð'.;—™i}!ôšåîqêÛ٤ёý£½ÆA–àôe"A$˝Úsäÿ ÷Û #°xŸëí(l »ý3—¥5m! rt`†0~'j2(]S¦¦kv,ÚÇ l¦øJA£Šƒ J3E8ÙiŽ:cÉžúeZ°€¯\®kÖ(79«Ž:¯X”¾³Š&¡* ….‰Ž(ÜíŸ2¥ª‡×Hi²TF¤ò[¨íÈRëÉ䢍mgÑ.Ÿ<öäS0í„ǹÁU´f#Vß;Õ–…P@3ío<ä-±»Ž.L|kªÀê›fÂ6@»eu‚|ÓaÞÆŸ…¨ááå>åŠ?cKü6ùTÍÆ”†sĤÚ;H2RÚ†õ\Ö·Ÿn'¾ ñ#ºI¤Å´%çÁ­‚â7›‹qT3Iï¨ÖÚ5I7Ë!ÅOóŸ¶øÝñØôת¦$Tcö‘[«Ö³šÒ';Aþ ¸èíg A2Z"i¸vdÄ÷.iõ®§)¿]¤À†–‡É&ä{V¶iŽ”.Ó×Õÿ û?h¬Mt–íª[ÿ Ñÿ ÌV(í}=ibÔ¡›¥¢±b Lô¥‡piη_Z<‡z§èŒ)iÖwiÇ 2hÙ3·=’d÷8éŽ1¦¸c¤µ€7›7Ø ð\á)} ¹fËí›pAÃL%âc2 í§æQz¿;T8sæ°qø)QFMð‰XŒÂ±N¢aF¨…8¯!U  Z©RÊ ÖPVÄÀÍin™Ì-GˆªÅËŠ›•zË}º±ŽÍFò¹}Uw×#ä5B¤{î}Ð<ÙD é©¤&‡ïDbàÁôMÁ." ¤‡ú*õ'VŽ|¼´Úgllº¼klz[Æüï÷Aób‡Eÿ dÑ»Xx9ÃÜ£ÁT/`¼¸vI±Ýµ·Ë‚“G³þ*Ÿû´r|*}<¨îºœ @¦mÄ’M¹”.œ«Y–|6ÏU¤jç¥ÕÞqO ˜kDÆÁ¨5ÿ š;ÐЦ¦€GÙk \ –Þ=â¼=SͧµªS°ÚÍpÜãQűÀõ¬?ÃÁ1Ñ•õZà?hóœ€ L¦l{Y*K˜Ù›zc˜–ˆâ ø+¾ ­-Ök¥%ùEÜA'}ˆ><ÊIè“bpÍ/qÞâvoX€w,\úªò6Z[XdÒæ­@Ö—€$òJí#é>'°Ú ôª˜<)4ryÙ£|óAÅn5žêŸyÒäMÝ2{"}‰–¤l÷ûWX\l¾Á¸góÉOÔ /óñB¤f¸çñ[.P˜ZsÊË*ßT܈§QN¢’¡¨§V¼(Üù*eÕ“”5T¨‹Âê¥FŒã½Dü[8'Ò¥a…Ú¶k7a *•›¼'Ò·\8¨ª\@\õ¢¦íq+DÙrmÎ…_ªæ»ŠÓœ¡¯’Ré9MÅ×D™lælffc+ŒÑ,ý™ÿ ¯þǤ=Å’Á7µ÷ÚÛ/“Ü€ñýã¼àí¾ÕÑ+ƒ,uµMâÀÄbm:ÒÎPæ{˜Gz[ƒ¯«® KHà`ߨŠéí¯P8Aq.C‰ à€kòpj´kN¶qô€…Õ,ÜNŠª-­{Zö’æû44‰sŽè‰îVíRœÕm" 6?³D9¡ÇTíÅꋇ`4«¸ÝÁô ï’ýorqКÇZ«x4Žâéþuïf¹µö[P ,Q£éaX±`PÉÍZ ¸äYúg üAx ’6Lê‚xÝÓ*äQ  Ï’¨hÍ =²,6ï#rÃ<¯–£»ƒ‹,–ê•€ aÛsñ'%Æ"®ÛüìBᝠHÚ3ß°©$“XnœÖ’î2ËTeûìxîß ¦å¿çÉ ðK§þ{‘t‚Ϋ¬jéîZ[ ”š7L¥4VÚCE×]m¤Øy”ä4-dz£œ§¸x.*ãÊÊ b÷•h:©‡¦s`BTÁRû¾g⻩‹jø sF¢àJøFl‘È•Xᓁà~*j¯ +(ÚÕ6-£¯÷GŠØy‚<Ç’.F‹Hœw(+)ÜÜâÈzÄäT§FߘãÏ;DmVœ3Àu@mÚüXÝü•3B¨òÌÁÛ<·ÃÜ z,Ì@õÅ·d2]ü8s÷IôÞ¯^Ç9¢u„~ëAŸï4«M? K]­ÅàPl@s_ p:°¬ZR”´›JC[CS.h‹ƒïËœ«Æ]–÷ó‚wR×k7X‰k›‘´ù¦=¡«‰¨¨Â')—71ó’c‡Ðúµ `é.{§p¹ój\Ž{1h{o±Ý=áUÊïGÖŒõ–-BÄm+AZX¶¡ ïHðæ¥JmÙ;…䡟ˆ¦ ° äšiÉg«$üMk5¤L“’çÊvïâï ,=f“"íἊ5ô¬x6{ɏžID0e¸vçmi'︧ºð9$ò¹÷*£’9ÿ ²TÔ…×>JV¥}Œ}$p[bÔ®*[jzS*8 ”·T›Í–ñUîƒwo$áè=LT™ç—~ô·¤ÈÚ$榍q‰„+´kFm)ž‹©i–ËqÞŠ‰à¶ü( ‚•§ •°ò·‡#5ª•µÊ﯅¡X¨šÁ*F#TXJÊ ušJVÍ&=iÄs1‚3•'fý§5Ñ<=[íÞ­ PÚ;ѱÌ_~Ä££8rÞ ²w;’hDT°>ÈG¬8Á²ÚzŽ®ò®qZcqJêäÞ-ö[ܘbň±çb“ж31²n×iƒðÕ;1¶þÉ ªX‰,ßqÏ$>•î íZ¥Z 1{ç൵+ƒÕµ¥°T$§K]á»Ûï*·¤tMI’ÂZbŽÕiÒ˜}bÓ0£ª5›¨ [5Ž^ÝœWøÂÝh° ¢OWun£¤5 a2Z.G2³YL]jåtì”ä ÁÓ‘%"©<Ôúʰsº UZvä‡ÄiÆÒM .÷V·™ø#kèýiíÌ–ª)µT[)BˆõÑ xB¾B€ÖT¨.¥~ð@VĶr#¸ü*åZNDŽH;âi ],©£öØpù(šºãö¼T.uCê•4@ÿ GÕÛ)Cx›®0ø#:ÏðFÒbR\(€€Ä®fã4Þ‰Fä¯HXƒÅ,†öEÑÔÜ]Öv²?tLÃvBY£ú6Êu5ÅAQ³1‘’¬x–HŒÐ‡ ^ ¸KwJôÖŽ5×CÚ¨vÜ«/B0$×k°=ðbÇ(Ï)w±A†Á† 11Í=èQšµ626ŒÜ/`G«µ<}—-Ö7KEHÈÉðóȤmݱû±·ø«Snmá=“䫚mݱŸ¡¶~ó·“äUóJæúòB|E LêŽy´jDÔ$G¢þÐñ7óR8ýÒ…Ç› WVe#·Ÿ p·Fx~•ݤF÷0Èÿ K¯æS<6’¡WШ; ´ÿ ¥Êø\Òuî†åÝ–VNœkÒ7oòX¨Á­Ø÷FÎÑä±g÷ÿ M~Çî=p,X´ ÝÌÚÅ‹’ÃjÖ.ØöÏñ qïQ¤ÓZE†° =6·]܈ s¸>v•Ž^Ý\wq9r‰Î\¸¡kURÒ$­*‹Nq?Þª*!sŠÆ:TU_u±T+øX¡ ®¹¡,ÄâÃBTsÜ$Ø›4m椴zÜK]’’›Pƒ @€#â˜`é¹=I‡fiV•Ôî“nRm+µFPOhÍ0B£ €+¬5c v•:P'ÒyÎ ‰V~‚Ó†ÖuókDoh$å\*ö%Ю=£«…aȼ½÷Û.-½VŒŠ¼'lyî±1¬3ó#ÞE¿ÔS¤gV£m›=§\û"—WU¤ÚǼÿ ÂnÁGŒÃ ‚õN D³õNÚíŒÕ;HôyÄÈ©P¹Ä{:?R‘Ô¨âF÷ø£bÅó® JS|‚R÷ivýáâ€Æé¡è³´IئÑT!§˜•ت‚¬â@q€wnïCWÄ@JU€ê¯m6]Ï:£âx'+ÒðXvÓ¦Úm=–´7œ $ì“B£~p%ÕŸUþ« N@¼üï~w˜ñø5®—'Ôe»¤5ã//€ž~‰Tþ›Å7•#¤× Íö pÄ$ùeåì*«ÓŠEØWEÈsßg ¦ûvžSsLpºÊW–âµEWöˬH; ™!CYõZ ÃÄf æ#1W. \uWâ\,\Çf j’<qTbên›Î[vxx£ë 'ö¨1›˜ÀM¼Pÿ H)ƒêêŒA7s,|F“ 꺸k³9Ìö*ç®;Ö!Ö$Eiž•¹ÒÚ†ýóéÝû¾ÕS®ó$’NÝäŸz¤5r¦ãÄÃD÷Üø!°ø‡Ô&@m™Ì^Ãä­d q5Lnÿ N;.6½·N|#ä"1Nƒx“ã<3('&ñßt  ~ªu”1Tb㫨9ê–›–bìd$ߣ=#ÕãÒmU¯eí$EFù5ýYô櫨æì™Ç—±ssM]·á¿0ÕåJRÓªîiƒ+O58ÖñªŠÒx" \µâá¨i’¤i —Ö ” M+M¤ë9‚‰A¦°Qõ¾ßøK~¼Ã‘g…Ö´~÷Ï[3GUœÒ½#…kàÔ®Ò”‰³·dWV‰IP‰Ú8u¹”E ÖqLj¾êÕCBš{A^Âß;–¨`¯¬ìö ˼ ×tìø.tƐm*n¨y4o&Àx¥n¦×î‡aupáÛj8¿m›è¶ã!o½;ß0y^ý×^EÑ¿ÒjzŒ­)vÚÑnÄL …^ªô× ‡—‚3k Îý­hï]içå–îÏ*÷ñþ»Ô CÒjøjÍznˆ´ ¹#b'Fô‹ ‰v¥'’à'T´ƒHýÍ%M‰ ƒ&ÆÇŒï1 ‘ –Þ ‰i¬s žR-Ÿ kЬá¬7:þ 0ŒÅÒÕ/aÙ¬ÃÝ#Úøœ ©aiVc‰. ¹¦ãµ” ›Yg¦›ÆÎýº°f³7ƒhá·¸­}&D9¡ÂsÉÙÞèŠõØàC™¨ñbFC|´Ü(ŸƒÚÒ-%»'a Ì¿)ËÇn¿úÿ ÞŽX…4ÊÅH^ôΑí@ù¹Eh¶“L8Çjù ¼ÎåVªóR©Ï5uà V4lZß®=€xÖŸ–ÑÈ ÷”¨°¾__yM1tÉ?uÆþIkÄgæ@þ[¢†°XÃJ£j·:nkÅ¢u ‘}âGzö­/IµèЬ¼48q¦F°ŽR¼=ûì{´¯RýicS ÕÛ íNtÍÙï£,w4rêì®»~x(©Uñ§#Ñ&œÕ¤>ÎåÍÓ9’Ö{9eV­[Öjâ²ãu]˜å2›qÑšÕJç0€sÄ|Êëè0튔bÁ>“{×_F`Ø©ºê:µä,v¤ðfc1±"«ÔÍän1#=· Âøv~H½ÐßA¾¿Ü€Óš]Õ; I¾÷ç‚Qi†î¹9ywÔKG˜áñ zQY—§ÃÕZ07§X‚ Áh;ÁM)iÌCH-¯T‘ë|A0{Ò½LÚ–TâÖkÜ’dÀ“rmm»”جPF³ÖcbE§T€ÒxKºû’Ó®7±²(\4ŽÃ¸Uu@j™yĵ;³µ!Á¢b.W¤=mõ´êµK k ¸K^ÜÛ#p*Ü14qkZç5ïë †°5Ï%ÍÛ<Õ¤×Ô¥ê†C Õ´¼ú$ƒÖ“”]Ù¬qÞÚ[4©ý!ûÏ—Áb쳐XµA¬â~`›Çr¸8ìùÝ䫦<>ä÷«?xs´ÇÑ /á;¹øüÊÈÙà{"@Žïzâ¬[âß‚ U_<ÇŸ½4èN˜ú61®qŠu ¦þF£»äJ_ˆÙÎ~ ÞAã–݄ϗrŠD;xTž‘ô`É«…suãO`?³à™ô Lý#Íc5öoæØ‚y´´÷«ZR§<&JÇ+éâô´€i!Àˆ0æAoàðLèÖ-2ŸõW.’t^–(KÁmHµV@xÜÇy®Ñø­â^:Ú3w· 7½¹°ñ¸â¹®:',«Mœ—n­Á+Ãbš LÈ‘ÄnRÓÅœ%¦²‰¨ùQ:¤f‚ "PÕtô¸…cæl…&˜Ú˜Ôkv‹ž+vŠ,=¢v­6—Xy*¥t£«<™:“aîϲ=¦6rO]XI¿Œ÷¤zÚ­›¶ 6÷”w\d ü~v®ˆÌk«^m<ÿ ¢‰Õ\)ùºŽ;… lîÙÅEŠ®cѾ@vnMÏ,¼“ñ•ŽBxðÃzãÇç%3ˆ"}Ù•Åî> BÉú;Ò]V+P˜F_´ßé> Øše|ï‡ÄOmFæÇ ãqÞ$/xÐx­z`ï9"œÜij‚!7.\Td…9M‡•iŽ‹¾‘50ÞŽn¥ß4ÉôO ¹*í^QêËÜÇÌ8=ާs‰'ÂëÙ«á%Pú[O †ÅP¯Vsް.‰,kc¶ ¬A9n˜XÎ-ÞšN["¹QÕ‰ƒMýÁߺXJæÍaLj¾×Ãmã¾ãÚ uñÒþåQô¦¥ /ÄUx:‚ÍÜ’ Đ©ØÝ3V¨‰ÕnÐ6ó*óúK­«…c ¯U òhsý­jóÔj#,ímŒRµ«lbïUTŒÑ8†Ä0œÏr`ð¡¬É Ї ë"À² ™ 6¥ f¶ ¢ÚoܱԷ-<Àî)†a¶ž'Ú»¨TXqØæ¶÷YÄHy˜9ÈIW­YÀuMFë ºÏ’AqÌ4·/Ú †ô'i$øä­=Ä Ý|öK×40è|È6p‘0§)o¥ctî§H+CA-“ xØ|ÐXАç l8íºð3Ø:³¤¬KX¯UÿÙ /** * Javascript implementation of PKCS#12. * * @author Dave Longley * @author Stefan Siegl * * Copyright (c) 2010-2014 Digital Bazaar, Inc. * Copyright (c) 2012 Stefan Siegl * * The ASN.1 representation of PKCS#12 is as follows * (see ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-12/pkcs-12-tc1.pdf for details) * * PFX ::= SEQUENCE { * version INTEGER {v3(3)}(v3,...), * authSafe ContentInfo, * macData MacData OPTIONAL * } * * MacData ::= SEQUENCE { * mac DigestInfo, * macSalt OCTET STRING, * iterations INTEGER DEFAULT 1 * } * Note: The iterations default is for historical reasons and its use is * deprecated. A higher value, like 1024, is recommended. * * DigestInfo is defined in PKCS#7 as follows: * * DigestInfo ::= SEQUENCE { * digestAlgorithm DigestAlgorithmIdentifier, * digest Digest * } * * DigestAlgorithmIdentifier ::= AlgorithmIdentifier * * The AlgorithmIdentifier contains an Object Identifier (OID) and parameters * for the algorithm, if any. In the case of SHA1 there is none. * * AlgorithmIdentifer ::= SEQUENCE { * algorithm OBJECT IDENTIFIER, * parameters ANY DEFINED BY algorithm OPTIONAL * } * * Digest ::= OCTET STRING * * * ContentInfo ::= SEQUENCE { * contentType ContentType, * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL * } * * ContentType ::= OBJECT IDENTIFIER * * AuthenticatedSafe ::= SEQUENCE OF ContentInfo * -- Data if unencrypted * -- EncryptedData if password-encrypted * -- EnvelopedData if public key-encrypted * * * SafeContents ::= SEQUENCE OF SafeBag * * SafeBag ::= SEQUENCE { * bagId BAG-TYPE.&id ({PKCS12BagSet}) * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}), * bagAttributes SET OF PKCS12Attribute OPTIONAL * } * * PKCS12Attribute ::= SEQUENCE { * attrId ATTRIBUTE.&id ({PKCS12AttrSet}), * attrValues SET OF ATTRIBUTE.&Type ({PKCS12AttrSet}{@attrId}) * } -- This type is compatible with the X.500 type 'Attribute' * * PKCS12AttrSet ATTRIBUTE ::= { * friendlyName | -- from PKCS #9 * localKeyId, -- from PKCS #9 * ... -- Other attributes are allowed * } * * CertBag ::= SEQUENCE { * certId BAG-TYPE.&id ({CertTypes}), * certValue [0] EXPLICIT BAG-TYPE.&Type ({CertTypes}{@certId}) * } * * x509Certificate BAG-TYPE ::= {OCTET STRING IDENTIFIED BY {certTypes 1}} * -- DER-encoded X.509 certificate stored in OCTET STRING * * sdsiCertificate BAG-TYPE ::= {IA5String IDENTIFIED BY {certTypes 2}} * -- Base64-encoded SDSI certificate stored in IA5String * * CertTypes BAG-TYPE ::= { * x509Certificate | * sdsiCertificate, * ... -- For future extensions * } */ var forge = require('./forge'); require('./asn1'); require('./hmac'); require('./oids'); require('./pkcs7asn1'); require('./pbe'); require('./random'); require('./rsa'); require('./sha1'); require('./util'); require('./x509'); // shortcut for asn.1 & PKI API var asn1 = forge.asn1; var pki = forge.pki; // shortcut for PKCS#12 API var p12 = module.exports = forge.pkcs12 = forge.pkcs12 || {}; var contentInfoValidator = { name: 'ContentInfo', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.SEQUENCE, // a ContentInfo constructed: true, value: [{ name: 'ContentInfo.contentType', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.OID, constructed: false, capture: 'contentType' }, { name: 'ContentInfo.content', tagClass: asn1.Class.CONTEXT_SPECIFIC, constructed: true, captureAsn1: 'content' }] }; var pfxValidator = { name: 'PFX', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.SEQUENCE, constructed: true, value: [{ name: 'PFX.version', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.INTEGER, constructed: false, capture: 'version' }, contentInfoValidator, { name: 'PFX.macData', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.SEQUENCE, constructed: true, optional: true, captureAsn1: 'mac', value: [{ name: 'PFX.macData.mac', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.SEQUENCE, // DigestInfo constructed: true, value: [{ name: 'PFX.macData.mac.digestAlgorithm', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.SEQUENCE, // DigestAlgorithmIdentifier constructed: true, value: [{ name: 'PFX.macData.mac.digestAlgorithm.algorithm', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.OID, constructed: false, capture: 'macAlgorithm' }, { name: 'PFX.macData.mac.digestAlgorithm.parameters', tagClass: asn1.Class.UNIVERSAL, captureAsn1: 'macAlgorithmParameters' }] }, { name: 'PFX.macData.mac.digest', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.OCTETSTRING, constructed: false, capture: 'macDigest' }] }, { name: 'PFX.macData.macSalt', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.OCTETSTRING, constructed: false, capture: 'macSalt' }, { name: 'PFX.macData.iterations', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.INTEGER, constructed: false, optional: true, capture: 'macIterations' }] }] }; var safeBagValidator = { name: 'SafeBag', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.SEQUENCE, constructed: true, value: [{ name: 'SafeBag.bagId', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.OID, constructed: false, capture: 'bagId' }, { name: 'SafeBag.bagValue', tagClass: asn1.Class.CONTEXT_SPECIFIC, constructed: true, captureAsn1: 'bagValue' }, { name: 'SafeBag.bagAttributes', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.SET, constructed: true, optional: true, capture: 'bagAttributes' }] }; var attributeValidator = { name: 'Attribute', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.SEQUENCE, constructed: true, value: [{ name: 'Attribute.attrId', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.OID, constructed: false, capture: 'oid' }, { name: 'Attribute.attrValues', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.SET, constructed: true, capture: 'values' }] }; var certBagValidator = { name: 'CertBag', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.SEQUENCE, constructed: true, value: [{ name: 'CertBag.certId', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.OID, constructed: false, capture: 'certId' }, { name: 'CertBag.certValue', tagClass: asn1.Class.CONTEXT_SPECIFIC, constructed: true, /* So far we only support X.509 certificates (which are wrapped in an OCTET STRING, hence hard code that here). */ value: [{ name: 'CertBag.certValue[0]', tagClass: asn1.Class.UNIVERSAL, type: asn1.Class.OCTETSTRING, constructed: false, capture: 'cert' }] }] }; /** * Search SafeContents structure for bags with matching attributes. * * The search can optionally be narrowed by a certain bag type. * * @param safeContents the SafeContents structure to search in. * @param attrName the name of the attribute to compare against. * @param attrValue the attribute value to search for. * @param [bagType] bag type to narrow search by. * * @return an array of matching bags. */ function _getBagsByAttribute(safeContents, attrName, attrValue, bagType) { var result = []; for(var i = 0; i < safeContents.length; i++) { for(var j = 0; j < safeContents[i].safeBags.length; j++) { var bag = safeContents[i].safeBags[j]; if(bagType !== undefined && bag.type !== bagType) { continue; } // only filter by bag type, no attribute specified if(attrName === null) { result.push(bag); continue; } if(bag.attributes[attrName] !== undefined && bag.attributes[attrName].indexOf(attrValue) >= 0) { result.push(bag); } } } return result; } /** * Converts a PKCS#12 PFX in ASN.1 notation into a PFX object. * * @param obj The PKCS#12 PFX in ASN.1 notation. * @param strict true to use strict DER decoding, false not to (default: true). * @param {String} password Password to decrypt with (optional). * * @return PKCS#12 PFX object. */ p12.pkcs12FromAsn1 = function(obj, strict, password) { // handle args if(typeof strict === 'string') { password = strict; strict = true; } else if(strict === undefined) { strict = true; } // validate PFX and capture data var capture = {}; var errors = []; if(!asn1.validate(obj, pfxValidator, capture, errors)) { var error = new Error('Cannot read PKCS#12 PFX. ' + 'ASN.1 object is not an PKCS#12 PFX.'); error.errors = error; throw error; } var pfx = { version: capture.version.charCodeAt(0), safeContents: [], /** * Gets bags with matching attributes. * * @param filter the attributes to filter by: * [localKeyId] the localKeyId to search for. * [localKeyIdHex] the localKeyId in hex to search for. * [friendlyName] the friendly name to search for. * [bagType] bag type to narrow each attribute search by. * * @return a map of attribute type to an array of matching bags or, if no * attribute was given but a bag type, the map key will be the * bag type. */ getBags: function(filter) { var rval = {}; var localKeyId; if('localKeyId' in filter) { localKeyId = filter.localKeyId; } else if('localKeyIdHex' in filter) { localKeyId = forge.util.hexToBytes(filter.localKeyIdHex); } // filter on bagType only if(localKeyId === undefined && !('friendlyName' in filter) && 'bagType' in filter) { rval[filter.bagType] = _getBagsByAttribute( pfx.safeContents, null, null, filter.bagType); } if(localKeyId !== undefined) { rval.localKeyId = _getBagsByAttribute( pfx.safeContents, 'localKeyId', localKeyId, filter.bagType); } if('friendlyName' in filter) { rval.friendlyName = _getBagsByAttribute( pfx.safeContents, 'friendlyName', filter.friendlyName, filter.bagType); } return rval; }, /** * DEPRECATED: use getBags() instead. * * Get bags with matching friendlyName attribute. * * @param friendlyName the friendly name to search for. * @param [bagType] bag type to narrow search by. * * @return an array of bags with matching friendlyName attribute. */ getBagsByFriendlyName: function(friendlyName, bagType) { return _getBagsByAttribute( pfx.safeContents, 'friendlyName', friendlyName, bagType); }, /** * DEPRECATED: use getBags() instead. * * Get bags with matching localKeyId attribute. * * @param localKeyId the localKeyId to search for. * @param [bagType] bag type to narrow search by. * * @return an array of bags with matching localKeyId attribute. */ getBagsByLocalKeyId: function(localKeyId, bagType) { return _getBagsByAttribute( pfx.safeContents, 'localKeyId', localKeyId, bagType); } }; if(capture.version.charCodeAt(0) !== 3) { var error = new Error('PKCS#12 PFX of version other than 3 not supported.'); error.version = capture.version.charCodeAt(0); throw error; } if(asn1.derToOid(capture.contentType) !== pki.oids.data) { var error = new Error('Only PKCS#12 PFX in password integrity mode supported.'); error.oid = asn1.derToOid(capture.contentType); throw error; } var data = capture.content.value[0]; if(data.tagClass !== asn1.Class.UNIVERSAL || data.type !== asn1.Type.OCTETSTRING) { throw new Error('PKCS#12 authSafe content data is not an OCTET STRING.'); } data = _decodePkcs7Data(data); // check for MAC if(capture.mac) { var md = null; var macKeyBytes = 0; var macAlgorithm = asn1.derToOid(capture.macAlgorithm); switch(macAlgorithm) { case pki.oids.sha1: md = forge.md.sha1.create(); macKeyBytes = 20; break; case pki.oids.sha256: md = forge.md.sha256.create(); macKeyBytes = 32; break; case pki.oids.sha384: md = forge.md.sha384.create(); macKeyBytes = 48; break; case pki.oids.sha512: md = forge.md.sha512.create(); macKeyBytes = 64; break; case pki.oids.md5: md = forge.md.md5.create(); macKeyBytes = 16; break; } if(md === null) { throw new Error('PKCS#12 uses unsupported MAC algorithm: ' + macAlgorithm); } // verify MAC (iterations default to 1) var macSalt = new forge.util.ByteBuffer(capture.macSalt); var macIterations = (('macIterations' in capture) ? parseInt(forge.util.bytesToHex(capture.macIterations), 16) : 1); var macKey = p12.generateKey( password, macSalt, 3, macIterations, macKeyBytes, md); var mac = forge.hmac.create(); mac.start(md, macKey); mac.update(data.value); var macValue = mac.getMac(); if(macValue.getBytes() !== capture.macDigest) { throw new Error('PKCS#12 MAC could not be verified. Invalid password?'); } } _decodeAuthenticatedSafe(pfx, data.value, strict, password); return pfx; }; /** * Decodes PKCS#7 Data. PKCS#7 (RFC 2315) defines "Data" as an OCTET STRING, * but it is sometimes an OCTET STRING that is composed/constructed of chunks, * each its own OCTET STRING. This is BER-encoding vs. DER-encoding. This * function transforms this corner-case into the usual simple, * non-composed/constructed OCTET STRING. * * This function may be moved to ASN.1 at some point to better deal with * more BER-encoding issues, should they arise. * * @param data the ASN.1 Data object to transform. */ function _decodePkcs7Data(data) { // handle special case of "chunked" data content: an octet string composed // of other octet strings if(data.composed || data.constructed) { var value = forge.util.createBuffer(); for(var i = 0; i < data.value.length; ++i) { value.putBytes(data.value[i].value); } data.composed = data.constructed = false; data.value = value.getBytes(); } return data; } /** * Decode PKCS#12 AuthenticatedSafe (BER encoded) into PFX object. * * The AuthenticatedSafe is a BER-encoded SEQUENCE OF ContentInfo. * * @param pfx The PKCS#12 PFX object to fill. * @param {String} authSafe BER-encoded AuthenticatedSafe. * @param strict true to use strict DER decoding, false not to. * @param {String} password Password to decrypt with (optional). */ function _decodeAuthenticatedSafe(pfx, authSafe, strict, password) { authSafe = asn1.fromDer(authSafe, strict); /* actually it's BER encoded */ if(authSafe.tagClass !== asn1.Class.UNIVERSAL || authSafe.type !== asn1.Type.SEQUENCE || authSafe.constructed !== true) { throw new Error('PKCS#12 AuthenticatedSafe expected to be a ' + 'SEQUENCE OF ContentInfo'); } for(var i = 0; i < authSafe.value.length; i++) { var contentInfo = authSafe.value[i]; // validate contentInfo and capture data var capture = {}; var errors = []; if(!asn1.validate(contentInfo, contentInfoValidator, capture, errors)) { var error = new Error('Cannot read ContentInfo.'); error.errors = errors; throw error; } var obj = { encrypted: false }; var safeContents = null; var data = capture.content.value[0]; switch(asn1.derToOid(capture.contentType)) { case pki.oids.data: if(data.tagClass !== asn1.Class.UNIVERSAL || data.type !== asn1.Type.OCTETSTRING) { throw new Error('PKCS#12 SafeContents Data is not an OCTET STRING.'); } safeContents = _decodePkcs7Data(data).value; break; case pki.oids.encryptedData: safeContents = _decryptSafeContents(data, password); obj.encrypted = true; break; default: var error = new Error('Unsupported PKCS#12 contentType.'); error.contentType = asn1.derToOid(capture.contentType); throw error; } obj.safeBags = _decodeSafeContents(safeContents, strict, password); pfx.safeContents.push(obj); } } /** * Decrypt PKCS#7 EncryptedData structure. * * @param data ASN.1 encoded EncryptedContentInfo object. * @param password The user-provided password. * * @return The decrypted SafeContents (ASN.1 object). */ function _decryptSafeContents(data, password) { var capture = {}; var errors = []; if(!asn1.validate( data, forge.pkcs7.asn1.encryptedDataValidator, capture, errors)) { var error = new Error('Cannot read EncryptedContentInfo.'); error.errors = errors; throw error; } var oid = asn1.derToOid(capture.contentType); if(oid !== pki.oids.data) { var error = new Error( 'PKCS#12 EncryptedContentInfo ContentType is not Data.'); error.oid = oid; throw error; } // get cipher oid = asn1.derToOid(capture.encAlgorithm); var cipher = pki.pbe.getCipher(oid, capture.encParameter, password); // get encrypted data var encryptedContentAsn1 = _decodePkcs7Data(capture.encryptedContentAsn1); var encrypted = forge.util.createBuffer(encryptedContentAsn1.value); cipher.update(encrypted); if(!cipher.finish()) { throw new Error('Failed to decrypt PKCS#12 SafeContents.'); } return cipher.output.getBytes(); } /** * Decode PKCS#12 SafeContents (BER-encoded) into array of Bag objects. * * The safeContents is a BER-encoded SEQUENCE OF SafeBag. * * @param {String} safeContents BER-encoded safeContents. * @param strict true to use strict DER decoding, false not to. * @param {String} password Password to decrypt with (optional). * * @return {Array} Array of Bag objects. */ function _decodeSafeContents(safeContents, strict, password) { // if strict and no safe contents, return empty safes if(!strict && safeContents.length === 0) { return []; } // actually it's BER-encoded safeContents = asn1.fromDer(safeContents, strict); if(safeContents.tagClass !== asn1.Class.UNIVERSAL || safeContents.type !== asn1.Type.SEQUENCE || safeContents.constructed !== true) { throw new Error( 'PKCS#12 SafeContents expected to be a SEQUENCE OF SafeBag.'); } var res = []; for(var i = 0; i < safeContents.value.length; i++) { var safeBag = safeContents.value[i]; // validate SafeBag and capture data var capture = {}; var errors = []; if(!asn1.validate(safeBag, safeBagValidator, capture, errors)) { var error = new Error('Cannot read SafeBag.'); error.errors = errors; throw error; } /* Create bag object and push to result array. */ var bag = { type: asn1.derToOid(capture.bagId), attributes: _decodeBagAttributes(capture.bagAttributes) }; res.push(bag); var validator, decoder; var bagAsn1 = capture.bagValue.value[0]; switch(bag.type) { case pki.oids.pkcs8ShroudedKeyBag: /* bagAsn1 has a EncryptedPrivateKeyInfo, which we need to decrypt. Afterwards we can handle it like a keyBag, which is a PrivateKeyInfo. */ bagAsn1 = pki.decryptPrivateKeyInfo(bagAsn1, password); if(bagAsn1 === null) { throw new Error( 'Unable to decrypt PKCS#8 ShroudedKeyBag, wrong password?'); } /* fall through */ case pki.oids.keyBag: /* A PKCS#12 keyBag is a simple PrivateKeyInfo as understood by our PKI module, hence we don't have to do validation/capturing here, just pass what we already got. */ try { bag.key = pki.privateKeyFromAsn1(bagAsn1); } catch(e) { // ignore unknown key type, pass asn1 value bag.key = null; bag.asn1 = bagAsn1; } continue; /* Nothing more to do. */ case pki.oids.certBag: /* A PKCS#12 certBag can wrap both X.509 and sdsi certificates. Therefore put the SafeBag content through another validator to capture the fields. Afterwards check & store the results. */ validator = certBagValidator; decoder = function() { if(asn1.derToOid(capture.certId) !== pki.oids.x509Certificate) { var error = new Error( 'Unsupported certificate type, only X.509 supported.'); error.oid = asn1.derToOid(capture.certId); throw error; } // true=produce cert hash var certAsn1 = asn1.fromDer(capture.cert, strict); try { bag.cert = pki.certificateFromAsn1(certAsn1, true); } catch(e) { // ignore unknown cert type, pass asn1 value bag.cert = null; bag.asn1 = certAsn1; } }; break; default: var error = new Error('Unsupported PKCS#12 SafeBag type.'); error.oid = bag.type; throw error; } /* Validate SafeBag value (i.e. CertBag, etc.) and capture data if needed. */ if(validator !== undefined && !asn1.validate(bagAsn1, validator, capture, errors)) { var error = new Error('Cannot read PKCS#12 ' + validator.name); error.errors = errors; throw error; } /* Call decoder function from above to store the results. */ decoder(); } return res; } /** * Decode PKCS#12 SET OF PKCS12Attribute into JavaScript object. * * @param attributes SET OF PKCS12Attribute (ASN.1 object). * * @return the decoded attributes. */ function _decodeBagAttributes(attributes) { var decodedAttrs = {}; if(attributes !== undefined) { for(var i = 0; i < attributes.length; ++i) { var capture = {}; var errors = []; if(!asn1.validate(attributes[i], attributeValidator, capture, errors)) { var error = new Error('Cannot read PKCS#12 BagAttribute.'); error.errors = errors; throw error; } var oid = asn1.derToOid(capture.oid); if(pki.oids[oid] === undefined) { // unsupported attribute type, ignore. continue; } decodedAttrs[pki.oids[oid]] = []; for(var j = 0; j < capture.values.length; ++j) { decodedAttrs[pki.oids[oid]].push(capture.values[j].value); } } } return decodedAttrs; } /** * Wraps a private key and certificate in a PKCS#12 PFX wrapper. If a * password is provided then the private key will be encrypted. * * An entire certificate chain may also be included. To do this, pass * an array for the "cert" parameter where the first certificate is * the one that is paired with the private key and each subsequent one * verifies the previous one. The certificates may be in PEM format or * have been already parsed by Forge. * * @todo implement password-based-encryption for the whole package * * @param key the private key. * @param cert the certificate (may be an array of certificates in order * to specify a certificate chain). * @param password the password to use, null for none. * @param options: * algorithm the encryption algorithm to use * ('aes128', 'aes192', 'aes256', '3des'), defaults to 'aes128'. * count the iteration count to use. * saltSize the salt size to use. * useMac true to include a MAC, false not to, defaults to true. * localKeyId the local key ID to use, in hex. * friendlyName the friendly name to use. * generateLocalKeyId true to generate a random local key ID, * false not to, defaults to true. * * @return the PKCS#12 PFX ASN.1 object. */ p12.toPkcs12Asn1 = function(key, cert, password, options) { // set default options options = options || {}; options.saltSize = options.saltSize || 8; options.count = options.count || 2048; options.algorithm = options.algorithm || options.encAlgorithm || 'aes128'; if(!('useMac' in options)) { options.useMac = true; } if(!('localKeyId' in options)) { options.localKeyId = null; } if(!('generateLocalKeyId' in options)) { options.generateLocalKeyId = true; } var localKeyId = options.localKeyId; var bagAttrs; if(localKeyId !== null) { localKeyId = forge.util.hexToBytes(localKeyId); } else if(options.generateLocalKeyId) { // use SHA-1 of paired cert, if available if(cert) { var pairedCert = forge.util.isArray(cert) ? cert[0] : cert; if(typeof pairedCert === 'string') { pairedCert = pki.certificateFromPem(pairedCert); } var sha1 = forge.md.sha1.create(); sha1.update(asn1.toDer(pki.certificateToAsn1(pairedCert)).getBytes()); localKeyId = sha1.digest().getBytes(); } else { // FIXME: consider using SHA-1 of public key (which can be generated // from private key components), see: cert.generateSubjectKeyIdentifier // generate random bytes localKeyId = forge.random.getBytes(20); } } var attrs = []; if(localKeyId !== null) { attrs.push( // localKeyID asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ // attrId asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, asn1.oidToDer(pki.oids.localKeyId).getBytes()), // attrValues asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, localKeyId) ]) ])); } if('friendlyName' in options) { attrs.push( // friendlyName asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ // attrId asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, asn1.oidToDer(pki.oids.friendlyName).getBytes()), // attrValues asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BMPSTRING, false, options.friendlyName) ]) ])); } if(attrs.length > 0) { bagAttrs = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, attrs); } // collect contents for AuthenticatedSafe var contents = []; // create safe bag(s) for certificate chain var chain = []; if(cert !== null) { if(forge.util.isArray(cert)) { chain = cert; } else { chain = [cert]; } } var certSafeBags = []; for(var i = 0; i < chain.length; ++i) { // convert cert from PEM as necessary cert = chain[i]; if(typeof cert === 'string') { cert = pki.certificateFromPem(cert); } // SafeBag var certBagAttrs = (i === 0) ? bagAttrs : undefined; var certAsn1 = pki.certificateToAsn1(cert); var certSafeBag = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ // bagId asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, asn1.oidToDer(pki.oids.certBag).getBytes()), // bagValue asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [ // CertBag asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ // certId asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, asn1.oidToDer(pki.oids.x509Certificate).getBytes()), // certValue (x509Certificate) asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [ asn1.create( asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, asn1.toDer(certAsn1).getBytes()) ])])]), // bagAttributes (OPTIONAL) certBagAttrs ]); certSafeBags.push(certSafeBag); } if(certSafeBags.length > 0) { // SafeContents var certSafeContents = asn1.create( asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, certSafeBags); // ContentInfo var certCI = // PKCS#7 ContentInfo asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ // contentType asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, // OID for the content type is 'data' asn1.oidToDer(pki.oids.data).getBytes()), // content asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [ asn1.create( asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, asn1.toDer(certSafeContents).getBytes()) ]) ]); contents.push(certCI); } // create safe contents for private key var keyBag = null; if(key !== null) { // SafeBag var pkAsn1 = pki.wrapRsaPrivateKey(pki.privateKeyToAsn1(key)); if(password === null) { // no encryption keyBag = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ // bagId asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, asn1.oidToDer(pki.oids.keyBag).getBytes()), // bagValue asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [ // PrivateKeyInfo pkAsn1 ]), // bagAttributes (OPTIONAL) bagAttrs ]); } else { // encrypted PrivateKeyInfo keyBag = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ // bagId asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, asn1.oidToDer(pki.oids.pkcs8ShroudedKeyBag).getBytes()), // bagValue asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [ // EncryptedPrivateKeyInfo pki.encryptPrivateKeyInfo(pkAsn1, password, options) ]), // bagAttributes (OPTIONAL) bagAttrs ]); } // SafeContents var keySafeContents = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [keyBag]); // ContentInfo var keyCI = // PKCS#7 ContentInfo asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ // contentType asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, // OID for the content type is 'data' asn1.oidToDer(pki.oids.data).getBytes()), // content asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [ asn1.create( asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, asn1.toDer(keySafeContents).getBytes()) ]) ]); contents.push(keyCI); } // create AuthenticatedSafe by stringing together the contents var safe = asn1.create( asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, contents); var macData; if(options.useMac) { // MacData var sha1 = forge.md.sha1.create(); var macSalt = new forge.util.ByteBuffer( forge.random.getBytes(options.saltSize)); var count = options.count; // 160-bit key var key = p12.generateKey(password, macSalt, 3, count, 20); var mac = forge.hmac.create(); mac.start(sha1, key); mac.update(asn1.toDer(safe).getBytes()); var macValue = mac.getMac(); macData = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ // mac DigestInfo asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ // digestAlgorithm asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ // algorithm = SHA-1 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, asn1.oidToDer(pki.oids.sha1).getBytes()), // parameters = Null asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '') ]), // digest asn1.create( asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, macValue.getBytes()) ]), // macSalt OCTET STRING asn1.create( asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, macSalt.getBytes()), // iterations INTEGER (XXX: Only support count < 65536) asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(count).getBytes() ) ]); } // PFX return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ // version (3) asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, asn1.integerToDer(3).getBytes()), // PKCS#7 ContentInfo asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ // contentType asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, // OID for the content type is 'data' asn1.oidToDer(pki.oids.data).getBytes()), // content asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [ asn1.create( asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, asn1.toDer(safe).getBytes()) ]) ]), macData ]); }; /** * Derives a PKCS#12 key. * * @param password the password to derive the key material from, null or * undefined for none. * @param salt the salt, as a ByteBuffer, to use. * @param id the PKCS#12 ID byte (1 = key material, 2 = IV, 3 = MAC). * @param iter the iteration count. * @param n the number of bytes to derive from the password. * @param md the message digest to use, defaults to SHA-1. * * @return a ByteBuffer with the bytes derived from the password. */ p12.generateKey = forge.pbe.generatePkcs12Key;