Cryptographic operations sample

This full sample code demonstrates how to monitor reader events, populate a list from the certificates found in smart cards, verify a PIN, and perform signature, verification, encryption and decryption operations. Note that, for the operations made on the public key (verification and encryption), the public key is extracted from the certificate data and the cryptographic operation is computed in pure JavaScript, using the BSD-licensed Forge library.

<!doctype html>
<html>

	<head>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>Titre de la page</title>
		<script src="libs/forge.min.js"></script>
		<script src="libs/promise-polyfill.js"></script>
		<script src="../src/scwsapi.js"></script>
		<script>

var privateKeys = {};

window.webappcert = "3WKVAuegUKJv}6.5vrlH2yJKIDd@n{fvTh{3B8#hpvR^OCB8LVjBz>@azy^qVx>Ir{zEWZjAc9TxCYkY0fF$QugbyTod}ryfwO#PYwi8y}xMOuniXJcvfFkare?]7kf/7NCf!$KwenfcEA5]T9f!+jnh7*Qre?]mwiX-xCg+rrZBz%n]fekjve?![ie?]ywgby:rxMOuniXJcviwJiwhAirzhzu:rf/IcFgby=8zl.77]a3uEmc@vMWs#=I-EbPF*EX14Y?t3CHGEgPU>q$!DZ*37zk-VBWplV.EQkEqWNo=%UyaK)6DHS2BM:6Ahf^Fp[Qj0W^a/1P]pG5#4=:P8lt@W&5L:itH<kVH5NKs*i&qbr@lBo[pPj0fvW0*]}NEm^NI?QxI.&!XJ)AKCE6E&$vfabiIlIn5am4t$r?GOIl[JoAU#.{1kF89-vG4}O=9J]egOW7Jw:>oq@fVKpmJaExQ2uU:Ah:)C=*9iC)5}.MyC?V<0KBec::^n?q%nnwb9i-XijWRH?sS:D>QIkWLJUDLYpT&5(VfJ6tc&*+H*U4hhuFHUQX4PS5<2YND^tFT9#R(=eA]>OM^9b-wPJ(YAg}-av%&ir*K/.h|MIIE5jCCAs6gAwIBAgIBADANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQGEwJGUjEdMBsGA1UEAwwUSWRvcHRlIFdlYkFwcENlcnQgQ0ExDzANBgNVBAoMBklkb3B0ZTAgFw0yNDA3MTUwOTU4MjFaGA8yMDUyMTIzMTIyNTkwMFowMTESMBAGA1UEAxMJTWF4aW1lRGV2MQ8wDQYDVQQKEwZJZG9wdGUxCjAIBgNVBAsTATAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDbbHFzPMOgflE7NLWtYXK0MV8xCLbSZ7qvUug0YdYSwiw9XDXe5uODG2ok9C1YrlSVuQ0O73CC07RqSys2XFsx6hW82JuwN+gEqJ+v+BKW3YQgT7NOzIRrvhucSo4UWrAZV6YXhd/1GZyYyyI1x3K1e1y/1p3Zw8nKAGeIP8vSQvSA7da124DOx860AXvF3eRUyonMEZagnPyueVVy0wodO1u1WRMjVN+f91FUhtbRf+u5am0vU+IZCE5Lv/ALghr0r3QqLAvHMlhe4uw9BmPCWQH7d/fdIbcZf7eDxWizei2dfl/dMh95f2RTGzE/Hnt56IVYvOT4kR5AU95gwCHAgMBAAGjgfowgfcwCQYDVR0TBAIwADAXBgNVHSUEEDAOBgwrBgEEAYHYJAECAgEwXgYDVR0gBFcwVTAPBg0rBgEEAYHYJAECAQEBMA8GDSsGAQQBgdgkAQIBAQMwDwYNKwYBBAGB2CQBAgEBBDAPBg0rBgEEAYHYJAECAQEGMA8GDSsGAQQBgdgkAQIBAQcwcQYDVR0RBGowaIYYaHR0cDovLzEwLjI1LjExLjEzMjoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTY1OjEyMzSGFWh0dHA6Ly8xMjcuMC4wLjE6MTIzNIYbaHR0cDovLzE5Mi4xNjguMTYwLjExNzoxMjM0MA0GCSqGSIb3DQEBCwUAA4ICAQA5hyRPrhPV8OhYfOxa5+mQoBi0n/m42nWotehg5QJmzUzDTDnZLgEV7t/9dVsNX8uh1t88Kgkbb98vAmgWHpslH++5P1jp9WQfNG9gzASpSglcTXF2BHblKH7oDJ5LMhXtGikUR13TuoK4iPvZxSweIeK5JZ7kMydOgFbj5xlk+rLSqqP+tebS6bOMSzUWKGwgaBQbS3sZ8B6IOhROGxbXxVQZ2vJ94meqmTG/qSoJa3vhf9kOxJLSdxCNdeuLlkIDHEqnHMMQHfnZztJ3dVt/rybr99Qx0IvW248jgRMoD39EeFYVya1sNB2LkAAeqnoVeUlotCW9F8JA3gVo/BFzoy46/vUDrrFUMvGQXWkdHga7PYwf8NHFjLO+o9R2ehATCHNykfmgbE6u+RuKJx3vqoM3MGaVa/OI1P9Jrha8/wGt5hkIMuEW0/n5w5UBs2NuUbMia+9abUy2EQxWJ2Nd4fJfmpvcsf/I1I9J0RuuBKNRcElnidBlK8tkGCw+gMv8T4H0njRoSSmo6dku8PEl71sLiorGdJddA9VRKorj2md3SW6IbeszPZM2/b/EVM4p0HPR6OV/PJruLcSqte8KaBwfsu9/s0e6FrNfhpkqnk+8asXPWSteCi3w3lo0578XQsHuIDHG8P6Dqhv4AtC+GUrhXdaFGWSZcmO9CyqU0w==";
//window.webappcert = "4gM?:G)lz^:r^BM:=BP.0{6YMM2.Vg{fOpeIjyU4cFud+E@>=ZFNFL]oK$eUW]/F?^z1gFmHj(KdQ15QEa(Vo/j*t3+b3Qnz/744vSt6UNj(Wt^R/ba5m#I/{U@)]HzXNcH:Nf}nR=)ansje.px01U1I8zGTx>O7zT}QieR7gwYPjITMoxtu799.8!LEGF?MhB$o&Ygz{<5dHkifW(9FJ6icJvxKbm<+xG1Qa%p>-J@6Q%czJ-/h@.b(XWkFA/1&u(7&=LvUB%tliH/kd3<5rcfryG]]+WZ&<^&[I[{l<xucJUM8fQHb]:cEeB*xbMzn+";
// http://127.0.0.1:1234,http://10.25.11.150:1234
window.password = "yyoussef";

  window.p12 = "30820A5E02010330820A2406092A864886F70D010701A0820A1504820A1130820A0D3082049F06092A864886F70D010706A08204903082048C0201003082048506092A864886F70D010701301C060A2A864886F70D010C0106300E0408EB8314818F8957E202020800808204584FD52765C18CF4EB7EE6F030C0D07EFECF901115D923DA104E80457E39AB58ED6D075F59EB02165DF36D6E674B80F313777F4675DA0F7E767F441D0B327AE13D347C54019BF3EEDFEDF574D2FFCDBFF09ABF66C70834E67053C57722DBEB9D37CC4E1A0624197BA8453BC7269AF210F27DFB290B16FD375DDDD36140506811412BFBAD8D3C70D1A8D3CC56BE973870B0E5D8A656E05777155A424E2E6147C66DD56EC2B844ACC8C1CBDAAEFF49FB991EBB4114422893D181ED52A1BE59B499AF0E7A2F8DE49C3C1DC4F96B4C93E342F8232D9A51949D6E2FB4B1F63D0D19196771E43B8CB0B3D849CF32DEEB62521D71318C3E35275FE00B269FE0F61768C7100D7C42DFC5E2E3CF64E56CA223CC2979EB3F9AA6B18216778F30368F06601790C7E162C8888C5FE0E29DB0DBCBE647487515A578462360DCC084C005B482EE6AF26A0B16AE61E293626D9000B370B682C3DB3514A8060600FD5AB3BE9A46C507CF69F972BA55CFF3AF6B11AFB120B7B1D917A8FB724AB3A15E799BCAF3A8DE493C141D20E7598645C3F8DA624F71C1EFC9CBE38C25666F469DA55DE4E397E76328EA2A1EA66F7C04877688C740CB68FEA034BE852300B40528E3161B7059FA2B2744846E95FED4D11474D769276FBAF61798D5B9468C48E21B5908076A80687ACB2A794F5180A2C4EE3B08E7A4C68BC58E2CC4A701B8FF0686385552A568B727998E0DFCD656DD8D5DA147A88AF0F72A3C92A08E3D03AEE504BCFA514D047A496C3E23211DFB54C2802C060202E9D10B06A929280061728D89612CE88563129669630B188D28193CE351B2544EC6155BD9EBA706804325C37972DEAFD99716E16AEA8EFE8B37DAA419E38C09515EF01C773B0BCD890CA3646FAF7B208B16033ADFBE91DCB7903EDA3C4A91D2DD210EBA5D434091F7189176A83730892CC70D35D3DF14EF0C6E7D8C45D0FFC6A409D9BD5A37A3F6EC8EAC0E56C30EAA2D276FBBB3382430908F8AEE21C9BC190DA25A7B5CDA3877C0FD7E14DCF2EF52991F87A3C805AE989EFAD89EFB76C17ED76E8416CC0089887794EDC2757D93EE81C70F7A79F705128CB487598F0C59CA1017A4F38F1270A2BB219BC26DB91DBE566720BF81D591FEC6F8166FAFBD6C8A3EF85D70AAE768908EE00A3C3053AD9C5FF57F09E70D14D33B934C946399FEBBB35096208B396B1D5526C038E704F670F804B095BC41D6EE6AB189A2C559170ED016E9FFA4BA2E153FA8635B17CE079342672CBDD7960B47527C2D3AB01916A82F86DF0CDBE8B0F123A5FE85812037450F9A5180D102B147EE6FD3A814D1872E060AA7D197FB0E07E874F92D34EE21416C0EA759C366FBBDE9E32209F72AEEBD7630C4FBA02C5F5DC7D908085C1C9B71DF34F072C95BAF2052808C7ADCC55162DE757AD53DE29EDE16EF009C3B836FC60251A5D49A075D86F86A2164F38CFE983FE571B6541B7B8B6A5C4CC09E64F4B19A829F3D67B9838779D1DF813A248ED21011610137C89272881E0D2F048B98DADF823F33DCB5EDBC5994EFBDB628341EFB1B2B699D8CEB77FDEE94773082056606092A864886F70D010701A0820557048205533082054F3082054B060B2A864886F70D010C0A0102A08204EE308204EA301C060A2A864886F70D010C0103300E0408FF988A81108A8C1602020800048204C88549F8E681336AF34A8822F0921A55FA2A718DBCB5D2B5A0733EEC24A757218DA1DA64BC73810C0217F58C6AEB8A2D5C5FB75BB0629153155A60C339734E00665D88229F96DD084D28F98BCA63E9B9353FA8F48751D164A12296DFCB38435A914EF90D1AC95E482636320FC71E8FAFD60227E8C07BB2B56D0444CF1548A87E80E315D644BE7462781E1E909C5D08D9730B4F26D2AAE68B7F8F0BFEB0CEAC08A0894B751AD52F0BCAE13FCAF2E3EF38F1C0931BADF3502D0BB0B8D803F8BC11134724B3A7320ED43BBEEF2800DFE699B677A188E12CFDD020FFFBA5751C16E779D03F99847CB3635AE9BF55BB50BBC81608D5B3E55E5C3F8686335604B24B861CCED7E02723052692A864897AB07342D6699A3938535F46833276B441B20B03BA546D6682C70A000DBE9AC208B23462BC91956BEBA7BB8A6A8F7737445B77E9D797237D9AFF31445ABD7F7A7DDE013E1EF23200137A826C3328341AFB362EBE18E0F2A2CB5DA294AD75A671E410D46BB3863501038A93CD4C254ADE0EC911D9E0EEFFFD16072BC4E1D2F881A7E1B449C18AEDC339BE1054EEC874F47B21CB2D0E27A6B9D00517468DAF209CD85A2A15F1E020FE3464D133CECF173F8075A3407BF3CCB5C5BAE26756338893A406A3EA4F09993D62C1A8495F74FAEF373B1CBEF5341324C7098E28F9FDF9824A357AEF67367C9676F228342D1DC9EC63AB9B73246BD0A770D4A64590043D055C41B718EE443E612AF2DCE5B0223AF5E9B6D9AD2BD1DCA2DBC6FE63BD6BAD2EBCC9E6F6605C8EFAF92CD92D08F1D0CD960EA517618C00B90E72879CB950BB0739091DB180308C66BA360899D457558877776A8CA375615CEAFB7DB8A7FDAC94AB52544FC87535129D42015F8E379B24F44D3D1C8629C0FDBED8A5F06EAFBDF386F60D0DA566EABE8B7F4B39C1A8E54ED285452EDA410E7719548A45A6024A221C698B306A55047F6837B0D2E35669F4A8705256757276F7BBFF8DC5CEFFDEB8E05BF019581D18FC788C0D1BB1418A978C654D7950E8ADB9D394F0360A54824862D809E3ADC5EAF7C7602F65A40758EAFCDFC6EF7824E8DC6DE40BBB4269F8E3578FF7157FA5F1BD19B0205BB50ADA7CCCF2D3AE631F5EF25F5E86B4BDB25F9A068613A1CAA76B3788F2A35BC5CD8D072105D7168EC533ABA1859CEA2904CAAA02C2719B7F1832E0AA52819492E3464BCA2260DB34650E1B2421F0D1C885CA9981A6D1BA5BE87E85CF02F6102485B7D672019931A7A7E58B33C81E0E8D139ACF0957765BCC90B147A77E3AF3E680A7DF6006FF3DD5EBCBE1C6F762A661594DDD8F012C1F8B907DF6511E41A98AABE1D93437B2C1DF09387C09B97840D1B6FCCE60096D942485BEBB2D525AD96108636AA18732B24DA59C6271690ABAD068305B8DA33974171B70F359199ACE68C140273DD7C5B98D54258D7232DC18DAE6389564A1C9E8515E57BDF05531697C8A5547AF868B24F67832A96EAA71395739651B727533EAAB53794AEAFE7EF0E89E4C8FF1C9DB3CBA3F68881B8F0B7857F2A226BFD8403ABD03ACF2FCFC18D545E47A84E846645B0B0F608D251A890B6ECCD6E8634FDF7F01E2C1145841D2A089C63B588DC6900E60DD359382AF53FD302579DD9733885D76BAF339565B52A28BEF147E4D2BEFA3668A6B99B7FC034FFA07BFAC6844CF1AD743F875B6CD9E47F6612A9AF7B0C051C568B82F41BD7130CD314A302306092A864886F70D01091431161E140079006F00750073007300650066005F00760033302306092A864886F70D01091531160414E7D7343D5B87A9E7F4B6D87353270FD8B3A89B0630313021300906052B0E03021A05000414EB2034DB2BE189DBF87422063FB6EBF5C46C4057040886C2012A3342832A02020800";
  window.certificate = "-----BEGIN CERTIFICATE-----\n MIIDNDCCAhwCCQDWfPjiGAVE3DANBgkqhkiG9w0BAQUFADBAMQswCQYDVQQGEwJGcjEPMA0GA1UECAwGVmllbm5lMQ8wDQYDVQQHDAZWaWVubmUxDzANBgNVBAoMBklkb3B0ZTAeFw0xODA1MzExMDIxNTNaFw0yMjA1MzExMDIxNTNaMHgxCzAJBgNVBAYTAkZyMQ8wDQYDVQQIDAZWaWVubmUxDzANBgNVBAcMBlZpZW5uZTEPMA0GA1UECgwGSWRvcHRlMQ4wDAYDVQQDDAVaSVRBTjEmMCQGCSqGSIb3DQEJARYXeW91c3NlZi56aXRhbkBpZG9wdGUuZnIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqPMenM1biCZbzAiaOt9rIoc7sHVEXGwex1yehBVfZi+P3B4bMY3h1i6k7YwfM2wdRRn/7vDbEwiT4tPc7MHnkWoFmrpIEVcSQzgNwBVY8yPNDxtVu3wtemCcnk7MT6c4F68cCnSsE9b2Jl2YzHOB3Ls+LSYRCscmCQTzLfIq3woW+PSgRBNiNDUZWiY1QyQWARqmyND0eD9hMeeEa/pvofZgEgiZPuCJT8QJSSOIp04Wok8wofZULc0GRImlV2QJiwogcJoExCpiVSBY+V0nC3JmITIXYzLZ+ojhTOCEETCnlmjPodQk0tiZM3NdgRA/zTCBCTqVu220/9Ik+LMnXAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBACqfHAhEQr988fVxksK5weg744wwYXoVZBCT9aIjEwjoTkP1EJEoUr8WHBvOJ9yXa6JSqRaaLzPE4E3Og62wIln39DIfAYf4OKo0noJ1Y4pT4b+nz3Hk+t3wIaUO53bnNxGaB8R5jWcMBv/sniPCAJ60/UO5NF6+D1yqCuR8fjL/WXpftSmL0Q1IFQJ/d0Xn+5VNEW4FB1/uIjZEXoAOxnsOOWUpoFKoePO6vvchE9cyL6T+1Ww36gOELT8k7g/WrnldPbc8sg4gW1UsW12kjLP9Pa3jzkRt31WZUISuTg1SSJO/QfshCY2luJNMNtSFkPcQ2tDoRNahA8xsXq8MKJI=\n-----END CERTIFICATE-----";

  // Ron Weasley
  window.p12_example_2k = "308209610201033082092706092A864886F70D010701A08209180482091430820910308203C706092A864886F70D010706A08203B8308203B4020100308203AD06092A864886F70D010701301C060A2A864886F70D010C0106300E040832FA54643F7EA7E6020208008082038039A48F64463F40D27EE7FFD881B2ECF92CB614501029D8D40D275F3BDF59674D52D74EB736616D0F7752D1BF3BE6D3F398949FE6C3BC351D0E7C4D617A0B33251F6C35210240886A0096CFB69D8CF3929734A5360075CC4938FE449F211A4DBB4C2B6B0F741671AAE2217D7911A6DC663E624E3249D50AC638A89282C1ADD25829A80BEDE67D35304875815EB87EE9062DF8E16B34DD25679D5B5531F3EBD9274404796DC7EB3414C64B8E89DA81A90407FFC17A2768C74981EE886AE72179A7D4B84B205202DA28419FAFB8943A69EC0F9838DF3C2E6B35ECD5B0D9E79DAD794F5AA2B0C92594244DD735C2F1149CBE7C83AF75444CEE91D1C7613747B93E0FF5C65C4E044E40A1FC2394558CEC13272DCA42B585FA53E29F8B3476454EEBC298A462BB4481591536DD2C68FD921BED1642659D50BE9AFB54BC71ED99CD5C0230719FDF0DDE6A9755A45EB604FDDC1DF6F911C5E95E86AEDA3ABB48B48ACFA2A613E8B4948F97074B7779EF7415274084A4561EB00B3A35CA08CDD8BC9651639BBB52F42EE7E10FCA74499E9A8996F9547371BF68A7001953D32D5EB64213951422C66D824C93D2169951031357633C84DF77B7F47588B7DF0A5425DBA1A54436EA0BE1EC5F9FDD565998D8B8CF9E435C1ECD103FAD1DC800388D40E2F43799A2373DB6B758727B612FB69D5E4CFA3F3BAFA773F8254F5EBFC012E19510F28AAAD2E357EA089651621DB302C8546931AE5218463FA64E402613D9DF45A808C079CC116357517251BA31DBFFBAED3EA647766E39D1E9371058E4CDE233FF3A54C4C244AEDB211495FFA4FEC4612D1C0F1E0332057623B54D3CC1D37AC636859E9E7A64DA081C064BBF4116785389C4E796760DDDD39D6C4C933DA32BCC6040C7BC6370F020F2CAF411B41043DD207041B179CC3BBA130B899B4C47BADB4D507937205A2BEBE3614ECBF1CFCB662B53122320A24E712CE4BED61BBBF67E690D998791FD07B9D12BBC84F088F6A4E0CFC7FBCA844153B345B138B7550967CA9E36EC7F2F47622F494EA45AD6B9B34C2592B1D74DE4295613B35F08DB4FE7CE4DF0FC50BD7B0D239345A38850B131F5A3F763E03BF246D1F7456966208CD71908F946361AE09585FF021D6BF17D4510CAD7DC297CB4046D3D39DFB69AA0428CA11EEDEB00DB40FA2C324F9A26BC65FF61FE3145388856E11D4F2C289ADB3E5CE4CDAF9E60BE46E1E7DE17FA5FBACBC0FA9FF19B0523D21E179FA85B8FA4E80C39413082054106092A864886F70D010701A08205320482052E3082052A30820526060B2A864886F70D010C0A0102A08204EE308204EA301C060A2A864886F70D010C0103300E0408619A9042B629B4EF02020800048204C8422B9AED2B9F84DA847E9E399DA052D809355516524B8B781B8E9E9E147962F4D0118051F5D700D60D7AB3667129426D20D84A843C165E67177BC9A7FF44B6B3F6AC27B521571370F5D332066E8D92858BBD2E86F97558CFBE42BB7AC822CF02953100F343E6E99FA4142034F3EF28C58B1A8CE5C6DAED3C4DAFDB0E317A6E22047F361A0B5371CD3FC8E0D8199DF8AA49E09918E8318171D5E0F2CC988868213CDD52BCC66C77FA1872D6439A70CC6964173B876FE1A5D1FA33DAC300C1E83554950D95BD4313B7C2411AA6AEA46884CBFB55880DD5C874F8EB3C1C99DB2DA17CF8CBFCEAEEFC586262C575E02E5536659CC9048876D12143407FE375313DFC41EEA825C81F9446D28901FAD50B085D17FF2F43765E7353D51D4C7B555EBD7B5C6380F57B83594659E3C65E65796AD46C6DDA678D6F42C8D11828736B009E9152B10F722D53DEF7F9B924580B4F6226C6F99A9C9198CB537B23E1CB575F6035E3D51EECEA8890F48EAE4C44882B04D2C9848EF718D8821339C1CFBD3E3BFFDECD8E5BE895E1C1BD29CCD8EC1111DAE1B7A8A28BC9736118D5056F7F40FCF215270A2B55BB9D107A88835D912575DBB1BD59D4C9365A4ACE242199F7225E3FF9722A345804D29FB751FA15DF2EE23497F60FA6E97AA5375528F89D1A3CF6D364FCDB46037BEFB640D6A83FA36C09DB4CB8074154AA74BB595E145E6C1738E69CDFCFC2FE76805E0AB8F2A1454D9285AD9D9FAD5BA190AF5013EE374248E580485206C0E5E71D112B03E1955788B4D34FDD09BB752C759DF75F880DDEBF9336B72C71D304C22256F464303D10DE4E86BD2E8BDD07CD453560E2E4F32BBF837C8ADBA974576D5AFE4C32CAF5D6479421FB92E0A012BAF49B7B9DB8C8DE68F15E2E823B760B6E63F2C03AAB5D3DD028A11AB36F02AEB7F9409284D9458D89C095FE7C9918C2E08578B2F21E2C0C02F22D3B4AAF90BC075045FD77AA1992EC4FEA521955CB50A46EBB88BDD2F24A348CE6D69A3EE635D59B396B4F2A4DFF3B816A587E12313B08B8DCB093307E0D4459990D722EE7D5E6E1CFC5C4BC4158C63ABC40F7DD9EA75C0DB767035E66695E8E0D787240DD8C06F017FE442AC08749863C5C6773BEAB890CB80B6433EEDF27331AC27615D4DE9AE5E47B0C47DB7BDE639055DEF5F299F9EA4CBE2581D19EFA8A7773FDE8BBA15925DB35B30ED0D7E6E4BDF00E06FB131D3B13D88278CAFD9638BAF049E0A443215E673DA1A5B77888BF61BAA6E1CCA4039444596F22058D32726418E55203CFE993926B6857DC13A88E0F99C2195C9D0BB16A33911192E4D68276F15A514D68011F4BA38ED4A2454CFF493232129BE9D89039801217BDB4CC34C282ADC9EE6745AD1E2250261E63260140D65FCB2B11BF55CF3D1E9ED76EBCC76BF1A6A9591B754C3A877A9CF569866B114E7142F5CC4B7C902D25C70F0FDA7057761E653D8F9E7F248E90F9CF94B2039C7ACADDC906B46FF3CBB54CDFA2A92C63CDE2A72651AB92E20F44D572945D019531EA12EB1F7860BC3A40538778B89E0074E0F72E8EB28D93B62616A59C00CEC952ACC17F1044B94ADCF5D7A060E685E51495ACDDFA2D376B9F123BBDC09D995D6A018676EEC4B0BB583FECB6BD601E045DE8BA13A49DCA07EB9EFC2004C94F9BA702EC69E5E8C9F0E5499332F7A975289E7F18FE7A400CE71066A21997792F4A3C11552FB9C721ECD33125302306092A864886F70D01091531160414D86B3570EE8F5A9A19981B37EFC5F464AA66ED2E30313021300906052B0E03021A05000414A98DFD66C04366C2A5FF362784885E91D348B4F704086B40573B8A4A5B7302020800";
  // Minerva McGonagall
  window.p12_example_3k = "30820C2902010330820BEF06092A864886F70D010701A0820BE004820BDC30820BD83082044F06092A864886F70D010706A08204403082043C0201003082043506092A864886F70D010701301C060A2A864886F70D010C0106300E04085700CB60B38906300202080080820408F1FE6AD2A0D6774AF27897B39DEAEF03C2801FF8DBAD850F0BEB0EDC86BC57C5571B362777A6C45AD6010A54C04562BB41280BD946A4BC9E08B15BD2AE49466F80BABE31FC149478A5E0D05033BF8C6EDA6D34E978C7C66A23F29A2E73E83524A67633F7BEBA418A4AC236234C089590026D1F28795270CC04B709705DB789BBDCCFA2104A4D3C98AD219C18E33D1AE6D4040C11E7AB726A3F394EE54C75DF00B2C7E44A535D3E97779A2233CF23D03CE1E9140849ABB44F734BB2770897D93D310D8AB75510932106922E333CB7098BECA39C88C4D08C2D0E95812CB1E0B1AB3633D3A28B4095A27949306640D1C0245D6996B0F927CA546B9E3B98970358F050CFFBC213D3E485CBB1D3C898AEEA3260DD156A79172A5C4CFBD4ED7E1849C2B2E347EEA44CF43798BA3F9D973ED092518FA86FA6A12722880C1DED50A262214CE2814192C5F133C5D65504462CEB04F90624A0DB236BE5218697E201951CA8BF8FCB39C5771CF2F00DD5135BBE72B44858C1898A08D6305DB272040E3E10155780F7A34536D64EA28D8530689B7EDDDFABC864BF93986381F04E5F34A8D3EA04BA68113AB25219F6B125894BC8B088009825351F3B0831F95333CE210D002552466BCEF8BA161AF1DDC527CA5F707E60FC0417411CBB4BA5C5E7FE7B5ADE94A8A48E0707FF719CB8B6E4BD1B5831A37F310D6F3D96A681C6229DCC197566B8BBFD4F3D79E792EC9EA044706232792C71091C3041DDA0A6BA462DE50AEE2754B500DE1133CEBA1A9E65F4D8CF0F805BF00C4856F66F37289CE2F4DA61BBA43C62DD33A59B4F1F4ED5E625BB76AFCDBC1345DEBFAE0985AEB755CEB8FA926DB6CBD36CC06A32C599FB9AF2C311D8B62352F0B7982B72A557F92E76065FFCC8B86A032F699117A43506DCE283CED549062AAD0E11C3619F2F784CEEABA89454905E884AF7910C341F407DEC0A825E4E87FB6A7C86C8795CF67ED3E71A2725FBEDF84D88291BB5B5A60098E48E90096929E9A6F2A4F048BC3E57EEBE9A3FD5D8F2292E70B1B7EF69C067162156CCB9F7A33F06A4778909AFCBD43F72C1961B5F2AF4143935E5D2B725FAE817DF83A9A8A53125549F2D33D5916EB0DE009DBBB2319BC67B54DF0424C9074C2992F5514871FFB95E3904DA8F8E8D2D337FF2D9AAC05C091043EE53FFD60DA34456823DC94C0B6670D36924926567B756ACB2A5B7D18019DB0B1DC4B9C6BB074D0B6548EBA812F315963B0598A23AA99CE33FDF585BAC11C1A511F9AA06B3FCCC315F0BFB15C2009275DB6FCC45666FA74C9D474E7C7FB894CFAFBE9E7BED91BAB73C2AF4B5DB461E519DCE1E917FF332F39F7B767B2B9EAF5A84650B9A4C1A3A71336F81534CA6D95362A69C47C2948BA922035AEC3EBBBE6C15B6EEF5636C32F0FFECCED3A6BF47B16E1A83A7E83DFB98D540CAAD99265CA89C58267A3082078106092A864886F70D010701A08207720482076E3082076A30820766060B2A864886F70D010C0A0102A082072E3082072A301C060A2A864886F70D010C0103300E04086193DA7A9703AC400202080004820708B1F34C521B43D76D4927C2743C2B814F5D1EE37A0170A0D1BC713FBA2B30F7625A4E2FAAD85CEB0C1E98AC8B4845A48DD44F20F4217DE1B815A06530D0E035A10F371BB281EDCEBE637E21352B2B4FEC91FA1BC9174CCAEDBFFD11515AA19915B3CD66EA7DAE2E91F1A746B9E3188D2B52DF1FC818EF4480656716C7A9D0DEA33F06E1D24CEC0034FA7297BF7412CDD74AD416C8F620E02256BC3E35ADD3DC64D88979360CB600571240AFDD230BFE4C3AC27ED12090CEF3F573D9868E6EF2BB5E97C4958005467BE9D6BFE49A9D95F4D7C6376CD8E50CED4D49B0E52CC2D52FABDE255943F73D4A3469C357FEC10907BB2CA35F8287299F50F0ACEB05DBC2126DF42EC8A4F0585C8EE57A0712F50B57FA716483DD106B4F38638878AE420921EA37409F5012055FB0DE874590795E427EDB5B3BEB8DA66AC343FCDA677743991F1DA2463664AEE8007F47FF091852783615636CA022192182370D5F773FDC9E82FC8EBFFFFD8CCC75234A9E20FEEF3D3A2481C9500910E2671142B6960265A3B982C6322DC33C0866FAC5690DCF7AC621AFB0119B16F17AA9AF4D32B208E719B03B29EDD69982EE86A0A6568E11D66861A055ED8BBB0F330648A0F62B089B3B911A4AD16B71A4E5572DD62B6E2EC80BAF85E809C72B1C7AC4E68D6C36BF886DD747406DCD8564468F290705FD3878FA73AA2CFD45AAB60D672BA9E3FFB082B2DD1AE0ED0139C362D9EF413526D8E90BEAC57F991F11A3955A0153DD5301F63F1221B2C1149EED6582CF9338AC6475FE8A4383885531A33914AAD0B593B9429C9E3945D504E000E8511918A60F620488BD41505CB9A55B757CEBB2AC4DC89B316533611993F082E91AA7BDD51CFD7E98E6ADFCA471FDC57CFD7E6B7E9E14C78866990A2351BD8ACA33923B4508B81B545A429DF6E8985019A90D0E776BEF524360AD144F145025C3AD79DEB1B7A946AD85D55BBA95A14BFBF99B1B1199E97D938105C4D277DC5C30E3DAF8E7BEF76F1525E407C86A80AC92EC40F239B5A82D35BF18B963969B45940D8FF6D32221D0F31ACB264A6A2900704201CA13257AF4B644DCCC4A2AD45A65403718D1E079D91053F4361AD81B0EB86707210162ABE4DB63726F28BED67CDE6F5DCEED704D32CA5BCF1B0FC1BF0AAB2E486951D58F3E28C1C5C8D6ABD9E79EF184778423F425DAFC7CC1D8A3E94C00AEC6C8F617D37BB722509684B55C65FC6FE436FF18F7A66D669C99C3FD021AF6F2C411F92D67794FC947FF622F39572173C5B9484D139EDD89241766EBC7D271216E5AAFE43662A59EA8039A433DC81131E3863CECFF701EAD413566922A6D46445E7796943F24FF1176664DB3C7778FEDF6149D8301435F6FE76047E132336C1CB3962D6CB997B45503C489125C5E40E0DEA93770B5741646F9A1A6100B72FB66E51904BE58C224B1CA69528B7B32414673540B100F8A80446CDA0E9FB2A8E11C7A2C79CAE49195B5C0DECEFD42077239791BC30C8A3ECEBBE3644444D9621E601125F73A4A7205F2FCDD04466113922243D93061E554CCB6D7792195845579691003F7161350BF9B02A57D26C4EC9533D7CDB8D72998BEE287F57A7E71FEB95102CB37F759B1F642AA0478D157087D726CFD8EC48EBD3CF85BD5AFA62E7E0D47248D02C73A80473EC5917834F1B472AFBC4CE6ACD37FC4A496ECFC99AE5429A9D63222B018EDB9136BB9192E25D716621262BCF25C94D3A43E38EB7FC788B863BBE3157A302BE1D90FE78130673766020E4DF37E70E76774DE9BD3FF48094CF2F175F619E40B1B4723B701E952D69B445CA17199A60C9D08877D9792F83AA54249B98C3C1765F9589013C282DC57571861B984DB1E1BB4175AB553E76312B1A833C9B749AAD5531309C2B82E0494ADE063F0A3623CB0A1C4948868BB4F464AA8F0A66497124015C38055CCCEEB069128D8AD8DBE9243FE366A1FB06041E83883B34434D3ACEE292A6B52F82EF0474BBFA67CD7E928D9B0BED79E35A4223E7568B45CC7CE056945980E13442A9D2F88C72AB64AD9DA21D4901EE107356F4618034C70796735B0931E766B5C470442D823EF4754DAD7E07B1771EAC4983E83C1D8B54E8E16373CD5C8E20464E9A83E2755B649B64527031CA03DADB04842C1697141FEDFD1C415FAC8EB1420813864435B9B73A2CF8938B119E4CFE31E8E91266BE31BB78C221EAD6CC128D01B5DE3157A678AE65FFE0D946F5B4A82643FF77EA88404F580DF7AE16B53F898925ECAD32D93E6157002F3839C727108CFF616F1530F6E4648FC47DBF260788E63F2D9F57A1AF050205BF0B78EE04381137B6F5BC87469CB9E320725730011D52B39B8E2BC1ADEC92893F0186D85F5D46093F26F489CFF32B9BDEF5F4E10B6EC142725F29EC7993317FDDE0872F2C13160D24921BF193F0DF0BC88CB626A0D10B90E959F2A1B41445FA118B100C7CFA23D8ECEA461CC1D0EE1E9DF06D48A24A2242EBD524898731F5A270EDA4A54772B46C9CDBECCB65EAFF982B1A52444E385A7A0278D3125302306092A864886F70D01091531160414139E53DCFE0A469A87BE5A4E7DF0CA0DD140375A30313021300906052B0E03021A05000414700329ED5B865AD7038CEE0647CE3BAAEEBA06CD04086CA19C703F8B3D6802020800";
  // Lucius Malefoy
  window.p12_example_4k = "30820EE102010330820EA706092A864886F70D010701A0820E9804820E9430820E90308204C706092A864886F70D010706A08204B8308204B4020100308204AD06092A864886F70D010701301C060A2A864886F70D010C0106300E04089C8A610C337D1BD4020208008082048062E3A9C868F924FB4C8EF7E474BDAEBA151165B78FF99A79BF832D37949B12648ACD21AFB00C11F9F5EFDC53C3263EC3F153C0D96EB98FEA350F5207BE237EEDB8AB13CDD0A456E35F12BAD17831776B974A34AA9B7D41166155CEC463EC4CC1CC5159086476D14CF28554BFC070AD03F02A5172470774855E1C173A2B867F36764DB4B85FACAD6D34170A8655CCCA939876078D78473C05ABC878CAC43A6A72DA910526127EAAC1B88154FD77BCEA112DAD0B1C7A469581618AA4FFF7C222890D3F830B6B976ABE51D2A77782EC87D29FB56FC52BC74AA61DD18226AEE8ED960B6A9B0BC878C145E01DB7C502AC9B182F88C613BC8F635506ACA702699BC3681D265EA9CB05EFDA22DA0559712F42931CFE1CFF8FA8F18D5735B4CF3B079D34DA9CB9C6047C780AD6EEDD687299F3E94CAF8FA995EB6261751F8DBE44C0D83E50EC4EF57691F6870AD723B20785FB958F9275C0309164445D66667EEAEEB3A3001F73086ADBE292287ACEE45C01F9CB13EFEBF7FBF747DD59875A2367A32EE610E4F04C4C89AB7BD7F0B2E6528EE7BE6120AFF7587C218A117B0EB7839F08E78E2C8392DD170F54CA8BC9BDC26E86C9288CDCC16E95FD47E58B36164035A7E3D0A50E684142CE757F6CE28A258AB50B50FF26DDF292F617C5A0990DFA790E566353573AA153D54DCAC19CAEB6F09252CABEC4714798F3E4BABF4BCADAF7DA5376356F1DF15D11300C0164C026E35AF2377DDE103F20EF673BDAA84B3E1515DDAB55F481B7CF8B25861123B414FCC1917DF65C3E708C943F4A8AA2050295A3EB46650358789F2E0E77AF3509280CAD420B3B3D16C6F21DB396D2C40014A5DA8D2238F1C99DEC9AC5602E66C9A932EE0D841B66906B476140354DFEA11D9D0A2D6A39EB4FAA9A63EE9AD3B67C8035138965C3025413F5571C45E54794CAB22E9731279683DF37CE03C5D6D6C8E8218F2B6D4040ED8B8A8006A0724E20FA0633F7CD405A3433BCF181BEA9D337983A928BF85599950087D2392B0BA8B48422E232C197EE85F0FADC5F473DFBD76774FF779B451677CDB456CA8DCC54AD7DBAA8E362D566FEE0B2B1817BF331B9842EDBD3C2DF7EF065F54C8CA512C84AA3472B10CC01A7F0F63D93C29AE40D6D238CB1407683415615047FECB93401F245E21CC000CC054892F4A2AA7A458C761B396E8CFA7397FC2B156187BB1F7170CC42D775C1F6400C4E8B5C2B6C05B61CD1ABC7F07DFFE3CD5564AE90F19B7F2B9559FB772F31C518CC8BDAE1DF7C3AE1CD0AF832157AB6D4D53C5FC640A5CA8AA37AEFAF04E65DFBB08E2D4493459A0745ABCDE11BC8D672BF058B1FD35315633B2FC773BF7FDDD59E714692885A021A016067F8EF6E1EAAAB4A84AE8367EA266D6398CD1AA3367C25A576D1A1268A6F08A7FFFDA1C5E2E228A4214095431C2078B4BDBD4EE4B0C20EE24C88BC87FE10B152A278FC7E353491308CE655B7BE0BFA1A7ABC296AC3249936193DB9E394852B92C41A1B8DD702EC70978B760A92480055053838BDD424345029D1F9919284F8EFFE1E1C73D058963F6B7032B436326E891789205C88F78D5CDCD31DAB80F22F9F5D444632F2C5C8D01180271D3043510FD79E308209C106092A864886F70D010701A08209B2048209AE308209AA308209A6060B2A864886F70D010C0A0102A082096E3082096A301C060A2A864886F70D010C0103300E040810EB0E65BF20EA0B020208000482094879563FC0DBA96E51E5C69FB869D8B71732CEE7413BDE9251ABE8972CEBE844850E32F3C8E044D9BBE4CD16CF01CEBCB03FAA46AE5479C9E301B3E8DCB6FCC7BA727E0CB2F243254501F834666050680699BDA60622F40AB2AD911EECB8A8661E1A77B2D8F3F2EC734A4619298F39001866C1321089ED2CC61EBF773D8312F492FCB70CF0D2B8B54ABD3BF211500B45A31C87195275E173F31B71E3F3FCDF21D9ABE9421412F8F24B51B60E1BC97781924D7E5BCE92ED3FFABC436A051512A1E1B5F99B3574FCB38943AE6926091151630FB7941E6752C6E84D8076A510B81BD31F5364246440B58E5A7F9962D35D42D8702C9FCF83C4F5477873C5C635EF0E8655B98991091D93D345463F4877541F990164F062A1598D394B0B98A6B1F56FD9B3E268FEFE7662906B5B7E68D4839DD154FD81FE877CED81B5EFE6E2E777DC6E4A0E55D15876FD83531B6451A3225E27A0666DFF0A9ADB4D504F540808CFA92E0D40E0163194C42E9B93E9BE37F5BEAE6FFCFCDF60A4DADD48A26373CCD10640822D0D677859A6D44CBF1D9A60D800188448D1D022C163A39E8D2D450B98BBB4040830EB2C8FF62B60B046B5EA757BC9C967F4373A710EF469807AC81F975161194E657A93F5DF1A88BB56D383316E910DE7848C6F968B4FFC81CED08E9BC137145F1E503B08347BEFC124120F0EA01BC921370FBBDC5BA387C8BE687B4CDA84222D21E0905EBAC1A2D0BFB0DC337C24428B006180FEF1F0A3DBFFF6E9D1E095457DFAF4FAD47C3CBAE7611C03ED65F8CD892B6D4A453F33165DEB965DE100D9B5EEF7836CE1FD67AD86EC1C312624496C0BE8EAA57B7A2283E2B8FAE58FE082A92C0746DB8CC1BDAC3CA6DBC6F2B6C52A1FAE13294E547D6D74BD5A046545EB8297986D0AAE282EC7A4F4895D8210A8DD1C506D6946FE431492A7C74A6F45C022F31221648AA3A60DE55C39BAAF3D051DAEBB9EE665D0E53FED88B6820FA4FA6CED39A828AF8803C0315AC18845760DDBB59D59C0775DB1E953CA38A1E3E19B86A42910526EB1075384484DCF0C94F24D423A66DE8C24DB9A58AB5BD4F8CBE717874FF3C67BD04831557EAF831EC7526595AAAF2A6B876C6EE13EBA420661EDA8CD13DDE4A840959C95B94A51095B085D8686A1154428DB970628A0E109F96D5C1678C91A6289EF2BB885557194A05D941B321B5929D208E418CFC6EF6C1557D7E77F3C2E0CBED29E3D6D90593B4E3176B4C52F61EA517503EA021C4820BC4FC18C0F13A5C27500492AF72EC1E7857FFEB107952FBB0DD1DBAB1DF8004940293651E04877CDEA71CEF61016F1BBA4F865871C339FF152030B2F17E9BE7B8D4869B878554649D552BFFDA1FEEE09DD0E9E0DB9DFBE4DD08F2D39DE27416E5785990C14591F0B5058A8E77F3F731BAF5758DA6DFBF116D218DFEE1864D5517D3A03CE8D41FDC265B40DC36E8B8E1F22030ED9B44337920B3D0B682ABB1118C845E5151F969A2977595AF8BAC523147422B4F03EB62F5B3ED97422529EE84A5920976B228D2C229F9A96682E7142589BC3230DFF7F9D4950D583B078C95F8819D60422E141546023D8AF072C2FA5D9CD04666F49BF9B56CAE9592B43FF9E2E8367CF204185BB0169D2C3CB364723B2DB473550857F98C644B5E54304CE79895ED44DF0A876FD5159745570546A668AA1A512723C9470C5896A5CA00E2CED14C77151314C931690E24F48EBAB73BC1C64F6DAC97610DA0A564A940EE87745EAA9580BB35BF0EE269CC574DAC166B0B22F71B5EC4841598084FC8AFFE1A00E6856B270697986B3523AB5E7E5FFE42C2A88661DE63164CAFCC6DD20EA72837E143B33015B28539C6BFEF5FCE4108D4384F20D04D2097CA23F55F351004F83A088E57CE27E0C415775C0FA88E8524D6979C1CA811431BBD5D7997077E9472091B427ECB1968B425F675C58EA892F51DECCE6AF70619958105C999AA8C13D6366E58D49E655D720E7BFB3BF97CC04CC0FD0287F4C3CC0AA507EB95D6DA1CDFB1859B95D8B1997B8433BA2DCBA15A0E78998D9195994F8BE5C0CBE403D853C99A700C38F9460AD362F790896357EE490F0251781E34F4A9B54C8BE12E7640272AF5810C2E2D4D8C7FE774F3FC20228D806FD8A2346EC44D5F1FECCBC041F5AB1D581729A00C439A94E6B1C4C41D1FD5714FC2C6851C5E42B572DA284457DF78CA628052DEE9910D9F6AFBF033E52A64422AC1C62A65DBC1E1B8B84ABE3B10E06517127A0D1C09C256A7C870C467A5B0BC4F17460D92BC28B53E382185BCCA55487735E9CEA399F26C3B3EB39D96C5BD72CF3BF44D92BA25B2907CC6D871EF5EA4762A3617833037159FCD5EE90041311760B687CDAFB16D5B252BE2DBFDACD3A29143B8709B931F40277BAFE4001368D35F4555FA202D3C9F7168381210D89D06BC090AA4F831113D6B6229BCFC50530F8579B41BADB85055E921ECC439549833CA2C32FA84F7405E4CAAEFE706DC13114CB0BA7738D1488E0FFAAA7EB982E1AD5443FC9CD4862217D632C7027256F4F909F396B7B43170999E4505A6F98D6E5825D3EC33B9C6BA649C1925D8B04EF3E796935668274093E9C8241704712DE95B115938F9089911D6BD1F4D9605FD37A720EC88F49F828D0076B607B9689BA64D266DAE7E95649788916BC29AE25E198948D92809E3539F65E47E947FA8ABB72D2FCE656F73E2A3296CFBD6352E2490916C4C38451594654F666F0B6D06485573137D0B45DF419FAAAEEB9904AEAF55D3CFB0A44994A47D3B79A544FE0935211F8BF0C108286C87152A565AE34EC228D0706BA32255F1B6A2B0083EA43624FB48B6634C27C0F1770D697CA75CF54EC4FBFE9E195F6151D8AB3A9409DA144EA5BA35C5476B706109EA95C1608D4F3EB5F1EBD1927D64CC6F19330EDAAF38DF3CD2D08DCD5D7067429E882E93CF8246D5EAF170A9852D4C2A22060FCC1C7707752AD6DF8B7B8450CFA42D82A02832C2C117DAC43182FFB97EF69182032F1365DE675E441770CC9EBE66DCB952B5E5118A5EDF8B999EF76907FEF70E4E0F0F2F601C8525EAA4226087CE75E6B5840726E54A8B6BFE0B9A8FD37D24429D8AAEDE5FACABACA7F3A8BB554E72F781BD1A89C785165E624926B15894F04913F11EBFF58C74F4538C18DAC0D869570693B2253A9067868D61D23144181018480BF6B08DD4A385A864559F58042A7408305EF0ACCC875F95E6F6FC2207CC5B6E300C7700DA714CAA21F9628D906E35792EFE7F7FAC6DEA91E873A76CCD54C303A0280C64BB4403CC6E0B580656523C3E3EF2D914D44AA5F903FF0816B82852BD1502FB5466FBA020F680D873A009FFC463125302306092A864886F70D010915311604143359BF2499D1FC7E7DC11B9E8092563755A7CD8430313021300906052B0E03021A05000414485364B5B1D594DD5A3CC8EB98C5E2271B3018A50408621240A9856972E602020800";

