PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /usr/share/perl5/vendor_perl/IO/Socket/SSL
Viewing File: /usr/share/perl5/vendor_perl/IO/Socket/SSL/Utils.pm
package IO::Socket::SSL::Utils; use strict; use warnings; use Carp 'croak'; use Net::SSLeay; # old versions of Exporter do not export 'import' yet require Exporter; *import = \&Exporter::import; our $VERSION = '2.014'; our @EXPORT = qw( PEM_file2cert PEM_string2cert PEM_cert2file PEM_cert2string PEM_file2key PEM_string2key PEM_key2file PEM_key2string KEY_free CERT_free KEY_create_rsa CERT_asHash CERT_create ); sub PEM_file2cert { my $file = shift; my $bio = Net::SSLeay::BIO_new_file($file,'r') or croak "cannot read $file: $!"; my $cert = Net::SSLeay::PEM_read_bio_X509($bio); Net::SSLeay::BIO_free($bio); $cert or croak "cannot parse $file as PEM X509 cert: ". Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error()); return $cert; } sub PEM_cert2file { my ($cert,$file) = @_; my $string = Net::SSLeay::PEM_get_string_X509($cert) or croak("cannot get string from cert"); open( my $fh,'>',$file ) or croak("cannot write $file: $!"); print $fh $string; } sub PEM_string2cert { my $string = shift; my $bio = Net::SSLeay::BIO_new( Net::SSLeay::BIO_s_mem()); Net::SSLeay::BIO_write($bio,$string); my $cert = Net::SSLeay::PEM_read_bio_X509($bio); Net::SSLeay::BIO_free($bio); $cert or croak "cannot parse string as PEM X509 cert: ". Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error()); return $cert; } sub PEM_cert2string { my $cert = shift; return Net::SSLeay::PEM_get_string_X509($cert) || croak("cannot get string from cert"); } sub PEM_file2key { my $file = shift; my $bio = Net::SSLeay::BIO_new_file($file,'r') or croak "cannot read $file: $!"; my $key = Net::SSLeay::PEM_read_bio_PrivateKey($bio); Net::SSLeay::BIO_free($bio); $key or croak "cannot parse $file as PEM private key: ". Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error()); return $key; } sub PEM_key2file { my ($key,$file) = @_; my $string = Net::SSLeay::PEM_get_string_PrivateKey($key) or croak("cannot get string from key"); open( my $fh,'>',$file ) or croak("cannot write $file: $!"); print $fh $string; } sub PEM_string2key { my $string = shift; my $bio = Net::SSLeay::BIO_new( Net::SSLeay::BIO_s_mem()); Net::SSLeay::BIO_write($bio,$string); my $key = Net::SSLeay::PEM_read_bio_PrivateKey($bio); Net::SSLeay::BIO_free($bio); $key or croak "cannot parse string as PEM private key: ". Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error()); return $key; } sub PEM_key2string { my $key = shift; return Net::SSLeay::PEM_get_string_PrivateKey($key) || croak("cannot get string from key"); } sub CERT_free { my $cert = shift or return; Net::SSLeay::X509_free($cert); } sub KEY_free { my $key = shift or return; Net::SSLeay::EVP_PKEY_free($key); } sub KEY_create_rsa { my $bits = shift || 2048; my $key = Net::SSLeay::EVP_PKEY_new(); my $rsa = Net::SSLeay::RSA_generate_key($bits, 0x10001); # 0x10001 = RSA_F4 Net::SSLeay::EVP_PKEY_assign_RSA($key,$rsa); return $key; } if (defined &Net::SSLeay::EC_KEY_generate_key) { push @EXPORT,'KEY_create_ec'; *KEY_create_ec = sub { my $curve = shift || 'prime256v1'; my $key = Net::SSLeay::EVP_PKEY_new(); my $ec = Net::SSLeay::EC_KEY_generate_key($curve); Net::SSLeay::EVP_PKEY_assign_EC_KEY($key,$ec); return $key; } } # extract information from cert my %gen2i = qw( OTHERNAME 0 EMAIL 1 DNS 2 X400 3 DIRNAME 4 EDIPARTY 5 URI 6 IP 7 RID 8 ); my %i2gen = reverse %gen2i; sub CERT_asHash { my $cert = shift; my $digest_name = shift || 'sha256'; my %hash = ( version => Net::SSLeay::X509_get_version($cert), not_before => _asn1t2t(Net::SSLeay::X509_get_notBefore($cert)), not_after => _asn1t2t(Net::SSLeay::X509_get_notAfter($cert)), serial => Net::SSLeay::P_ASN1_INTEGER_get_dec( Net::SSLeay::X509_get_serialNumber($cert)), signature_alg => Net::SSLeay::OBJ_obj2txt ( Net::SSLeay::P_X509_get_signature_alg($cert)), crl_uri => [ Net::SSLeay::P_X509_get_crl_distribution_points($cert) ], keyusage => [ Net::SSLeay::P_X509_get_key_usage($cert) ], extkeyusage => { oid => [ Net::SSLeay::P_X509_get_ext_key_usage($cert,0) ], nid => [ Net::SSLeay::P_X509_get_ext_key_usage($cert,1) ], sn => [ Net::SSLeay::P_X509_get_ext_key_usage($cert,2) ], ln => [ Net::SSLeay::P_X509_get_ext_key_usage($cert,3) ], }, "pubkey_digest_$digest_name" => Net::SSLeay::X509_pubkey_digest( $cert,_digest($digest_name)), "x509_digest_$digest_name" => Net::SSLeay::X509_digest( $cert,_digest($digest_name)), "fingerprint_$digest_name" => Net::SSLeay::X509_get_fingerprint( $cert,$digest_name), ); my $subj = Net::SSLeay::X509_get_subject_name($cert); my %subj; for ( 0..Net::SSLeay::X509_NAME_entry_count($subj)-1 ) { my $e = Net::SSLeay::X509_NAME_get_entry($subj,$_); my $o = Net::SSLeay::X509_NAME_ENTRY_get_object($e); $subj{ Net::SSLeay::OBJ_obj2txt($o) } = Net::SSLeay::P_ASN1_STRING_get( Net::SSLeay::X509_NAME_ENTRY_get_data($e)); } $hash{subject} = \%subj; if ( my @names = Net::SSLeay::X509_get_subjectAltNames($cert) ) { my $alt = $hash{subjectAltNames} = []; while (my ($t,$v) = splice(@names,0,2)) { $t = $i2gen{$t} || die "unknown type $t in subjectAltName"; if ( $t eq 'IP' ) { if (length($v) == 4) { $v = join('.',unpack("CCCC",$v)); } elsif ( length($v) == 16 ) { my @v = unpack("nnnnnnnn",$v); my ($best0,$last0); for(my $i=0;$i<@v;$i++) { if ($v[$i] == 0) { if ($last0) { $last0->[1] = $i; $last0->[2]++; $best0 = $last0 if ++$last0->[2]>$best0->[2]; } else { $last0 = [ $i,$i,0 ]; $best0 ||= $last0; } } else { $last0 = undef; } } if ($best0) { $v = ''; $v .= join(':', map { sprintf( "%x",$_) } @v[0..$best0->[0]-1]) if $best0->[0]>0; $v .= '::'; $v .= join(':', map { sprintf( "%x",$_) } @v[$best0->[1]+1..$#v]) if $best0->[1]<$#v; } else { $v = join(':', map { sprintf( "%x",$_) } @v); } } } push @$alt,[$t,$v] } } my $issuer = Net::SSLeay::X509_get_issuer_name($cert); my %issuer; for ( 0..Net::SSLeay::X509_NAME_entry_count($issuer)-1 ) { my $e = Net::SSLeay::X509_NAME_get_entry($issuer,$_); my $o = Net::SSLeay::X509_NAME_ENTRY_get_object($e); $issuer{ Net::SSLeay::OBJ_obj2txt($o) } = Net::SSLeay::P_ASN1_STRING_get( Net::SSLeay::X509_NAME_ENTRY_get_data($e)); } $hash{issuer} = \%issuer; my @ext; for( 0..Net::SSLeay::X509_get_ext_count($cert)-1 ) { my $e = Net::SSLeay::X509_get_ext($cert,$_); my $o = Net::SSLeay::X509_EXTENSION_get_object($e); my $nid = Net::SSLeay::OBJ_obj2nid($o); push @ext, { oid => Net::SSLeay::OBJ_obj2txt($o), nid => ( $nid > 0 ) ? $nid : undef, sn => ( $nid > 0 ) ? Net::SSLeay::OBJ_nid2sn($nid) : undef, critical => Net::SSLeay::X509_EXTENSION_get_critical($e), data => Net::SSLeay::X509V3_EXT_print($e), } } $hash{ext} = \@ext; if ( defined(&Net::SSLeay::P_X509_get_ocsp_uri)) { $hash{ocsp_uri} = [ Net::SSLeay::P_X509_get_ocsp_uri($cert) ]; } else { $hash{ocsp_uri} = []; for( @ext ) { $_->{sn} or next; $_->{sn} eq 'authorityInfoAccess' or next; push @{ $hash{ocsp_uri}}, $_->{data} =~m{\bOCSP - URI:(\S+)}g; } } return \%hash; } sub CERT_create { my %args = @_%2 ? %{ shift() } : @_; my $cert = Net::SSLeay::X509_new(); my $digest_name = delete $args{digest} || 'sha256'; Net::SSLeay::ASN1_INTEGER_set( Net::SSLeay::X509_get_serialNumber($cert), delete $args{serial} || rand(2**32), ); # version default to 2 (V3) Net::SSLeay::X509_set_version($cert, delete $args{version} || 2 ); # not_before default to now Net::SSLeay::ASN1_TIME_set( Net::SSLeay::X509_get_notBefore($cert), delete $args{not_before} || time() ); # not_after default to now+365 days Net::SSLeay::ASN1_TIME_set( Net::SSLeay::X509_get_notAfter($cert), delete $args{not_after} || time() + 365*86400 ); # set subject my $subj_e = Net::SSLeay::X509_get_subject_name($cert); my $subj = delete $args{subject} || { organizationName => 'IO::Socket::SSL', commonName => 'IO::Socket::SSL Test' }; while ( my ($k,$v) = each %$subj ) { # Not everything we get is nice - try with MBSTRING_UTF8 first and if it # fails try V_ASN1_T61STRING and finally V_ASN1_OCTET_STRING Net::SSLeay::X509_NAME_add_entry_by_txt($subj_e,$k,0x1000,$v,-1,0) or Net::SSLeay::X509_NAME_add_entry_by_txt($subj_e,$k,20,$v,-1,0) or Net::SSLeay::X509_NAME_add_entry_by_txt($subj_e,$k,4,$v,-1,0) or croak("failed to add entry for $k - ". Net::SSLeay::ERR_error_string(Net::SSLeay::ERR_get_error())); } my @ext = ( &Net::SSLeay::NID_subject_key_identifier => 'hash', &Net::SSLeay::NID_authority_key_identifier => 'keyid', ); if ( my $altsubj = delete $args{subjectAltNames} ) { push @ext, &Net::SSLeay::NID_subject_alt_name => join(',', map { "$_->[0]:$_->[1]" } @$altsubj) } my $key = delete $args{key} || KEY_create_rsa(); Net::SSLeay::X509_set_pubkey($cert,$key); my $is = delete $args{issuer}; my $issuer_cert = delete $args{issuer_cert} || $is && $is->[0] || $cert; my $issuer_key = delete $args{issuer_key} || $is && $is->[1] || $key; my %purpose; if (my $p = delete $args{purpose}) { if (!ref($p)) { $purpose{lc($2)} = (!$1 || $1 eq '+') ? 1:0 while $p =~m{([+-]?)(\w+)}g; } elsif (ref($p) eq 'ARRAY') { for(@$p) { m{^([+-]?)(\w+)$} or die "invalid entry in purpose: $_"; $purpose{lc($2)} = (!$1 || $1 eq '+') ? 1:0 } } else { while( my ($k,$v) = each %$p) { $purpose{lc($k)} = ($v && $v ne '-')?1:0; } } } if (delete $args{CA}) { # add defaults for CA %purpose = ( ca => 1, sslca => 1, emailca => 1, objca => 1, %purpose ); } if (!%purpose) { %purpose = (server => 1, client => 1); } my (%key_usage,%ext_key_usage,%cert_type,%basic_constraints); my %dS = ( digitalSignature => \%key_usage ); my %kE = ( keyEncipherment => \%key_usage ); my %CA = ( 'CA:TRUE' => \%basic_constraints, %dS, keyCertSign => \%key_usage ); my @disable; for( [ client => { %dS, %kE, clientAuth => \%ext_key_usage, client => \%cert_type } ], [ server => { %dS, %kE, serverAuth => \%ext_key_usage, server => \%cert_type } ], [ email => { %dS, %kE, emailProtection => \%ext_key_usage, email => \%cert_type } ], [ objsign => { %dS, %kE, codeSigning => \%ext_key_usage, objsign => \%cert_type } ], [ CA => { %CA }], [ sslCA => { %CA, sslCA => \%cert_type }], [ emailCA => { %CA, emailCA => \%cert_type }], [ objCA => { %CA, objCA => \%cert_type }], [ emailProtection => { %dS, %kE, emailProtection => \%ext_key_usage, email => \%cert_type } ], [ codeSigning => { %dS, %kE, codeSigning => \%ext_key_usage, objsign => \%cert_type } ], [ timeStamping => { timeStamping => \%ext_key_usage } ], [ digitalSignature => { digitalSignature => \%key_usage } ], [ nonRepudiation => { nonRepudiation => \%key_usage } ], [ keyEncipherment => { keyEncipherment => \%key_usage } ], [ dataEncipherment => { dataEncipherment => \%key_usage } ], [ keyAgreement => { keyAgreement => \%key_usage } ], [ keyCertSign => { keyCertSign => \%key_usage } ], [ cRLSign => { cRLSign => \%key_usage } ], [ encipherOnly => { encipherOnly => \%key_usage } ], [ decipherOnly => { decipherOnly => \%key_usage } ], [ clientAuth => { clientAuth => \%ext_key_usage } ], [ serverAuth => { serverAuth => \%ext_key_usage } ], ) { exists $purpose{lc($_->[0])} or next; if (delete $purpose{lc($_->[0])}) { while (my($k,$h) = each %{$_->[1]}) { $h->{$k} = 1; } } else { push @disable, $_->[1]; } } die "unknown purpose ".join(",",keys %purpose) if %purpose; for(@disable) { while (my($k,$h) = each %$_) { delete $h->{$k}; } } if (%basic_constraints) { push @ext,&Net::SSLeay::NID_basic_constraints, => join(",",'critical', sort keys %basic_constraints); } else { push @ext, &Net::SSLeay::NID_basic_constraints => 'critical,CA:FALSE'; } push @ext,&Net::SSLeay::NID_key_usage => join(",",'critical', sort keys %key_usage) if %key_usage; push @ext,&Net::SSLeay::NID_netscape_cert_type => join(",",sort keys %cert_type) if %cert_type; push @ext,&Net::SSLeay::NID_ext_key_usage => join(",",sort keys %ext_key_usage) if %ext_key_usage; Net::SSLeay::P_X509_add_extensions($cert, $issuer_cert, @ext); my %have_ext; for(my $i=0;$i<@ext;$i+=2) { $have_ext{ $ext[$i] }++ } for my $ext (@{ $args{ext} || [] }) { my $nid = $ext->{nid} || $ext->{sn} && Net::SSLeay::OBJ_sn2nid($ext->{sn}) || croak "cannot determine NID of extension"; $have_ext{$nid} and next; my $val = $ext->{data}; if ($nid == 177) { # authorityInfoAccess: # OpenSSL i2v does not output the same way as expected by i2v :( for (split(/\n/,$val)) { s{ - }{;}; # "OCSP - URI:..." -> "OCSP;URI:..." $_ = "critical,$_" if $ext->{critical}; Net::SSLeay::P_X509_add_extensions($cert,$issuer_cert,$nid,$_); } } else { $val = "critical,$val" if $ext->{critical}; Net::SSLeay::P_X509_add_extensions($cert, $issuer_cert, $nid, $val); } } Net::SSLeay::X509_set_issuer_name($cert, Net::SSLeay::X509_get_subject_name($issuer_cert)); Net::SSLeay::X509_sign($cert,$issuer_key,_digest($digest_name)); return ($cert,$key); } if ( defined &Net::SSLeay::ASN1_TIME_timet ) { *_asn1t2t = \&Net::SSLeay::ASN1_TIME_timet } else { require Time::Local; my %mon2i = qw( Jan 0 Feb 1 Mar 2 Apr 3 May 4 Jun 5 Jul 6 Aug 7 Sep 8 Oct 9 Nov 10 Dec 11 ); *_asn1t2t = sub { my $t = Net::SSLeay::P_ASN1_TIME_put2string( shift ); my ($mon,$d,$h,$m,$s,$y,$tz) = split(/[\s:]+/,$t); defined( $mon = $mon2i{$mon} ) or die "invalid month in $t"; $tz ||= $y =~s{^(\d+)([A-Z]\S*)}{$1} && $2; if ( ! $tz ) { return Time::Local::timelocal($s,$m,$h,$d,$mon,$y) } elsif ( $tz eq 'GMT' ) { return Time::Local::timegm($s,$m,$h,$d,$mon,$y) } else { die "unexpected TZ $tz from ASN1_TIME_print"; } } } { my %digest; sub _digest { my $digest_name = shift; return $digest{$digest_name} ||= do { Net::SSLeay::SSLeay_add_ssl_algorithms(); Net::SSLeay::EVP_get_digestbyname($digest_name) or die "Digest algorithm $digest_name is not available"; }; } } 1; __END__ =head1 NAME IO::Socket::SSL::Utils -- loading, storing, creating certificates and keys =head1 SYNOPSIS use IO::Socket::SSL::Utils; my $cert = PEM_file2cert('cert.pem'); # load certificate from file my $string = PEM_cert2string($cert); # convert certificate to PEM string CERT_free($cert); # free memory within OpenSSL my $key = KEY_create_rsa(2048); # create new 2048-bit RSA key PEM_string2file($key,"key.pem"); # and write it to file KEY_free($key); # free memory within OpenSSL =head1 DESCRIPTION This module provides various utility functions to work with certificates and private keys, shielding some of the complexity of the underlying Net::SSLeay and OpenSSL. =head1 FUNCTIONS =over 4 =item * Functions converting between string or file and certificates and keys. They croak if the operation cannot be completed. =over 8 =item PEM_file2cert(file) -> cert =item PEM_cert2file(cert,file) =item PEM_string2cert(string) -> cert =item PEM_cert2string(cert) -> string =item PEM_file2key(file) -> key =item PEM_key2file(key,file) =item PEM_string2key(string) -> key =item PEM_key2string(key) -> string =back =item * Functions for cleaning up. Each loaded or created cert and key must be freed to not leak memory. =over 8 =item CERT_free(cert) =item KEY_free(key) =back =item * KEY_create_rsa(bits) -> key Creates an RSA key pair, bits defaults to 2048. =item * KEY_create_ec(curve) -> key Creates an EC key, curve defaults to C<prime256v1>. =item * CERT_asHash(cert,[digest_algo]) -> hash Extracts the information from the certificate into a hash and uses the given digest_algo (default: SHA-256) to determine digest of pubkey and cert. The resulting hash contains: =over 8 =item subject Hash with the parts of the subject, e.g. commonName, countryName, organizationName, stateOrProvinceName, localityName. =item subjectAltNames Array with list of alternative names. Each entry in the list is of C<[type,value]>, where C<type> can be OTHERNAME, EMAIL, DNS, X400, DIRNAME, EDIPARTY, URI, IP or RID. =item issuer Hash with the parts of the issuer, e.g. commonName, countryName, organizationName, stateOrProvinceName, localityName. =item not_before, not_after The time frame, where the certificate is valid, as time_t, e.g. can be converted with localtime or similar functions. =item serial The serial number =item crl_uri List of URIs for CRL distribution. =item ocsp_uri List of URIs for revocation checking using OCSP. =item keyusage List of keyUsage information in the certificate. =item extkeyusage List of extended key usage information from the certificate. Each entry in this list consists of a hash with oid, nid, ln and sn. =item pubkey_digest_xxx Binary digest of the pubkey using the given digest algorithm, e.g. pubkey_digest_sha256 if (the default) SHA-256 was used. =item x509_digest_xxx Binary digest of the X.509 certificate using the given digest algorithm, e.g. x509_digest_sha256 if (the default) SHA-256 was used. =item fingerprint_xxx Fingerprint of the certificate using the given digest algorithm, e.g. fingerprint_sha256 if (the default) SHA-256 was used. Contrary to digest_* this is an ASCII string with a list if hexadecimal numbers, e.g. "73:59:75:5C:6D...". =item signature_alg Algorithm used to sign certificate, e.g. C<sha256WithRSAEncryption>. =item ext List of extensions. Each entry in the list is a hash with oid, nid, sn, critical flag (boolean) and data (string representation given by X509V3_EXT_print). =item version Certificate version, usually 2 (x509v3) =back =item * CERT_create(hash) -> (cert,key) Creates a certificate based on the given hash. If the issuer is not specified the certificate will be self-signed. The following keys can be given: =over 8 =item subject Hash with the parts of the subject, e.g. commonName, countryName, ... as described in C<CERT_asHash>. Default points to IO::Socket::SSL. =item not_before A time_t value when the certificate starts to be valid. Defaults to current time. =item not_after A time_t value when the certificate ends to be valid. Defaults to current time plus one 365 days. =item serial The serial number. If not given a random number will be used. =item version The version of the certificate, default 2 (x509v3). =item CA true|false If true declare certificate as CA, defaults to false. =item purpose string|array|hash Set the purpose of the certificate. The different purposes can be given as a string separated by non-word character, as array or hash. With string or array each purpose can be prefixed with '+' (enable) or '-' (disable) and same can be done with the value when given as a hash. By default enabling the purpose is assumed. If the CA option is given and true the defaults "ca,sslca,emailca,objca" are assumed, but can be overridden with explicit purpose. If the CA option is given and false the defaults "server,client" are assumed. If no CA option and no purpose is given it defaults to "server,client". Purpose affects basicConstraints, keyUsage, extKeyUsage and netscapeCertType. The following purposes are defined (case is not important): client server email objsign CA sslCA emailCA objCA emailProtection codeSigning timeStamping digitalSignature nonRepudiation keyEncipherment dataEncipherment keyAgreement keyCertSign cRLSign encipherOnly decipherOnly Examples: # root-CA for SSL certificates purpose => 'sslCA' # or CA => 1 # server certificate and CA (typically self-signed) purpose => 'sslCA,server' # client certificate purpose => 'client', =item ext [{ sn => .., data => ... }, ... ] List of extensions. The type of the extension can be specified as name with C<sn> or as NID with C<nid> and the data with C<data>. These data must be in the same syntax as expected within openssl.cnf, e.g. something like C<OCSP;URI=http://...>. Additionally the critical flag can be set with C<critical => 1>. =item key key use given key as key for certificate, otherwise a new one will be generated and returned =item issuer_cert cert set issuer for new certificate =item issuer_key key sign new certificate with given key =item issuer [ cert, key ] Instead of giving issuer_key and issuer_cert as separate arguments they can be given both together. =item digest algorithm specify the algorithm used to sign the certificate, default SHA-256. =back =back =head1 AUTHOR Steffen Ullrich