function log(msg) {
	window.infodiv.insertAdjacentHTML('beforeend', msg + '<br>');
	window.infodiv.scrollTop += 1000;
}

window.certlistObject = [];

window.onload = function() {
	window.infodiv = document.getElementById("infodiv");
	window.certlist = document.getElementById("certificatelist");
	window.scandlg = document.getElementById("scandlg");
	window.scanfield = document.getElementById("scanfield");
	window.scandlg.style.visibility = "hidden";

	var promise = new Promise(function(resolve, reject) {
		var req = new XMLHttpRequest();
		req.onreadystatechange = function () {
			if (this.readyState === 4) {
				if (this.status === 200){
					resolve(req.responseText);
				}
				else
					reject(new Error("Challenge generation failed"));
			}
		};
		req.open("GET", "generatechallenge.php", true);
		req.send();
	}).then(function(challenge) {
		/* connecting to SCWS */
		log("Connecting to SCWS...");
		SCWS.findService(window.webappcert, challenge).then(function(findServiceData) {
			log("Connection to SCWS succeeded");
			return new Promise(function(resolve, reject) {
				var req = new XMLHttpRequest();
				req.onreadystatechange = function () {
					if (this.readyState === 4) {
						if (this.status === 200)
							resolve(req.responseText);
						else
							reject(new Error("Cryptogram verification failed"));
					}
				};
				req.open("GET", "verifycryptogramsignchallenge.php?keyID=" + findServiceData.keyID + "&cryptogram=" + findServiceData.cryptogram + "&rnd=" + findServiceData.challenge, true);
				req.send();
			}).then(function(signature) {
				log("create environment...");
				return SCWS.createEnvironment(signature);
			});
		}).then(function() {
			log("SCWS environment created successfully");
			SCWS.onserviceunresponsive = function() {
				log("Service became unresponsive");
			}
		}).catch(function(err){
			log("ERROR: " + err.message);
			window.certlist.clearChildren();
		});
	}).catch(function(err) {
		log("ERROR: " + err.message);
		window.certlist.clearChildren();
	});
}

if( typeof Element.prototype.clearChildren === 'undefined' ) {
	Object.defineProperty(Element.prototype, 'clearChildren', {
		configurable: true,
		enumerable: false,
		value: function() {
			while(this.firstChild) this.removeChild(this.lastChild);
		}
	});
}

function requestCertificates() {
	log("request certificates...");
	var options = {
	};
	var enableSoftToken = getEnableSoftToken();
	if (enableSoftToken != "default") 
		options["enableSoftToken"] = enableSoftToken;
	var scanDialogMode = getScanDialogMode();
	if (scanDialogMode == "synchronous") {
		options["showScanDlg"] = showScanDlg;
		options["closeScanDlg"] = closeScanDlg;
		options["setScanDlgMsg"] = setScanDlgMsg;
	}
	else if (scanDialogMode == "asynchronous") {
		options["showScanDlg"] = showScanDlgAsynchronous;
		options["closeScanDlg"] = closeScanDlgAsynchronous;
		options["setScanDlgMsg"] = setScanDlgMsgAsynchronous;
	}
	if (getScanMessageMode() == "customized") {
		options["waitingMsg"] = "Veuillez insérer une carte.";
		options["connectingMsg"] = "Connexion à la carte.";
		options["readingMsg"] = "Lecture de la carte";
	}

	return SCWS.requestCertificates(function(certificates) {
		log("update list of certificates...");
		window.certlist.clearChildren();
		for (var i = 0; i < certificates.length; i++) {
			var certificate = certificates[i];
			var elt = document.createElement("option");
			elt.id = window.certlistObject.length;
			window.certlistObject.push(certificate);
			var readerName = "soft token";
			if (certificate.parent.reader)
				readerName = certificate.parent.reader.name;
			elt.textContent = certificate.subject + " (" + certificate.issuer + ") - " + readerName ;
			window.certlist.appendChild(elt);
		}
		log("Found " + certificates.length + " certificates");
		return certificates;
	}, options).then(function(certificates){
		log("requestCertificates finished");
		log("Found " + certificates.length + " certificates");
		log("");
	}).catch(function(err) {
		log("ERROR on request certificates: " + err.message);
		window.certlist.clearChildren();
		log("");
	});
}

function getEnableSoftToken()
{
	var elt = document.querySelector('input[name="softToken"]:checked');
	if (!elt || !elt.value)
		return "default";
	return elt.value;
}

function getScanDialogMode()
{
	var elt = document.querySelector('input[name="scandlg"]:checked');
	if (!elt || !elt.value)
		return "asynchronous";
	return elt.value;
}

function getScanMessageMode()
{
	var elt = document.querySelector('input[name="scanMessage"]:checked');
	if (!elt || !elt.value)
		return "asynchronous";
	return elt.value;
}

function showScanDlg() {
	window.scandlg.style.visibility = "visible";
}

function closeScanDlg(message) {
	window.scanfield.value = getCloseScanMessage(message);
	window.scandlg.style.visibility = "hidden";
}

function getCloseScanMessage(message) {
	if (message)
		return message;
	else if (getScanMessageMode() == "nominal")
		return "The operation is finished with success.";
	else
		return "L'opération s'est terminée avec succés.";
}

function setScanDlgMsg(message) {
	window.scanfield.value = message;
}

function showScanDlgAsynchronous() {
	return new Promise(function(resolve, reject) {
		window.scandlg.style.visibility = "visible";
		resolve();
	});
}

function closeScanDlgAsynchronous(message) {
	return new Promise(function(resolve, reject) {
		window.scanfield.value = getCloseScanMessage(message);
		return setTimeout(function() {
			window.scandlg.style.visibility = "hidden";
			resolve();
		}, 2000);
	});
}

function setScanDlgMsgAsynchronous(message) {
	return new Promise(function(resolve, reject) {
		window.scanfield.value = message;
		resolve();
	});
}

function reconnectCertificate(certificate)
{
	return new Promise(function(resolve, reject){
		var reader = SCWS.getReader(certificate.parent.reader.name);
		if(!reader)
			reject("reader not found");
		reader.connect().then(function(token) {
			token.getObjects().then(function(objects){
				for (var i = 0; i < objects.length; ++i) {
					var object = objects[i];
					if(object.type === "certificate" && object.ckId === certificate.ckId)
						resolve([certificate,token]);
				}
			}).catch(reject)
		}).catch(reject)
	});
}

function chooseCert()
{
	/* get selected certificate element */
	var elt = window.certlist.options[window.certlist.selectedIndex];
	/* enumerate through connections and certificate items to retrieved corresponding certificate object */
	var conn = null;
	var certificate = window.certlistObject[elt.id];
	
	/* remember selected elements */
	window.choosenCertificate = certificate;
	if (certificate) {
		/* updating pin name label */
		document.getElementById("pinname").textContent = certificate.parent.pins[certificate.pinNumber].label || "(Default PIN)";
		/* updating certificate value */
		certificate.getValue().then(function(value) {
			document.getElementById("certificatevalue").textContent = value;
		/* build software certificate and public key using forge library */
			window.forgeCert = forge.pki.certificateFromPem(value);
			window.forgePubKey = window.forgeCert.publicKey;
		})
	}
}

function getGlobalPin()
{
	return new Promise(function (resolve, reject) {
		if (!window.choosenCertificate)
			throw "no certificate selected";
		reconnectCertificate(window.choosenCertificate).then(function(res) {
			window.connection = res[1];
			resolve(res[1].pins[res[0].pinNumber]);
		});
	});
}

function getGlobalToken()
{
	return new Promise(function (resolve, reject) {
		if (!window.choosenCertificate)
			throw "no certificate selected";
		reconnectCertificate(window.choosenCertificate).then(function(res) {
			window.connection = res[1];
			resolve(window.connection);
		});
	});
}

function disconnectAll()
{
	if(window.connection)
		return window.connection.disconnect();
	else
		return Promise.resolve();
}

function startAutoLogin()
{
	getGlobalPin().then(function(pin) {
		log("startAutoLogin ...");
		pin.startAutoLogin(document.getElementById("auto-login-counter").value).then(function() {
			disconnectAll().then(function() {
				log("startAutoLogin successful");
			});
		}, function(err) {
			disconnectAll().then(function() {
				log("ERROR: " + err.message);
			});
		});
	});
}

function stopAutoLogin()
{
	getGlobalPin().then(function(pin) {
		log("stopAutoLogin ...");
		pin.stopAutoLogin().then(function() {
			disconnectAll().then(function() {
				log("stopAutoLogin successful");
			});
		}, function(err) {
			disconnectAll().then(function() {
				log("ERROR: " + err.message);
			});
		});
	});
}

function login()
{
	getGlobalPin().then(function(pin) {
		log("Login...");
		pin.login(document.getElementById("pin").value).then(function() {
			disconnectAll().then(function() {
				log("Login successful");
			});
		}, function(err) {
			disconnectAll().then(function() {
				log("ERROR: " + err.message);
			});
		});
	});
}

function change()
{
	getGlobalPin().then(function(pin) {
		log("change pin...");
		pin.change().then(function() {
			disconnectAll().then(function() {
				log("change pin successful");
			});
		}, function(err) {
			disconnectAll().then(function() {
				log("ERROR: " + err.message);
			});
		});
	});
}

function init()
{
	getGlobalPin().then(function(pin) {
		log("Login PUK...");
		pin.login(false, true).then(function() {
			disconnectAll().then(function() {
				log("Login PUK successful");
			});
		}, function(err) {
			disconnectAll().then(function() {
				log("ERROR: " + err.message);
			});
		});

		log("init pin...");
		pin.init().then(function() {
			disconnectAll().then(function() {
				log("Init pin successful");
			});
		}, function(err) {
			disconnectAll().then(function() {
				log("ERROR: " + err.message);
			});
		});
	});
}

function loginWithPinDialog()
{
	getGlobalPin().then(function(pin) {
		log("Login...");
		pin.login(false).then(function() {
			disconnectAll().then(function() {
				log("Login successful");
			});
		}, function(err) {
			disconnectAll().then(function() {
				log("ERROR: " + err.message);
			});
		});
	});
}

function loginWithPinPad()
{
	getGlobalPin().then(function(pin) {
		log("Login...");
		pin.login().then(function() {
			disconnectAll().then(function() {
				log("Login successful");
			});
		}, function(err) {
			disconnectAll().then(function() {
				log("ERROR: " + err.message);
			});
		});	
	});
}

function RequestCredentialLoop() {
	getGlobalPin().then(function(pin) {
		var state = pin.initSubmissionsState;
		log("--------------------------");
		log("Request credential loop...");

		var iter = 0;

		let requestLoop = function() {
			log(" - requestCredential (" + ++iter +")...");
			pin.requestCredential(
				pin.credentialProperties,
				state,
				pin.token.reader.name,
				pin.token.label).then(function(credential) {
					log(" - requestCredential (" + iter +") done");

					if (credential) {
						log(" - login (" + iter +")...");
						pin.login(credential, state).then(function() {
							disconnectAll().then(function() {
								log("Login successful");
								log("--------------------------");
							});
						}, function(err) {
							log("\tlogin error: " + err.message);
							const noFatalErrors = [
								"CKR_PIN_INCORRECT",
								"CKR_PIN_INVALID",
								"CKR_PIN_LEN_RANGE",
								// for test only, we accept this error to check request credential/pin dialog behaviors if a credential is being blocked
								"CKR_PIN_LOCKED"
							];
							if (noFatalErrors.indexOf(err.code) !== -1)
								requestLoop();
							else
								disconnectAll().then(function() {
									log("ERROR: " + err.message);
									log("--------------------------");
								});
						});
					}
					else {
						disconnectAll().then(function() {
							log("Canceled by the user.");
							log("--------------------------");
						});
					}
				}, function(err) {
					disconnectAll().then(function() {
						log("ERROR requestCredential: " + err.message);
						log("--------------------------");
					});
				}
			);
		};

		requestLoop();
	});
}



function bioEnroll() {
	getGlobalPin().then(function(pin) {
		log("Bio enroll...");
		pin.bioEnroll().then(
			function() {
				log("bio enroll successful");
			},
			function(err) {
				log("ERROR bio enroll: " + err.message);
			}
		);
	});
}

function ActivateBiometry() {
	getGlobalPin().then(function(pin) {
		log("Activating biometry feature...");
		pin.login(false, true).then(function() {
			pin.ActivateBiometry().then(
				function() {
					log("Biometry successfully activated");
				},
				function(err) {
					log("ERROR Activate biometry: " + err.message);
				}
			);
		}, function(err) {
			disconnectAll().then(function() {
				log("ERROR login SO: " + err.message);
			});
		});
	});
}

function initToken() {
	log("Initializing token...");
	SCWS.readers[0].connect().then(function(token) {
		// Check if pins have been entered
		pin = new SCWS.Pin(token, 0);
		let constraints = {
			label: document.getElementById("labelfield").value,
			minUpperCase: document.getElementById("minUpperCase").value,
			minLowerCase: document.getElementById("minLowerCase").value,
			minDigit: document.getElementById("minDigit").value,
			minSpecial: document.getElementById("minSpecial").value,
			maxIdenticalSequence: document.getElementById("maxIdenticalSequence").value,
			maxIncDecSequence: document.getElementById("maxIncDecSequence").value,
			minLength: document.getElementById("minLength").value,
			maxLength: document.getElementById("maxLength").value,
			minAlphabetic: document.getElementById("minAlphabetic").value,
			minAlphanumeric: document.getElementById("minAlphanumeric").value,
			pinDuration: document.getElementById("pinDuration").value,
			maxUnlock: document.getElementById("maxUnlock").value,
			maxTriesSoftware: document.getElementById("maxTriesSoftware").value,
			historyCount: document.getElementById("historyCount").value,
		};

		// Check compatibility
		if (constraints[minLength] > constraints[maxLength])
			log("ERROR: min length is larger than max length.");
		var sumMinLength = constraints[minUpperCase] + constraints[minLowerCase] + constraints[minDigit] +
			constraints[minSpecial] + constraints[minAlphabetic] + constraints[minAlphanumeric];
		if (constraints[sumMinLength] > constraints[maxLength])
			log("ERROR: max length and required minimum are not compatible");
		if (constraints[minAlphabetic] < constraints[minUpperCase] + constraints[minLowerCase])
			log("ERROR: minAlphabetic must be ≥ minUpperCase + minLowerCase");
		if (constraints[minAlphanumeric] < (constraints[minAlphabetic] + constraints[minDigit]))
			log("ERROR: minAlphanumeric must be ≥ minAlphabetic + minDigit");

		token.initToken(constraints).then(function(response) {
			log("SUCCESS: Token initialized.");
		}).catch(function(error) {
			error("ERROR: Failed to init token... ", error);
		});
	})
}

function getHashAlg()
{
	var elt = document.querySelector('input[name="hashalg"]:checked');
	if (!elt || !elt.value)
		return null;
	return elt.value;
}

function getKeyType()
{
	var elt = document.querySelector('input[name="keytype"]:checked');
	if (!elt || !elt.value)
		return null;
	return elt.value;
}

function fillOptions()
{
	var options = {
	};
	var enableSoftToken = getEnableSoftToken();
	if (enableSoftToken != "default") 
		options["enableSoftToken"] = enableSoftToken;
	var scanDialogMode = getScanDialogMode();
	if (scanDialogMode == "synchronous") {
		options["showScanDlg"] = showScanDlg;
		options["closeScanDlg"] = closeScanDlg;
		options["setScanDlgMsg"] = setScanDlgMsg;
	}
	else if (scanDialogMode == "asynchronous") {
		options["showScanDlg"] = showScanDlgAsynchronous;
		options["closeScanDlg"] = closeScanDlgAsynchronous;
		options["setScanDlgMsg"] = setScanDlgMsgAsynchronous;
	}
	if (getScanMessageMode() == "customized") {
		options["waitingMsg"] = "Veuillez insérer une carte.";
		options["connectingMsg"] = "Connexion à la carte.";
		options["readingMsg"] = "Lecture de la carte";
	}
	return options;
}

function sign()
{
	/* sign the input data using the selected hash algorithm */
	log("request private key...")
	var options = fillOptions();

	SCWS.requestPrivateKey(window.choosenCertificate, false, function(pkey){
		log("Signature...");
		try {
			var data = document.getElementById("datafield").value;
			var hashAlg = getHashAlg();
			window.signatureKeyType = pkey._keyAlg;
			if (hashAlg) {
				return pkey.hashAndSign(data, hashAlg).then(function(data) {
					data = SCWS.toHexString(data);
					log("Signature done:<br>&nbsp;&nbsp;" + data);
					window.lastRet = forge.util.hexToBytes(data);
				});
			}
			else {
				return pkey.sign(SCWS.fromHexString(data)).then(function(data) {
					data = SCWS.toHexString(data);
					disconnectAll().then(function() {
						log("Signature done:<br>&nbsp;&nbsp;" + data);
						window.lastRet = forge.util.hexToBytes(data);
					});
				});
			}
		}
		catch(err)	{
			log("ERROR: " + err.message);
			throw err;
		}
	}, options).then(function() {
		log("request private key succeeded.");
		log("");
	}).catch(function(error) {
		log("request private key failed: " + error.message);
		log("");
	});
}

function verify()
{
	if (window.signatureKeyType === "ECDSA")
		verifyEC();
	else if (window.signatureKeyType === "RSA")
		verifyRSA();
}

function verifyRSA()
{
	/* verify the previously generated signature, using the input data and hash algorithm */
	var r;
	try {
		var hash, scheme;
		var data = document.getElementById("datafield").value;
		var hashAlg = getHashAlg();
		if (hashAlg) {
			hash = forge.md[hashAlg].create();
			hash.update(data);
			hash = hash.digest().bytes();
			scheme = "RSASSA-PKCS1-V1_5";
		}
		else {
			hash = forge.util.hexToBytes(data);
			scheme = null;
		}
		log("Recovered data:<br>&nbsp;&nbsp;" + forge.util.bytesToHex(forge.rsa.decrypt(window.lastRet, window.forgePubKey, true, true)));
		r = window.forgePubKey.verify(hash, window.lastRet, scheme) ? "OK" : "failed";
	}
	catch (ex) {
		r = "failed (" + ex + ")";
	}
	log("Verification " + r);
}

function makeASN1Integer(hexStr) {
    // Remove leading zeros
    hexStr = hexStr.replace(/^00+/, '');

    // Add leading 00 if high bit is set (to indicate positive integer)
    if (parseInt(hexStr[0], 16) >= 8) {
        hexStr = '00' + hexStr;
    }

    const bytes = forge.util.hexToBytes(hexStr);

    return forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.INTEGER, false, bytes);
}

function RawToDERSignature(rawSig) {
    // rawSig: Forge binary string or Uint8Array with r||s
    if (rawSig instanceof Uint8Array) {
        rawSig = forge.util.createBuffer(rawSig).getBytes();
    }

    const len = rawSig.length;
    if (len % 2 !== 0) {
        throw new Error("Invalid raw signature length");
    }

    const r = rawSig.substring(0, len / 2);
    const s = rawSig.substring(len / 2);

    const rBytes = forge.util.createBuffer(r, 'raw').toHex();
    const sBytes = forge.util.createBuffer(s, 'raw').toHex();

    return forge.asn1.toDer(forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.SEQUENCE, true, [
        makeASN1Integer(rBytes),
        makeASN1Integer(sBytes)
    ])).getBytes();
}

function verifyEC() {
    let r;
	console.log(typeof window.lastRet);
    try {
        const data = document.getElementById("datafield").value;
        const hashAlg = getHashAlg();
        if (!hashAlg) throw "No hash algorithm selected";

        // Hash the message
        const md = forge.md[hashAlg].create();
        md.update(data, 'utf8');
        const digest = md.digest().bytes();

		const signatureDer = RawToDERSignature(window.lastRet);

        const verified = window.forgePubKey.verify(digest, signatureDer);
        r = verified ? "OK" : "failed";
    } catch (ex) {
        r = "failed (" + ex + ")";
    }
    log("Verification " + r);
}

function encrypt()
{
	/* encrypt the input data */
	var data = document.getElementById("datafield").value;
	var r = window.forgePubKey.encrypt(forge.util.hexToBytes(data));
	window.lastRet = r;
	r = forge.util.bytesToHex(r);
	log("Encryption done:<br>&nbsp;&nbsp;" + r);
}

function decrypt()
{
	var options = fillOptions();

	/* decrypt the result of the last encryption */
	SCWS.requestPrivateKey(window.choosenCertificate, false, function(pkey){
		try {
			//var a = forge.util.binary.raw.decode(window.lastRet);  Ligne utile pour essayer décrypt en IE
			//log("Decryption... :<br>&nbsp;&nbsp" + a );
			log("Decryption...");
			return pkey.decrypt(forge.util.binary.raw.decode(window.lastRet)).then(function(data) {
				data = SCWS.toHexString(data);
				log("Decryption done:<br>&nbsp;&nbsp;" + data);
			});
		}
		catch(err) {
			log("ERROR: " + err.message);
		}
	}, options);
}

function loadFile(file, callback)
{
	var xobj = new XMLHttpRequest();
	xobj.open('GET', file, true);
	xobj.onreadystatechange = function () {
		if (xobj.readyState == 4 && xobj.status == "200") {
			callback(xobj.responseText);
		}
	};
	xobj.send(null);
}

loadFile('rootCA.crt', function(file) {
	window.caCert = forge.pki.certificateFromPem(file);
});

loadFile('rootCA.key', function(file) {
	window.caKey = forge.pki.privateKeyFromPem(file);
});

function changePage()
{
	localStorage.setItem("arr", JSON.stringify(SCWS.saveEnvironment()));
	window.location.href = "restore_environment.html";
}

function generatekeypair()
{
	SCWS.readers[0].connect().then(function(token) {
    pin = new SCWS.Pin(token, 0);
    pin.login(false, false).then(function(value){
		if (getKeyType() == "RSA")
		{
			token.generateKeyPair(document.getElementById("keysize").value, {container: document.getElementById("containername").value, label: "test"}).then(function(){
           log("key pair generated."); 
        });
		}
		if (getKeyType() == "EC"){
			token.generateKeyPair(document.getElementById("keysize").value, {container: document.getElementById("containername").value, label: "test", parameters: {curveName: document.getElementById("keyparam").value}}).then(function(){
           log("key pair generated."); 
        });
		}
    }, function(err) {console.log(err);});
});
		
}

function signCSR(csrPath)
{
	return new Promise(function(resolve, reject) {
		var req = new XMLHttpRequest();
		req.onreadystatechange = function () {
			if (this.readyState === 4) {
				if (this.status === 200)
					resolve(req.responseText);
				else
					reject(new Error("read file failed"));
			}
		};
		req.open("GET", csrPath, true);
		req.send();
	}).then(function(csrPem) {
		var csr = forge.pki.certificationRequestFromPem(csrPem);
		var certificate = forge.pki.createCertificate();
		certificate.serialNumber = '01';
		certificate.validity.notBefore = new Date();
		certificate.validity.notAfter = new Date();
		certificate.validity.notAfter.setFullYear(certificate.validity.notBefore.getFullYear() + 1);
		certificate.setSubject(csr.subject.attributes);
		certificate.setIssuer(window.caCert.subject.attributes);
		certificate.setExtensions([{
			name: 'keyUsage',
			keyCertSign: true,
			digitalSignature: true,
			nonRepudiation: true,
			keyEncipherment: true,
			dataEncipherment: true
		}]);
		certificate.publicKey = csr.publicKey;
		// sign certificate with CA key
		certificate.sign(window.caKey);
		return certificate;
	});
}

// bout de code pour détruire un objet dans la carte via SCWS
// A mettre dans la console
/*
SCWS.readers[0].connect().then(function(token) {
    pin = new SCWS.Pin(token, 0);
    pin.login(false, false).then(function(value){
        token.getObjects().then(function(objects){
            console.log(objects);
            SCWS.destroyObjects(objects[1]).then(function(){
                console.log("object destroyed");
            });
        });
    }, function(err) {console.log(err);});
});
*/

// A mettre dans la console
//Genere une paire de clés
/*
SCWS.readers[0].connect().then(function(token) {
    pin = new SCWS.Pin(token, 0);
    pin.login(false, false).then(function(value){
        token.generateKeyPair(2048, {container: "auth", label: "test"}).then(function(){
           console.log("key pair generated."); 
        });
    }, function(err) {console.log(err);});
});
*/

// A mettre dans la console
//importe le certficat Voldemort (Pour l'utiliser sur IE il faut le passer par Babel)
/*
var formData = new FormData();
var p12_1 = "30820ED102010330820E9706092A864886F70D010701A0820E8804820E8430820E80308204B706092A864886F70D010706A08204A8308204A40201003082049D06092A864886F70D010701301C060A2A864886F70D010C0106300E0408494179C1A0857B4C0202080080820470BDD9C74C9A0CF83D84140769D944CE1363AEC607A61E1C8D029B81EFE2A18155FE3ED188F0AF9C961ABF4504B84DAD799CA43F48568C6703759DEDD1E4F1B34197FDC1E977B98FE8001A6BD88E6264F6C759B2AE0D3848F82B8D4B040CC8B9587747393B03E21C1649C7EA9A9EA2F88BEA45932A021FE5508386452A1F7257FB725145431333DA98547F36A576CDDE12FEB9E1BC1A637495248B6BF173B4C4D537B353D5BA43A91F08D547BB06E2CDD5CD34ECBEF825633C12476FD14B8AB8C3C8B386478B87F6F8DA17BE21090BD4D7B4D82D82587FB15DB6918A1225C6AE1F31AB51B32F9C2EC4A049267BE76E66DB37639EE83CDE59320652BA65D0815E6E6A6BEF2E81C06ECD4AFBFAEDFE1FDF60CC90FE0615D92EF2F8BD23254A4AE606C1AD43E1CC928F2A625FFAC09DCF66CA7A0A8CADC11EB0B4A0DB1B59F85BE0F0AC3514A2A920CF9346910EC14E7A6E16160CBD44ECEA65F3D1A859170DA8E9303DDB219D8F10439C503684DFEAD035E98786AF802371DD5E671017F6E611035E4650F8528886129B3124885121BE6F4B17E29B6D44F848E596496FF1EF0296B800AE35927E6B55B150780CE40303BDEF2B0FBE5E49D70ABE15511100DDC09C0ACDED17480FE64A299CD4B7FE5A6E32ECDA35B9272AE247D4BB6B37625BB49353A6271B9B87B86F145C4557381861CFE575C984CB35A01CF82C2C6370E8174F56340613F2D8D76D4C409D06461DE43EBB64A7C67113EF6285225F934B8048102DE55E49F9E3390F2C0EBCC33D6110E7F8F9A1DD4722CE1D7AEFE72E41B91FB0FF0F03D1F35AB65EDAE19DC3A9915728B5B16F1072025EDAA8D49D3D5C62AE3E74D010DEF568B33EFDD2DE3F5593E7EBCF52D78A5054A06234B9C31AF7FE8374402EF8EF1B75315F9561843F3ADE56E9235CB93B2147FFFC3DA890EAA49DA0D46AA7B11C8B55DEBE605072C8D5E7EDFB5C5AE59DDDEFE1BC3F72A78777CD77E258B332D0B3F4CAEE17D2B6666EF07548315A992F1044FE9776860F3049EA266529CAD3AB635689B2145072162BE179F5DC4406C3D0E749A9101B88D9B77673509013B48811A8762D504301349B5A4B6CA7803D2A238BE26A6B865A869FE4509EFD7313389F740EA4D44B40E68EE5B3139279D1B75AB74EAD6B41308FF3F78861477C9B016383343F37D3D5018C5E8C90A96BE0AAF1658DE5377AF39B58AE881D41B711D09030257E446CB737A20CE8D8DDDD1C1F38BB1B43C629522C22C670AEA42DC27AF05AB936A76D457E08BFBAF90A140E861A74D1630C0EEC44215ECCC5559E9C53B140A3963F039DA7800F1F4CFEDA2D2953ABDE245C87CBACA452F1EF44B356BC4F59C474EB03D9658CA662F529F124D471D8863A4FA6EFBBA2EE37373762693258F8728B95BA4FEEF6F4529B8ACB629C5B77EE713461365671AB9D744C32338FF59C898A38C22ADBF850CD179A25ADA62740AD2E7D25EB757B864F4070D5C2E338CFB3377CB203E6E71BBA6ED9546A76C80E83DE9D5A2194352BD1C456EE2B8564282A5EF3E4152C94BB2275A99FD13C872E2F47AB39E9BAEBA582700A9D6968E8DB3B50F538EDCBB7E0CEA08A308209C106092A864886F70D010701A08209B2048209AE308209AA308209A6060B2A864886F70D010C0A0102A082096E3082096A301C060A2A864886F70D010C0103300E0408E2B529BCA62C2E6102020800048209481F406C1306D006BDFB5D389E2B48DA151519E67F0AE5DD6CF0383809BC63349FEA998C604A3DA577368A43CBDB714ACDE42AAAF56D8EE4342246CEFC92E71ACB778527FB543F01EC9639F4B54CA57D758ACD71D3E8354C50088E010FB939A58BEB0DD065FF04AFDAFB60134DA8B45889BEE8A16DF092FD2C49863C666460F02F96EE12E8345EC5778B3EDBA083381C8035F5D4DB97C08267CDADD9DF3A7F13ADB3063F285A5A53FD9DD6BDC8E49E2A9B490000E08EC1A4BC6088D578351E8A407F86C781CB85D2722050128EF5C07789559FEEF76FF580788A2E8AE834711924A60B67888003CB8160EAD88A13D1A8C124E099891D1B70A8CAF5BC84271A4B6E23898057E3B6F170B1FC86993059159622EEC94C3248D39423F4DE262A8D815FE46627B87FAE713983BC1E1466C0BB2BCFAC7528BE72D2EC1296D9F9669945E5EFABD1A98534D9225EE1FBD3CECE92B7CDBAC12015CF2BCB0D57A6C28CA6F52BA715FBFC14A298AF421A5DD86D3FB37E47068AD0FBAC2036EC6B667851114BB50FAC0D7327203CA812C57CCDE11EA0382CA725B39EE7BB0C1BA001659B44104795F1AD5069980437EC473EB6DB45BA947E7199044CD5E2F919ED6A1BD8141950D9202C2FF4D85A3DB0C1272EBDDF851DD94B8D19DE71EB9302FDC433C3EFF618B31483D5578503D88AB1DDE47C88109453821C41E38529720ED739968C1C7C53B72E7319B0358809EA63CB124FFB808346F7B8E26DED03B622337DFDE9665FD756649D630E32FB1A9EA22F06F1EFCD4A08F571158A4CCCEE6E77EE369B410818D3910206247C2C007EC7BD719AB58989CC0928ABECCCFBE2F26E025DEED9B92E6221218C7DC35342DEA26C64C7C4CF1ACB5848B17D5B00EA64E21562DD6DF49D4BDCFCD073ADB5C8EC245DB37680BF49580ABECB8326B2BF4730988E29F38FA79BB5B16575F7AA0923557B55CCD5F599AC8D34387A47B2CD59DF76B524129AC2C16D9B227BA895CA7B4657B1B1B9363EE68B533BFAF927E7CC8E34BB665DE59339041E1402D4BBBD06005CE1944B48245BA049A51591A2CF72B6796EA8F0E8DC5653F88FF3758DC0F903151C6A2C10ADD310B9A71CAF7B31E03BBCFDD3099E09136C456422DB9F97848F307ABE4A5BECFDFDBF083B8DAE617DEC8B91B45A0700D60ECBDA89F353A220F3D453147F85EF6C0F150A892FA352709BBB7890541416CCC6A83C45F935D5364F9B2562C97F21B3D502F352F67EE5E5ADFB937C9970FB46B2DC0427BE1357F2B2C3FC9E4B82F134783A2201078BE351CB5F5895331CCFCF161A4728E74F20392640F62CADDB61B153CB51E927D2132294353C5E961A2394E85D2797DF1C62B8ECDC52CE7B8842DEEB42337D3A5A23DD81723453818F3C3D60600B3F108F4FF105C464AD4D2012B123F5D3C7304DD0C8AB0C49296E17F7C34A51E0BFBF83A889B1EDB73AEFE87AC6D21DCF6DD3E3F783A88A13197B9AEB910ECFAB88D25B47758795FE36CAED4B9DFAE379793FD4B15EEA6BB78D8C11B6AC721BB3CCB711262B431F283138C9634E9939D04BB7C5006D7AC792D7F9CC3E18618277FF398A636FFA41303BBDB96D1DDD2BC198B9914FF1C816237C878C8E62FA0567B8061006FB4F7464EB84EB9394BEB90932A2464398348BF47DEBE6D2B527EA9E93B9FBB6A4E7CBF84E5957BD07F92AB7425A920868F7DABE9852B6AB49ACC95064902F97A3621C59168F3B78D3D6B91A9A98A271E91A40642DB9A1F839D8B7E26AF5B5E86E20CB007B7748C90BCE58CA643D6DBB8753A19AABE0246DD9C64BB14B6077F7C909901F0A7B0968479340297A0D01889F87B7553AC0EE9DF9A8BE4895BDB4179E42B35D8650D1729EE2CCB37458EB777023590A75FD1591C814AFB50EB17830A9C5DC4A90D2F2515F78D1B8E34F9F1B35C29CEF63982FB78C178A1F1C52019323964BD299A8C12C0FBA7B8B66B563888A7F069D00816BEE76C4EB37A8D088F0FE846698601A41AF28465A790B87542BE2CE058A7D7BA9EE607E5F014801592401079502B2738F50651D5F683C29AF2F2461FDDAF0984F785C8CF8568138B4DFF328C26971BE81B19A679B2CD049012D5C5073A9C6A9BBA8B10C07A9CE85E23382313D1B2F2C59422638A54D1919FEA68B3996D869C97FFFAFD86EBBFFCF63090C73D57070963C43047B976714BFA73A96EAC237C4109C8512D6012237FFC73567B57A2893B3F735E4093D484530B19ECDD2E2FF410B0EA6D2FC87F1CC7FEF540DE871272A99FC1C7067C0310FDC0E339D8F20DDECF839400704D5426E5412B76951B4B49D957DB7E8E180428AA6CC7B24844DBC3C0540C73482AB3E2D2BABE8891B3D2A66A465636AF0EA2C2C9938AC9579B2707E93504590250AB389CFFAD9EC7118C7C0443E790436845346CDB5F10B17EE2D8C52C3300CC890433CDDB154811270EC161789D48B070EB5B7305F2CA764025B82DC929160F54FCE280B8E496B7ADB494B132AE0767A6E53A0B076F9EFAA3B41F1DFAB5035155DA337D8BF4F8F21A95830FF4A9D0241ADAC75CA0EA1E06FAEFBA9BEED97DD5BC73FEC8B5E9DF0F2735328E28B7F5495A11F65542C10BFE607DF7D9C209934ACD3AAFA01645CBE4D346E898F8C737FF74756C222063B7C4030A7F019945F16911771C5D515F8A7B835DD094D2FF28CC2F5291210CD6A261523EABB50F82EC42B5DEBB6527E3AF128DAA905255EADFE0B4AEEF671F53A9DCC19D3E28F58239D7917D20194FC690E3717E557877F0BB70A129AA4C0980D048D5194727DDE359B415E41AD6EFC9A09EE0B7C739E5B471D95B0AD6A0BA96948E353CBAC8B09D96C9F505CEBAD12178145480156D0E4B9E5F986DE3554358E9DCE3940A5B2402FE4CE33EAA34A5D47AE6F5C3D04296009AD28DA2B49DA9C5AB91D2317F140BBD09E7026B52BB1638B4634405204FA7AAFBDE4E917F89DA54F66F4DC266310C812879BBA5EB01DC73B0E8A73777239C7CCDA599DB03309198F2C56B6E0E319391B0C8E1B65AB529AA8785C60499D333543D4D35AC32AB7F93B2387F4DA5751FEA0389557BE5E9E839B95D0BB2525FF20CB2FF5C1D7E8C17BCA775F2299D2AABF23A46ABA565BA117F1F78E0845EEB935C41AF293D256F719CEA2C0E1968071CB88EC9FD039A8764A390718379026E00E9502D17ECB6EF3C3CD80CDCD4AE453144E61E52D15BC3536C9DCDE1B6A19DB803DC419667EA5AA18E838143D7C39A1185909773E72E8803CAC3D4D76BD938865138E02E8B27EFEE8985BA85B270080DF1F7F22CB56ADB08B0EFE809E70DBA5A4E7AE563D23B22918092EE32192815DBE6EC2C00B5670ADDDEA3125302306092A864886F70D010915311604145889DC9A6EDC33B421C06020AA0E1AA53302D20630313021300906052B0E03021A0500041438234ECC794DEA8185C68702DFCD318E0DA4A249040803240516228D99EE02020800";
var byteArray1 = new Uint8Array(p12_1.match(/.{2}/g).map(e => parseInt(e, 16)));
var blob1 = new Blob([byteArray1], {type: "application/octet-stream"});
formData.append("file", blob1);
formData.append("password", "yyoussef");

SCWS.readers[0].connect().then(function(token) {
    pin = new SCWS.Pin(token, 0);
    pin.login(false, false).then(function(value){
        token.getObjects().then(function(objects){
            token.import(formData, {label: "P12"}).then(() => {
                console.log("object imported.");
            });
        });
    }, function(err) {console.log(err);});
});
*/

var testSoftToken2k = function() {
	log("Test Softtoken import 2k...");
	return getSoftTokenAndImport(window.p12_example_2k);
}
var testSoftToken3k = function() {
	log("Test Softtoken import 3k...");
	return getSoftTokenAndImport(window.p12_example_3k);
}
var testSoftToken4k = function() {
	log("Test Softtoken import 4k...");
	return getSoftTokenAndImport(window.p12_example_4k);
}

function softtoken() 
{
	testSoftToken2k()
		.then(testSoftToken3k)
		.then(testSoftToken4k);
}


function removeTestP12FromStore() {
	return SCWS.getSoftToken().then(
		function(token) {
			token.getObjects().then(function(objects) {
				
				var ckIdsToRemove = [];
				var subjectsToRemove = [
					"Ron Weasley", //  (2025)
					"Minerva McGonagall", //  (2025)
					"Lucius Malefoy"  //  (2025)
				];
				
				var objectNb = objects.length;

				for (var i = 0; i < objectNb; i++) {
					if (subjectsToRemove.includes(objects[i].subject)) {
						ckIdsToRemove.push(objects[i].ckId);
					}
				}
				
				var promises = [];
				for (var i = 0; i < objectNb; i++) {
					if (ckIdsToRemove.includes(objects[i].ckId) ) {
						promises.push(SCWS.destroyObjects(objects[i]));
					}
				}
				Promise.allSettled(promises).then(function() {
					log("test p12 removed from store");
				});
			})
		}
	);
}


function getSoftTokenAndImport(p12)
{
	return new Promise(function(resolve, reject) {
		SCWS.getSoftToken().then(function(token) {
			var attributes = {"label": "test"};
			var data = {"subjectName":{"CN":"Test Sign","OU":"Dev","O":"Idopte","L":"Vienne","ST":"Isere","C":"FR", "emailAddress":"test@idopte.fr"}};

			var formData = new FormData()
			var byteArray = new Uint8Array(p12.match(/.{2}/g).map(function(e) { return parseInt(e, 16);}));
			var blob = new Blob([byteArray], {type: "application/octet-stream"});
			formData.append("file", blob);
			formData.append("password", window.password);
			token.import(formData, attributes).then(function(objects) {
				log("Softoken imported");
				resolve();
			}).catch(function (err) {
				log(err);
				log("try 'SoftToken Clear test P12 From Store' first");
			});
		});
	});
}

function getCertStores() {
	log ("Test SCWS.getCertStores()");
	SCWS.getCertStores().then(function(stores){
		var storeNb = stores.length;
		log("Found " + storeNb + " stores");
		for (var i = 0; i<storeNb; i++) {
			var store = stores[i];
			store.getObjects().then(function(objects){
				log("Store name: "+ store.name);
				log("Store systemName: "+ store.systemName);
				var objectNb = objects.length;
				log("Found " + objectNb + " objects");
				for (var j=0; j<objectNb && j<5; j++) {
					log ("[" + j + "] Certificate: " + objects[j].subject);
					log ("[" + j + "]   issued by: "+ objects[j].issuer);
				}
				if (objectNb > 5) {
					log ("...");
				}
			})
		}
	}
	);
}

function callbackChipAuth(pubKey) {
	return pubKey;
}

function ChipAuth() {
	SCWS.readers[0].connect().then(function(token) {
    token.chipAuthenticate("534D","3","0.4.0.127.0.7.2.2.3.2.4",callbackChipAuth).then(function(){
		log("chip auth success"); 
	});
});
}

// SignEncrypt functions

/* Download file content from a blob client side */
function downloadBlobLocal(blob, fileName) {
	var a = document.createElement("a");
	document.body.appendChild(a);
	a.style = "display: none";
	var url = window.URL.createObjectURL(blob);
	a.href = url;
	a.download = fileName;
	a.click();
	window.URL.revokeObjectURL(url);
};

function isIE(){
	var ua = window.navigator.userAgent;
	var pf = window.navigator.platform ;
	return window.document.documentMode || (!!ua.match(/MSIE/i) && !!pf.match(/win/i));
}

/* Send a Blob object to web server running locally on 127.0.0.1:1234 */
function sendBlobToServer(blob, fileName) {
	var xhr = new XMLHttpRequest();
	xhr.open("POST", 'http://127.0.0.1:1234/test/saveFile.php?fileName=' + fileName);
	xhr.send(blob);
}

/* Download file content from a blob */
function downloadBlob(blob, fileName) {
	if (isIE()) {
		log ("Downloading " + fileName + " server side (scwsapi/javascript/test/downloads)")
		sendBlobToServer(blob, fileName)
	} else
		downloadBlobLocal(blob, fileName)
};

/* Get PDF from local storage as a Blob */
function getBlobFromLocal(inputID) {
	var selectedFile = document.getElementById(inputID).files[0];
	return selectedFile;
}

function getFormatName(format) {
	if (format == "PADES") {
		return "pades";
	}
	else if (format == "PKCS7") {
		return "p7";
	}
	else if (format == "CADES_ATTACHED") {
		return "cades_att";
	}
	else if (format == "CADES_DETACHED") {
		return "cades_det";
	}
	else if (format == "XADES_DETACHED") {
		return "xades";
	}
	else {
		console.log("unsupported format: "+ format);
		return "undefined";
	}
}


function getDownloadExtension(format) {
	if (format == "PADES"){
		return ".pdf";
	}
	else if (format == "PKCS7" || format == "CADES_ATTACHED" || format == "CADES_DETACHED") {
		return ".p7s";
	}
	else if (format == "XADES_DETACHED") {
		return ".xml";
	}
	else {
		console.log("unsupported format: "+ format);
		return;
	}
}

function signDoc() {
	var blob = getBlobFromLocal("inputFile");
	var options = {format: document.querySelector('input[name="signEncryptFormat"]:checked').value, hashAlgorithm: document.querySelector('input[name="signEncryptHashAlg"]:checked').value};
	if (options.format == "XADES_DETACHED"){
		for(var key in {fileName: "filename"}){
			if({fileName: "filename"}.hasOwnProperty(key)){
				options[key] = {fileName: "filename"}[key];
			}
		}
	}
	var options2 = fillOptions();
	SCWS.requestCertificates(function(certificates) {
		return certificates;
	},options2).then(function(certificates) {
		var index = 0;
		if (window.certlist != undefined && window.certlist.selectedIndex != undefined) {
			index = window.certlist.selectedIndex;
		}
		SCWS.requestPrivateKey(certificates[index], false, function(pKey, cert) {
			return SCWS.signDocument(blob, pKey, cert, options).then(function(signedBlob) {
				log("Signature has succeeded.");
				downloadBlob(
					signedBlob,
					"Test_sign_" + blob.name + "_" + getFormatName(options.format) + 
					"_" + options.hashAlgorithm + getDownloadExtension(options.format));
			}, function(err) {log(err);});
		},options2);
	});
}


function encryptDoc() {
	var options = fillOptions();
	var blob = getBlobFromLocal("inputFile");

	SCWS.requestCertificates(function(certificates) {
		var certsForEncryption = certificates;
		if (window.certlist != undefined && window.certlist.selectedIndex != undefined) {
			certsForEncryption = certificates[window.certlist.selectedIndex];
		}
		return SCWS.encryptDocument(blob, certsForEncryption).then(function(signedBlob) {
			log("Encryption has succeeded.");
			downloadBlob(signedBlob, "Test_encrypted_" + blob.name + ".p7");
		}, function(err) {log(err);});
		return certificates;
	},options);
}

function signEncryptDoc() {
	var blob = getBlobFromLocal("inputFile");
	var options = {hashAlgorithm: document.querySelector('input[name="signEncryptHashAlg"]:checked').value};
	var options2 = fillOptions();

	// var x509_raw = "308206B73082059FA003020102021211212643A594608C44C30238006F117D6796300D06092A864886F70D01010B0500306D310B300906035504061302465231123010060355040A13094B45594E4543544953310C300A060355040B130349435331173015060355040B130E3030303220343738323137333138312330210603550403131A4B45594E454354495320494353205155414C4946494544204341301E170D3230303531343131343832385A170D3233303531343131343832385A3081B9310B300906035504061302465231123010060355040A0C09484153484C4F4749433118301606035504610C0F4E545246522D34323134353731383531293027060355040C0C20496E646570656E64616E74732064697665727320707265737461746169726573311730150603550405130E4F543030303852335044303030383111300F060355042A0C085068696C69707065310D300B06035504040C04445241593116301406035504030C0D5068696C69707065204452415930820122300D06092A864886F70D01010105000382010F003082010A0282010100C12BDF410EFC5CF770AEAC60380661326F0032D7BD4A49A63F9478FF3711D69056A2D19D5D0930747EAF24DF2F6CBBDB7EC21BC4350B2B2F0ABEE5F161408C6153B0D52AB93B7265FCD5E975A7F7B475EC253C5C3BA9810E7812185A7D2C27FE6EC8BC2BF6CFA4D83822F12D17D77DC95286AA0B434074B2918A3DE06F66DC710B71D480E097A09C63FC63518478EB98A4D75B7C24B707174039E8F17C51D6148415403C9A5C0B73F359BBCF2D6FA29430DFB20DCB7C74C89C2A9B5A8586108DBD6CFC138486E6511B366B2BFDCF24FFF13E5E14D4E1F26B04D6E6D421383C523BE39DB3640F44E1356B3F9F6CBD9BD105F6162EA9D4EA6C309D47EE92F16DAB0203010001A3820302308202FE300E0603551D0F0101FF0404030206C03013060A2A864886F72F0101090204053003020101302A0603551D2504233021060A2B0601040182370A030C06092A864886F72F01010506082B0601050507030230650603551D20045E305C305A060C2B0601040181AD5A0209030F304A304806082B06010505070201163C68747470733A2F2F7777772E646F63757369676E2E66722F736F63696574652F706F6C697469717565732D64652D63657274696669636174696F6E73300C0603551D130101FF0402300030600603551D1F045930573055A053A051864F687474703A2F2F747275737463656E7465722D63726C2E63657274696669636174322E636F6D2F4B65796E65637469732F4B45594E45435449535F4943535F5155414C49464945445F43412E63726C30819306082B06010505070101048186308183303C06082B060105050730018630687474703A2F2F6F6373702D69642E6473662E646F63757369676E2E6E65742F6963735F7175616C69666965645F6361304306082B060105050730028637687474703A2F2F6372742E6473662E646F63757369676E2E6E65742F6B65796E65637469736963737175616C696669656463612E70376330260603551D11041F301D811B5048494C495050452E4452415940484153484C4F4749432E434F4D3037060A2A864886F72F01010901042930270201018622687474703A2F2F7473732E6473662E646F63757369676E2E6E65742F63657274696430819C06082B0601050507010304818F30818C3008060604008E4601013008060604008E460104304A060604008E4601053040303E163868747470733A2F2F7064732E6473662E646F63757369676E2E6E65742F6B65796E65637469736963737175616C696669656463612E7064661302454E3013060604008E4601063009060704008E46010601301506082B06010505070B023009060704008BEC490102301D0603551D0E041604140F90E2B99C6875B0AB1A4384293866149603D6E5301F0603551D23041830168014549745C1EA00C545A8CDDB82F87DCBF59041A078300D06092A864886F70D01010B050003820101004BCE22A1A19B2CA0D520B41B7B78C6CBAA9E79CA60D7C9C3EE3E78750189BCCAEB6C385F3891A8D6A7323D1482634D70886F6A7C2AA7E7B263B6875F390E45F5DEA486ACEC3F429F45B3BDF63EFDE406DF6929104AAC05D4CC66D5C9D8FDE3F4C44EE796AD64A8AE3C035F8721FEA558B0A3BF73A17CF2E88D204CC92066EB815F06A51852E4C5ADAC7328E830E2B48BF1880D39A4C58461CFB6490F047044ABB8D026E7789F70A5DF07CFC70D7A57A8C249E53F73161374448B3140F8F5C30E8FA6224FF69F159AFA2DB98D83674DDDD9E467E528FBDD667995CF0E4F7B90FDD40E26BEEC8963B31E538B8FFF44DEF6ABA09AB3962AE85D322CEDBB5479C749";
	// var byteArray = new Uint8Array(x509_raw.match(/.{2}/g).map(e => parseInt(e, 16)));
	// var blobCert = new Blob([byteArray], {type: "application/octet-stream"});
	// SCWS.createCertificate(blobCert).then(function(certificate) {
	// 	console.log(certificate);
	// });

	SCWS.requestCertificates(function(certificates) {
		return certificates;
	},options2).then(function(certificates) {
		var index = 0;
		if (window.certlist != undefined && window.certlist.selectedIndex != undefined) {
			index = window.certlist.selectedIndex;
		}

		SCWS.requestPrivateKey(certificates[index], false, function(pKey, cert) {
			return SCWS.signEncryptDocument(blob, pKey, cert, cert, options).then(function(signEncryptedBlob) {
				log("SignEncryption has succeeded.");
				downloadBlob(signEncryptedBlob, "Test_signEncrypted.p7");
			}, function(err) {log(err);});
		},options2);
	});
}


function verifyDoc() {
	var blob = getBlobFromLocal("inputFile");
	var originalBlob = undefined;
	var originalBlobCallback = undefined;

	var options = {format: document.querySelector('input[name="signEncryptFormat"]:checked').value};

	if (options.format == "CADES_DETACHED" || options.format == "XADES_DETACHED") {
		originalBlob = document.getElementById("originalFile").files[0];
	}

	if (options.format == "CADES_ATTACHED" || options.format == "PKCS7") {
		originalBlobCallback = downloadBlob;
	}

	SCWS.verifyDocument(blob, options, originalBlob, originalBlobCallback).then(function(verifResult){
		log("");
		log("Verification has succeeded.");
		log("globalSignatureValidity: " + verifResult.globalSignatureValidity);
		for (var i = 0; i < verifResult.signatories.length; i++) {
			log("");
			log("\nSignatory " + i + ":");
			log("Name: " + verifResult.signatories[i].certificate._subjectName);
			log("Certificate handle: " + verifResult.signatories[i].certificate._handle);
			log("signatureValidity: " + verifResult.signatories[i].signatureValidity);
			log("signerTrust: " + verifResult.signatories[i].signerTrust);
			log("cadesBESCompliance: " + verifResult.signatories[i].cadesBESCompliance);

		}
	}, function(err) {log(err);});
}

function decryptDoc() {
	var blob = document.getElementById("inputFile").files[0];
	var options = fillOptions();
	function callbackDecrypt(recipientsList, decryptWith) {
		SCWS.requestCertificates(function(certificates) {
			return SCWS.matchIssuerAndSerialsWithCerts(recipientsList, certificates).then(function(result) {
				for (var i = 0; i < certificates.length; i++) {
					const index = result.indexOf(certificates[i]._handle);
					if (index != -1)
						return certificates[i];
				}
			});
		},options).then(function(certificate) {
			return SCWS.requestPrivateKey(certificate, false, function(pKey, cert) {
				return decryptWith(pKey, cert);
			},options);
		});
	}

	SCWS.decryptDocument(blob, callbackDecrypt).then(function(blob){
		log("Decryption has succeeded.");
		downloadBlob(blob, "Test_decrypted");
	}, function(err) {log(err);});
}


function decryptVerifyDoc() {
	var blob = document.getElementById("inputFile").files[0];
	var options = fillOptions();
	function callbackDecrypt(recipientsList, decryptWith) {
		SCWS.requestCertificates(function(certificates) {
			return SCWS.matchIssuerAndSerialsWithCerts(recipientsList, certificates).then(function(result) {
				for (var i = 0; i < certificates.length; i++) {
					const index = result.indexOf(certificates[i]._handle);
					if (index != -1)
						return certificates[i];
				}
			});
		},options).then(function(certificate) {
			return SCWS.requestPrivateKey(certificate, false, function(pKey, cert) {
				return decryptWith(pKey, cert);
			},options);
		});
	}

	SCWS.decryptVerifyDocument(blob, callbackDecrypt, downloadBlob).then(function(verifResult){
		log("");
		log("DecryptVerification has succeeded.");
		log("globalSignatureValidity: " + verifResult.globalSignatureValidity);
		for (var i = 0; i < verifResult.signatories.length; i++) {
			log("");
			log("\nSignatory " + i + ":");
			log("Name: " + verifResult.signatories[i].certificate._subjectName);
			log("Certificate handle: " + verifResult.signatories[i].certificate._handle);
			log("signatureValidity: " + verifResult.signatories[i].signatureValidity);
			log("signerTrust: " + verifResult.signatories[i].signerTrust);
			log("cadesBESCompliance: " + verifResult.signatories[i].cadesBESCompliance);

		}
	}, function(err) {log(err);});
}

		</script>
	</head>

	<body>
		<table>
			<tr>
				<td>
					<form id="certificateform">
						<table>
							<tr>
								<td>
									Enable soft token:<br>
									<label><input type="radio" name="softToken" value="default" checked="checked">default</label><br>
									<label><input type="radio" name="softToken" value="true">true</label><br>
									<label><input type="radio" name="softToken" value="false">false</label><br/>
								</td>
								<td>
									Scan dialog:<br>
									<label><input type="radio" name="scandlg" value="nominal">nominal</label><br>
									<label><input type="radio" name="scandlg" value="synchronous">synchronous</label><br>
									<label><input type="radio" name="scandlg" value="asynchronous" checked="checked">asynchronous</label><br/>
								</td>
								<td>
									message:<br>
									<label><input type="radio" name="scanMessage" value="nominal" checked="checked">nominal</label><br>
									<label><input type="radio" name="scanMessage" value="customized">customized</label><br/>
								</td>
								<td id="scandlg">
									Scan dialog:<br/>
									<form id="scanform">
										<textarea id="scanfield" name="scan" cols="40" rows="4"></textarea><br>
									</form>
								</td>
							</tr>
						</table>
						<input type="button" onclick="requestCertificates()" value="request certificates"/><br/>
						Certificate to use:<br>
						<div style="display:inline-block;vertical-align:top;">
							<select style="height:8rem;" size="4" id="certificatelist" onchange="chooseCert()">
							</select>
						</div>
						<div id="certificatevalue" style="white-space:pre;display:inline-block;vertical-align:top;width:20em;height:8rem;overflow:auto;border:1px solid gray;">

						</div>
					</form>
			
					<p><form id="dataform">
						Input data:<br>
						<textarea id="datafield" name="data" cols="40" rows="8"></textarea><br>
					</form></p>
			
					<p><form id="loginform">
						Counter (0 or less equals infinity) :<input id="auto-login-counter" type="number"/>
						<input type="button" onclick="startAutoLogin()" value="startAutoLogin"/>
						<input type="button" onclick="stopAutoLogin()" value="stopAutoLogin"/><br/>
						Pin <span id="pinname"></span>:<br>
						<input id="pin" type="password" />
						<input type="button" onclick="login()" value="Login"/>
						<input type="button" onclick="loginWithPinDialog()" value="LoginWithPinDialog"/>
						<input type="button" onclick="loginWithPinPad()" value="LoginWithPinPad"/>
						<input type="button" onclick="RequestCredentialLoop()" value="Request credential loop"/><br>
						<input type="button" onclick="change()" value="Change Pin"/>
						<input type="button" onclick="init()" value="Init Pin"/>
						<input type="button" onclick="ChipAuth()" value="Chip authenticate"/><br>
						Key size <span id="keypara"></span>: <input id="keysize" type="number" />  
						Curve Name : <input id="keyparam" type="text" /><br>
						Container Name <span id="container"></span>: <input id="containername" type="text" />
						Keytype: <label><input type="radio" name="keytype" value="RSA" checked="checked">RSA</label>
						<label><input type="radio" name="keytype" value="EC">EC</label><br>
						<input type="button" onclick="generatekeypair()" value="Generate key pair"/><br>
					</form></p>
					
					<p>
						<div style="display:inline-block;text-align:center;margin:0.5em 2em;">
							<input type="button" onclick="sign()" value="Sign"/>
							<input type="button" onclick="verify()" value="Verify"/>
							<input type="button" onclick="encrypt()" value="Encrypt"/>
							<input type="button" onclick="decrypt()" value="Decrypt"/><br>
							<input type="button" onclick="removeTestP12FromStore()" value="SoftToken Clear test P12 From Store"/>
							<input type="button" onclick="softtoken()" value="SoftToken"/><br>
							<input type="button" onclick="bioEnroll()" value="Bio Enroll"/>
							<input type="button" onclick="ActivateBiometry()" value="Activate biometry"/><br>
							<input type="button" onclick="changePage()" value="ChangePage"/>
							<input type="button" onclick="getCertStores()" value="CertStores"/><br>
						</div>
						<div style="display:inline-block"><form id="hashcfgform">
							Hashing algorithm:<br>
							<label><input type="radio" name="hashalg" value="" checked="checked">None (hex block as input)</label><br>
							<label><input type="radio" name="hashalg" value="sha1">SHA-1</label><br>
							<label><input type="radio" name="hashalg" value="sha256">SHA-256</label>
						</form></div>
					</p>
				</td>
				<td>
					<p>Information logs:</p>
					<div id="infodiv" style="display:inline-block;vertical-align:top;width:100%;height:30rem;overflow:auto;border:1px solid gray;"></div>
				</td>
			</tr>
		</table>

		<h3>
			SignEncrypt high level entry points
		</h3>

		<p>
			<div style="display:inline-block">
				Input file(s) :<br>
				<label for="inputFile">Input file</label><br>
				<input type="file" id="inputFile" name="inputFile"/><br>
		
				<label for="originalFile">Original file (for detached verification)</label><br>
				<input type="file" id="originalFile" name="originalFile"/>
			</div>

			<div style="display:inline-block">
				Format:<br>
				<label><input type="radio" name="signEncryptFormat" value="PKCS7">PKCS7</label><br>
				<label><input type="radio" name="signEncryptFormat" value="CADES_ATTACHED">Attached CADES</label><br>
				<label><input type="radio" name="signEncryptFormat" value="CADES_DETACHED">Detached CADES</label><br>
				<label><input type="radio" name="signEncryptFormat" value="PADES">PADES</label><br>
				<label><input type="radio" name="signEncryptFormat" value="XADES_DETACHED">Detached XADES</label>
			</div>

			<div style="display:inline-block">
				Hashing algorithm:<br>
				<label><input type="radio" name="signEncryptHashAlg" value="sha1">SHA-1</label><br>
				<label><input type="radio" name="signEncryptHashAlg" value="sha256">SHA-256</label><br>
				<label><input type="radio" name="signEncryptHashAlg" value="sha384">SHA-384</label><br>
				<label><input type="radio" name="signEncryptHashAlg" value="sha512">SHA-512</label>
			</div>

			<div style="display:inline-block;text-align:center;margin:0.5em 2em;">
				<input type="button" onclick="signDoc()" value="SignDocument"/><br>
				<input type="button" onclick="encryptDoc()" value="EncryptDocument"/><br>
				<input type="button" onclick="signEncryptDoc()" value="SignEncryptDocument"/><br>
				<input type="button" onclick="verifyDoc()" value="VerifyDocument"/><br>
				<input type="button" onclick="decryptDoc()" value="DecryptDocument"/>
				<input type="button" onclick="decryptVerifyDoc()" value="DecryptVerifyDocument"/>
			</div>
		</p>

		<h3>Pin Policies Choice</h3>

		<div style="display: flex; gap: 30px; align-items: flex-start;">
			<div id="pinConstraints" style="display: flex; flex-direction: column;"></div>
			<div id="others" style="display: flex; flex-direction: column;"></div>
			<div id="others" style="display: flex; flex-direction: column;">
				Token Label:<br>
				<textarea id="labelfield" name="data" cols="30" rows="1">Token label</textarea><br>
				<input type="button" onclick="initToken()" value="initToken"/>
			</div>
		</div>

		<script>
		function createPolicySelect(labelText, selectId, min, max, divId) {
			container = document.getElementById(divId);

			wrapper = document.createElement('div');
			wrapper.style.marginBottom = "10px";

			label = document.createElement('label');
			label.setAttribute('for', selectId);
			label.textContent = labelText;
			label.style.marginRight = "10px";

			select = document.createElement('select');
			select.id = selectId;
			select.name = selectId;

			for (var value = min; value <= max; value++) {
				var option = document.createElement('option');
				option.value = value;
				option.textContent = value;
				select.appendChild(option);
			}

			wrapper.appendChild(label);
			wrapper.appendChild(select);
			container.appendChild(wrapper);
		}

		createPolicySelect("Min Upper Case:", "minUpperCase", 0, 16, 'pinConstraints');
		createPolicySelect("Min Lower Case:", "minLowerCase", 0, 16, 'pinConstraints');
		createPolicySelect("Min Digit:", "minDigit", 0, 16, 'pinConstraints');
		createPolicySelect("Min Special:", "minSpecial", 0, 16, 'pinConstraints');
		createPolicySelect("Max Identical Sequence:", "maxIdenticalSequence", 0, 16, 'pinConstraints');
		createPolicySelect("Max Inc Dec Sequence:", "maxIncDecSequence", 0, 16, 'pinConstraints');
		createPolicySelect("Min Length:", "minLength", 4, 16, 'pinConstraints');
		createPolicySelect("Max Length:", "maxLength", 4, 16, 'pinConstraints');
		createPolicySelect("Min Alphabetic:", "minAlphabetic", 0, 16, 'pinConstraints');
		createPolicySelect("Min Alphanumeric:", "minAlphanumeric", 0, 16, 'pinConstraints');

		createPolicySelect("Pin Duration:", "pinDuration", 0, 60, 'others');
		createPolicySelect("Pin Max Unlock:", "maxUnlock", 0, 255, 'others');
		createPolicySelect("Pin Max Attempts:", "maxTriesSoftware", 1, 15, 'others');
		createPolicySelect("Pin History Count:", "historyCount", 0, 255, 'others');
		</script>
	</body>
</html>