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 src="secureChannel.js"></script>
	<script>
		// ==========================================================================================================
		// ==========================================================================================================
		// ==
		// ==  Global Variable
		// ==
		// ==========================================================================================================
		// ==========================================================================================================
		var privateKeys = {};
		var tokenglobal;
		var csrData = { "subjectName": { "CN": "Test Sign", "OU": "Dev", "O": "Idopte", "L": "Vienne", "ST": "Isere", "C": "FR", "emailAddress": "test@idopte.fr" } };
		var dataContainerAttr = { "private": false, "modifiable": true, "ckLabel": "test", "application": "app_test", "value": new ArrayBuffer(16) };
		window.webappcert = "4@%Af+B8@(.y?Wx*D2NhkB7x6dA4AIQx>7Q$Aa9*ezeS)1egU^Ww]@ckyH}xmd%2AqzE)Y}A^qRCem<6BiX&xDgbgKuf!s?+y?WD#Bv*17fF3)VBz%n]fekjve?![ie?]ywgby:rxMOuniXJcvfFkare?]7kf!+syf!$KwenfcEA5]T9f!+jnh7*Qre?]4liX-xCg+rrZBz%n]fekdkgbQWsf!LgmgcdcBgC*T@BAzE.fe24le&2ppf!>pnfGq9Fgby:rxMOuniXJcvfFkare?]7kf!+QGf!$KwenfcEA5]T9f!+jnh7*Qre?]7miX-xCg+rrZBz%n]fekdkgbQWsf!Lgnf/?3AgC*T@BAzE.fe24le&2ppf!>pnf!$^Agby:rxMOuniXJcvfFkare?]7kf!>EBf!$KwenfcEA5]T9f!+jnh7*Qre?]7qiX-xCg+rrZBz%n]fekdkgbQWsf!Lgnh99DEgC*T@BAzE.fe24le&2ppf!>pnf/z6Egby:rxMOuniXJcvfFkare?]7kf!>QFf!$KwenfcEA5]T9f!+jnh7*Qre?]7uiX-xCg+rrZBz%n]fekdkgbQWsf!Lgnixw(IgC*T@BAzE.fe24le&2ppf!>pngb7:zgby:rxMOuniXJcvfFkare?]7kf!$EAf!$KwenfcEA5]T9f!+jnh7*Qre?]apiX-xCg+rrZBz%n]fekdkgbQWsf!LgogDElCgC*T@BAzE.fe24le&2ppf!>pngbI3Dgby:rxMOuniXJcvfFkare?]7kf!$QEf!$KwenfcEA5]T9f!+jnh7*Qre?]atiX-xCg+rrZBz%n]fekdkgbQWsf!Lgoh--VGgC*T@BAzE.fe24le&2ppf!>pngb}rHgby:rxMOuniXJcvfFkare?]7kf!$:If!$KwenfcEA5]T9f!+jnh7*Qre?]doiX-xCg+rrZBz%n]fekdkgbQWsf!Lgpf/?3AgC*T@BAzE.fe24le&2ppf!>pngCR0Cgby:rxMOuniXJcvfFkare?]7kf/7QDf!$KwenfcEA5]T9f!+jnh7*Qre?]dsiX-xCg+rrZBz%n]fekdkgbQWsf!Lgph99DEgC*T@BAzE.fe24le&2ppf!>pngD4oGgby:rxMOuniXJcvfFkare?]7kf/7:Hf!$KwenfcEA5]T9f!+jnh7*Qre?]dwiX-xCg+rrZBz%n]fekdkgbQWsf!Lgpixw(IgC*T@BAzE.fe24le&2ppf!>png+Z%Bgby:rxMOuniXJcvfFkare?]7kf/gQCf!$KwenfcEA5]T9f!+jnh7*Qre?]griX-xCg+rrZBz%n]fekdkgbQWsf!LgqgDElCgC*T@BAzE.fe24le&2ppf!>png=dlFgby:rxMOuniXJcvfFkare?]7kf/g:Gf!$KwenfcEA5]T9f!+jnh7*Qre?]gviX-xCg+rrZBz%n]fekdkgbQWsf!Lgqh--VGgC*T@BAzE.fe24le&2ppf!>png=NJJgby:rxMOuniXJcvfFkare?]7kf/g)Kf!$KwenfcEA5]T9f!+jnh7*Qre?]jqiX-xCg+rrZBz%n]fekdkgbQWsf!Lgrf/?3AgC*T@BAzE.fe24le&2ppf!>pnh8miEgby:rxMOuniXJcvfFkare?]7kf/p:Ff!$KwenfcEA5]T9f!+jnh7*Qre?]juiX-xCg+rrZBz%n]fekdkgbQWsf!Lgrh99DEgC*T@BAzE.fe24le&2ppf!>pnh8^MJgby:rxMOuniXJcvfFkare?]7kf/p<If!$KwenfcEA5]T9f!+jnh7*Qre?]jyiX-xCg+rrZBz%n]fekdkgbQWsf!Lgrixw(IgC*T@BAzE.fe24le&2ppf!>pnhzvfDgby:rxMOuniXJcvfFkare?]7kf/y:Ef!$KwenfcEA5]T9f!+jnh7*Qre?]mtiX-xCg+rrZBz%n]fekdkgbQWsf!LgsgDElCgC*T@BAzE.fe24le&2ppf!>pnhz^DHgby:rxMOuniXJcvfFkare?]7kf/y)If!$KwenfcEA5]T9f!+jnh7*Qre?]mxiX-xCg+rrZBz%n]fekdkgbQWsf!Lgsh--VGgC*T@BAzE.fe24le&2ppf!>pnhAi-Lgby:rxMOuniXJcvfFkare?]7kf/z3Mf!$KwenfcEA5]T9f!+jnh7*Qre?]psiX-xCg+rrZBz%n]fekdkgbQWsf!Lgmg=^uDgC*T@BAzE.fe24le&2ppf!>pnh.)AGgby:rxMOuniXJcvfFkare?]7kf!+yAf!$KwenfcEA5]T9f!+jnh7*Qre?]pwiX-xCg+rrZBz%n]fekdkgbQWsf!Lgth99DEgC*T@BAzE.fe24le&2ppf!>pnh-rYKgby:rxMOuniXJcvfFkare?]7kf/I3Lf!$KwenfcEA5]T9f!+jnh7*Qre?]pAiX-xCg+rrZBz%n]fekdkgbQWsf!Lgtixw(IgC*T@BAzE.fe24le&2ppf!>pni50xFgby:rxMOuniXJcvfFkare?]7kf/Q)Gf!$KwenfcEA5]T9f!+jnh7*Qre?]sviX-xCg+rrZBz%n]fekdkgbQWsf!LgugDElCgC*T@BAzE.fe24le&2ppf!>pni5AVJgby:rxMOuniXJcvfFkare?]7kf/R3Kf!$KwenfcEA5]T9f!+jnh7*Qre?]sziX-xCg+rrZBz%n]fekdkgbQWsf!Lgmh--VGgC*T@BAzE.fe24le&2ppf!>pni5&@Ngby:rxMOuniXJcvfFkare?]7kf/RfOf!$KwenfcEA5]T9f!+jnh7*Qre?]vuiX-xCg+rrZBz%n]fekdkgbQWsf!Lgvf/?3AgC*T@BAzE.fe24le&2ppf!>pniwJSIgby:rxMOuniXJcvfFkare?]7kf/.3Jf!$KwenfcEA5]T9f!+jnh7*Qre?]vyiX-xCg+rrZBz%n]fekdkgbQWsf!Lgvh99DEgC*T@BAzE.fe24le&2ppf!>pniw@]Mgby:rxMOuniXJcvfFkare?]7kf/.fNf!$KwenfcEA5]T9f!+jnh7*Qre?]vCiX-xCg+rrZBz%n]fekdkgbQWsf!Lgvixw(IgC*T@BAzE.fe24uga>pti4={zfFk7mh--VGgC*T@BAzE.fe24le&2ppf!>pnh.^uFgby:rxMOuniXJcvfFkare?]7kgb7yyf!$KwenfcEA5]T9f!+jnh7*Qre?]pviX-xCg+rrZBz%n]fekdkgbQWsf!LgmhAAMFgC*T@BAzE.fe24le&2ppf!>pni5-(Mgby:rxMOuniXJcvfFkare?]7kf!+ECf!$Kw.!(V$+<#r}}9:O$/0QjO^8yyJbhkXX(B/&-^t7*%2^A#v/l:0E3#I@b^6Wt[-yLgD1iX&n.Fq>tSScSGM*cSsn1hnO]N%/M)H?ND9cvy:/pZjV]hkiED6sny0Ugdes{/BdJ%8e..n68b4?!JNoaZ0YE/U-TpE>YsNORa{^3zjrg)wwT1>dJVY@xlAq:bk&2]Lt^Fe%FED6V[IK&PCc5:JXr^bLCnJLj!(Qdc#-{X2(^MpBCWTk2#rOt*:*&R2fu4XRFQvRQrh-0=0EvCl>k-l]<J5Ui4/P0)IfEJ1YJ:q?P%g3UKEgatsgf.A&dFa0<rNeq9CT+.S*lgcmjHKj1aEy(jQi@!$gR[i:6</yN}?ZoX>N>GGGJ{5/1LhRK{|MIIO/DCCDOSgAwIBAgIBADANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQGEwJGUjEdMBsGA1UEAwwUSWRvcHRlIFdlYkFwcENlcnQgQ0ExDzANBgNVBAoMBklkb3B0ZTAgFw0yNTA1MjMxMTUyNTlaGA8yMDUyMTIzMTIzNTkwMFowMTESMBAGA1UEAxMJTWF4aW1lRGV2MQ8wDQYDVQQKEwZJZG9wdGUxCjAIBgNVBAsTATAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDbbHFzPMOgflE7NLWtYXK0MV8xCLbSZ7qvUug0YdYSwiw9XDXe5uODG2ok9C1YrlSVuQ0O73CC07RqSys2XFsx6hW82JuwN+gEqJ+v+BKW3YQgT7NOzIRrvhucSo4UWrAZV6YXhd/1GZyYyyI1x3K1e1y/1p3Zw8nKAGeIP8vSQvSA7da124DOx860AXvF3eRUyonMEZagnPyueVVy0wodO1u1WRMjVN+f91FUhtbRf+u5am0vU+IZCE5Lv/ALghr0r3QqLAvHMlhe4uw9BmPCWQH7d/fdIbcZf7eDxWizei2dfl/dMh95f2RTGzE/Hnt56IVYvOT4kR5AU95gwCHAgMBAAGjggsPMIILCzAJBgNVHRMEAjAAMBcGA1UdJQQQMA4GDCsGAQQBgdgkAQICATBeBgNVHSAEVzBVMA8GDSsGAQQBgdgkAQIBAQEwDwYNKwYBBAGB2CQBAgEBAzAPBg0rBgEEAYHYJAECAQEEMA8GDSsGAQQBgdgkAQIBAQYwDwYNKwYBBAGB2CQBAgEBBzCCCoMGA1UdEQSCCnowggp2hhVodHRwOi8vMTI3LjAuMC4xOjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xMDE6MTIzNIYYaHR0cDovLzEwLjI1LjExLjEwMDoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTAyOjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xMDg6MTIzNIYYaHR0cDovLzEwLjI1LjExLjEwOToxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTEwOjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xMTE6MTIzNIYYaHR0cDovLzEwLjI1LjExLjExMjoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTEzOjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xMTQ6MTIzNIYYaHR0cDovLzEwLjI1LjExLjExNToxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTE2OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xMTc6MTIzNIYYaHR0cDovLzEwLjI1LjExLjExODoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTE5OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xMjA6MTIzNIYYaHR0cDovLzEwLjI1LjExLjEyMToxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTIyOjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xMjM6MTIzNIYYaHR0cDovLzEwLjI1LjExLjEyNDoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTI1OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xMjY6MTIzNIYYaHR0cDovLzEwLjI1LjExLjEyNzoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTI4OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xMjk6MTIzNIYYaHR0cDovLzEwLjI1LjExLjEzMDoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTMxOjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xMzI6MTIzNIYYaHR0cDovLzEwLjI1LjExLjEzMzoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTM0OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xMzU6MTIzNIYYaHR0cDovLzEwLjI1LjExLjEzNjoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTM3OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xMzg6MTIzNIYYaHR0cDovLzEwLjI1LjExLjEzOToxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTQwOjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xNDE6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE0MjoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTQzOjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xNDQ6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE0NToxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTQ2OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xNDc6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE0ODoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTQ5OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xNTA6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE1MToxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTUyOjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xNTM6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE1NDoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTU1OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xNTc6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE1NjoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTU4OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xNTk6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE2MDoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTYxOjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xNjI6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE2MzoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTY0OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xNjU6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE2NjoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTY3OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xNjg6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE2OToxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTcwOjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xMDQ6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE3MjoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTAzOjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xNzQ6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE3NToxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTc2OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xNzc6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE3ODoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTc5OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xODA6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE4MToxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTgyOjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xODM6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE4NDoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTg1OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xODY6MTIzNIYYaHR0cDovLzEwLjI1LjExLjEwNzoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTg4OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xODk6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE5MDoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTkxOjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xOTI6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE5MzoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTk0OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xOTU6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE5NjoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTk3OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xOTg6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE5OToxMjM0hhtodHRwOi8vMTkyLjE2OC4xNjAuMTE3OjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xNzE6MTIzNIYYaHR0cDovLzEwLjI1LjExLjIwMDoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTczOjEyMzSGGGh0dHA6Ly8xMC4yNS4xMS4xMDY6MTIzNIYYaHR0cDovLzEwLjI1LjExLjE4NzoxMjM0hhhodHRwOi8vMTAuMjUuMTEuMTA1OjEyMzQwDQYJKoZIhvcNAQELBQADggIBAF1qe3JRUr2uKsGSqA2RMif+YRVYoJT+i8iqXLLuRAXDcYbK5JOE5IKCmyMISFbk1ySNx8gPglyHapdEypNdgs3CcVS4GCzYgvW7fzwjTd00mSvgFbaMw6/N79T59cB/+k96I/jPZ++a63zNDdneNahXBff3pJsHZCM7hvLWmqN2CXAvqZz4V2NM6x1hEjrSVQVKqpgw5u9t7OQpcFMKAJ7c6Q3aRp/xiY4nouJDh/JRf5zcDEruQ8Xm9k7LI7wk4AbwVB0gY1cpQw1e7cyS40IoFRIyx3RvMXC3VYIkyKEIgg3jhaGRiodtW9rWvU8wJ/MJ9JeoqfUu8n1vu822Fl5LE51Xjb9Of2jO7FQ+UVVLe0B0RSmtEjo+CMJtJweX7zy9c1rFXFx4EaDqzZd5hh2j/QUJfp2v/jlMhbDlVOHwclgqe/o1s0iLIxpLB4Lbe6mfvq3KJ8PM0cxNdwX2b634UMYAqEWZFuD4Xbnd4IdPoGTkauhmRCfxcIMTaohy+pyxEicilqhtorpU91MWcmOoKO65C34a41uQgFCsDuroO6JHMgqHLMseWp4Lf/5wHoBOaSSGoNvh+47iLXDCO1wVcFfu8ly7LKWc8NPI0O61nP4EB7b+hG3nVdL11vUR+SU4hrYz5DWLqJJqSvuqQTRbqQCvDgL6ry8HW475BIfO";
		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";
		window.certlistObject = [];
		window.listAllObject = [];
		loadJSON('cards.json', function (file) {
			window.cardsjson = file;
			populateCardsDropdown();
		});
		loadFile('rootCA.crt', function (file) {
			window.caCert = forge.pki.certificateFromPem(file);
		});
		loadFile('rootCA.key', function (file) {
			window.caKey = forge.pki.privateKeyFromPem(file);
		});
		// ==========================================================================================================
		// ==  Global Variable END
		// ==========================================================================================================
		// ==========================================================================================================
		// ==========================================================================================================
		// ==
		// ==  Connect
		// ==
		// ==========================================================================================================
		// ==========================================================================================================
		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";
			function updateReaderConnection(reader) {
				disconnect();
				var readers = document.getElementById("readerSelect");
				for (var i = 0; i < readers.options.length; i++) {
					if (readers.options[i].text == reader.name) {
						readers.value = i;
					}
				}
				if (reader.status == "ok" && reader.cardPresent)
					connect();
			}
			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");
					populateReaderDropdown();
					SCWS.onreaderadded = function (reader) {
						log("Reader inserted: '" + reader.name + "'");
						populateReaderDropdown();
						updateReaderConnection(reader);
					}
					SCWS.onreaderremoved = function (reader) {
						log("Reader removed: '" + reader.name + "'");
						disconnect();
						populateReaderDropdown();
					}
					SCWS.onreaderstatechanged = function (reader) {
						updateReaderConnection(reader);
					}
					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();
			});
		}
		function disconnect() {
			if (tokenglobal == null)
				return;
			var objectList = document.getElementById("objectlist");
			objectList.clearChildren();
			window.certlist.clearChildren();
			log("Disconnecting ...");
			tokenglobal.disconnect()
				.then(function () {
					log("Disconnected to reader.");
					carre.style.backgroundColor = 'red';
					tokenglobal = null;
				})
				.catch(function (err) {
					log("Disconnection error:", err.message);
				});
		}
		function disconnectAll() {
			if (window.connection)
				return window.connection.disconnect();
			else
				return Promise.resolve();
		}
		function connect() {
			log("Connecting ...");
			SCWS.readers[document.getElementById("readerSelect").value].connect()
				.then(function (token) {
					tokenglobal = token;
					log("Connected to reader.");
					carre.style.backgroundColor = 'green';
				})
				.catch(function (err) {
					log("Connection error:", err.message);
				});
		}
		// ==========================================================================================================
		// ==  Connect END
		// ==========================================================================================================
		// ==========================================================================================================
		// ==========================================================================================================
		// ==
		// ==  Utils
		// ==
		// ==========================================================================================================
		// ==========================================================================================================
		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 log(msg) {
			window.infodiv.insertAdjacentHTML('beforeend', msg + '<br>');
			window.infodiv.scrollTop += 1000;
		}
		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);
		}
		function populateReaderDropdown() {
			const select = document.getElementById("readerSelect");
			select.innerHTML = "";
			var readers = SCWS.readers;
			for (var i = 0; i < readers.length; i++) {
				const option = document.createElement("option");
				option.value = i;
				option.text = readers[i]._name;
				select.appendChild(option);
			}
		}
		function populateCardsDropdown() {
			const select = document.getElementById("cardSelect");
			select.innerHTML = "";
			for (var i = 0; i < window.cardsjson.length; i++) {
				const option = document.createElement("option");
				option.value = i;
				option.text = window.cardsjson[i].cardId;
				if (i == 0)
					option.setAttribute('selected', 'selected');
				select.appendChild(option);
			}
		}
		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 getUserType() {
			var elt = document.querySelector('input[name="UserType"]: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 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 changePage() {
			localStorage.setItem("arr", JSON.stringify(SCWS.saveEnvironment()));
			window.location.href = "restore_environment.html";
		}
		/* 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 ".asice";
			}
			else {
				console.log("unsupported format: " + format);
				return;
			}
		}
		function checkEC() {
			var elt = document.getElementsByName("keytype");
			elt[1].checked = true;
		}
		function selectObjects(objectList) {
			for (var i = 0; i < objectList.options.length; i++) {
				if (objectList.options[i].selected) {
					objectList.options[i].setAttribute("selected", "selected");
				}
				else {
					objectList.options[i].removeAttribute("selected");
				}
			}
		}
		function selectObjectsAndUpdateAuthbuttons(objectList) {
			selectObjects(objectList);
			window.updatecardJson().then(function (cardJson) {
				mutualAuthButton = document.getElementById("mutualAuth");
				cardJson.mutualAuthenticate ? mutualAuthButton.disabled = false : mutualAuthButton.disabled = true;
				chipAuthButton = document.getElementById("chipAuth");
				cardJson.chipAuthenticate ? chipAuthButton.disabled = false : chipAuthButton.disabled = true;
				extAuthButton = document.getElementById("extAuth");
				cardJson.externalAuthenticate ? extAuthButton.disabled = false : extAuthButton.disabled = true;
			});
		}
		function loadJSON(file, callback) {
			var xobj = new XMLHttpRequest();
			xobj.overrideMimeType("application/json");
			xobj.open('GET', file, true);
			xobj.onreadystatechange = function () {
				if (xobj.readyState == 4 && xobj.status == "200") {
					callback(JSON.parse(xobj.responseText));
				}
			};
			xobj.send(null);
		}
		function checkConnection() {
			if (tokenglobal == null) {
				log("ERROR : card not connected");
				return false;
			}
			return true;
		}
		function base64ToArrayBuffer(base64) {
			const binary = atob(base64);
			const len = binary.length;
			const buffer = new ArrayBuffer(len);
			const bytes = new Uint8Array(buffer);
			for (let i = 0; i < len; i++) {
				bytes[i] = binary.charCodeAt(i);
			}
			return buffer;
		}
		// ==========================================================================================================
		// ==  Crypto Utils
		// ==========================================================================================================
		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 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>  " + 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 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 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 = Math.floor(Math.random() * 999999999).toString();
				certificate.validity.notBefore = new Date();
				certificate.validity.notAfter = new Date();
				certificate.validity.notAfter.setFullYear(certificate.validity.notBefore.getFullYear() + 5);
				certificate.setSubject(csr.subject.attributes);
				certificate.setIssuer(window.caCert.subject.attributes);
				certificate.setExtensions([{
					name: 'keyUsage',
					keyCertSign: false,
					digitalSignature: true,
					nonRepudiation: true,
					keyEncipherment: true,
					dataEncipherment: true
				}]);
				certificate.publicKey = csr.publicKey;
				// sign certificate with CA key
				certificate.sign(window.caKey);
				return certificate;
			});
		}
		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>  " + r);
		}
		// ==========================================================================================================
		// ==  Crypto Utils END
		// ==========================================================================================================
		// ==========================================================================================================
		// ==  Utils END
		// ==========================================================================================================
		// ==========================================================================================================
		// ==========================================================================================================
		// ==
		// ==  Login 
		// ==
		// ==========================================================================================================
		// ==========================================================================================================
		function login(pinNumber, userType, pinValue) {
			if (checkConnection() == false) return;
			pin = new SCWS.Pin(tokenglobal, pinNumber);
			log("Login...");
			pin.login(pinValue, userType === "SO").then(function () {
				log("Login successful");
			}, function (err) {
				log("ERROR: " + err.message);
			});
		}
		function loginWithPinDialog(pinNumber, userType) {
			if (checkConnection() == false) return;
			pin = new SCWS.Pin(tokenglobal, pinNumber);
			log("Login...");
			pin.login(false, userType === "SO").then(function () {
				log("Login successful");
			}, function (err) {
				log("ERROR: " + err.message);
			});
		}
		function loginWithPinPad(pinNumber, userType) {
			if (checkConnection() == false) return;
			pin = new SCWS.Pin(tokenglobal, pinNumber);
			log("Login...");
			pin.login(null, userType === "SO").then(function () {
				log("Login successful");
			}, function (err) {
				log("ERROR: " + err.message);
			});
		}
		function logout(pinNumber) {
			if (checkConnection() == false) return;
			pin = new SCWS.Pin(tokenglobal, pinNumber);
			log("Logout...");
			pin.logout().then(function () {
				log("Logout successful");
			}, function (err) {
				log("ERROR: " + err.message);
			});
		}
		function loginSpecial(specialType, pinValue) {
			if (checkConnection() == false) return;
			log("Login special...");
			tokenglobal.loginSpecial(pinValue, specialType).then(function () {
				log("Special login successful");
			}, function (err) {
				log("ERROR: " + err.message);
			});
		}
		// Function to Init Special PIN Currently no card support this.
		function initPinSpecial(specialType, pinValue) {
			if (checkConnection() == false) return;
			log("Init pin special...");
			tokenglobal.initPinSpecial(pinValue, specialType).then(function () {
				log("Init pin successful");
			}, function (err) {
				log("ERROR: " + err.message);
			});
		}
		// ==========================================================================================================
		// ==  Login END
		// ==========================================================================================================
		// ==========================================================================================================
		// ==========================================================================================================
		// ==
		// ==  Secure Channel
		// ==
		// ==========================================================================================================
		// ==========================================================================================================
		function externalAuthenticate() {
			if (checkConnection() == false) return;
			var externalCredentialSelected = document.getElementById('externalCredential');
			var params = window.getExternalAuthenticateParams(externalCredentialSelected.options[externalCredentialSelected.selectedIndex].value);
			if (!params) {
				log("ERROR: no parameter set for external authenticate on this card");
				return;
			}
			tokenglobal.externalAuthenticate(params.keyPath, params.algorithm, params.callback).then(function () {
				log("External authenticate done");
			}, function (err) {
				log("ERROR: " + err.message);
			});
		}
		function mutualAuthenticate() {
			if (checkConnection() == false) return;
			var params = window.getMutualAuthenticateParams();
			if (!params) {
				log("ERROR: no parameter set for mutual authenticate on this card");
				return;
			}
			tokenglobal.mutualAuthenticate(params.keyPath,
				params.algorithm,
				params.callback1,
				params.callback2).then(function () {
					log("Mutual authenticate done");
				}, function (err) {
					log("ERROR: " + err.message);
				});
		}
		function chipAuth() {
			if (checkConnection() == false) return;
			var params = window.getChipAuthenticateParams();
			if (!params) {
				log("ERROR: no parameter set for chip authenticate on this card");
				return;
			}
			tokenglobal.chipAuthenticate(params.keyPath,
				params.keyID,
				params.algorithm,
				params.callback).then(function () {
					log("Chip authenticate done");
				}, function (err) {
					log("ERROR: " + err.message);
				});
		}
		// ==========================================================================================================
		// ==  Secure Channel END
		// ==========================================================================================================
		// ==========================================================================================================
		// ==========================================================================================================
		// ==
		// ==  Card Administration
		// ==
		// ==========================================================================================================
		// ==========================================================================================================
		function generatekeypair() {
			if (checkConnection() == false) return;
			log("Generating key pair ....");
			if (getKeyType() == "RSA") {
				tokenglobal.generateKeyPair(document.getElementById("keysize").value,
					{ container: document.getElementById("containername").value, label: document.getElementById("label").value }).then(function () {
						log("key pair generated.");
					}, function (err) {
						log("key pair generation : " + err.message);
					});
			}
			if (getKeyType() == "EC") {
				tokenglobal.generateKeyPair(document.getElementById("keysize").value, {
					container: document.getElementById("containername").value, label: document.getElementById("label").value,
					parameters: { curveName: document.getElementById("keyparam").value }
				}).then(function () {
					log("key pair generated.");
				}, function (err) {
					log("key pair generation : " + err.message);
				});
			}
		}
		function generateKeyPairAndImportCSR() {
			if (checkConnection() == false) return;
			log("Generating key pair ....");
			if (getKeyType() == "RSA") {
				tokenglobal.generateKeyPair(document.getElementById("keysize").value, {
					container: document.getElementById("containername").value,
					label: document.getElementById("label").value
				}).then(function (keypair) {
					log("key pair generated.");
					createCSRandSign(keypair);
				}, function (err) {
					log("key pair generation : " + err.message);
				});
			}
			if (getKeyType() == "EC") {
				tokenglobal.generateKeyPair(document.getElementById("keysize").value, {
					container: document.getElementById("containername").value,
					label: document.getElementById("label").value,
					parameters: { curveName: document.getElementById("keyparam").value }
				}).then(function (keypair) {
					log("key pair generated.");
					createCSRandSign(keypair);
				}, function (err) {
					log("key pair generation : " + err.message);
				});
			}
		}
		function createCSRandSign(keypair) {
			csrData.subjectName.CN = document.getElementById("cn").value;
			csrData.subjectName.OU = document.getElementById("ou").value;
			csrData.subjectName.O = document.getElementById("o").value;
			csrData.subjectName.L = document.getElementById("l").value;
			csrData.subjectName.ST = document.getElementById("st").value;
			csrData.subjectName.C = document.getElementById("c").value;
			csrData.subjectName.emailAddress = document.getElementById("email").value;
			log("Generating CSR ...");
			keypair.privateKey.generateCSR(csrData).then(function (csrpath) {
				log("CSR generated");
				signCSR(csrpath).then(function (cert) {
					log("CSR signed.");
					var formData = new FormData();
					const pem = forge.pki.certificateToPem(cert);
					var blob = new Blob([pem]);
					formData.append("file", blob);
					tokenglobal.import(formData, {
						container: document.getElementById("containername").value,
						label: document.getElementById("label").value
					}).then(function (object) {
						log("certificate imported.");
					}, function (err) {
						log("Import certificate error: " + err.message);
					});
				}, function (err) {
					log("Sign CSR error: " + err.message);
				});
			}, function (err) {
				log("generate CSR error: " + err.message);
			});
		}
		function bioEnroll(pinNumber) {
			if (checkConnection() == false) return;
			pin = new SCWS.Pin(tokenglobal, pinNumber);
			log("Bio enroll...");
			pin.bioEnroll().then(
				function () {
					log("bio enroll successful");
				},
				function (err) {
					log("ERROR bio enroll: " + err.message);
				}
			);
		}
		function change(pinNumber) {
			if (checkConnection() == false) return;
			pin = new SCWS.Pin(tokenglobal, pinNumber);
			log("change pin...");
			pin.change().then(function () {
				log("change pin successful");
			}, function (err) {
				log("ERROR: " + err.message);
			});
		}
		function changeWithoutDialog(pinNumber, oldPassword, newPassword) {
			if (checkConnection() == false) return;
			pin = new SCWS.Pin(tokenglobal, pinNumber);
			log("change pin...");
			pin.change(oldPassword, newPassword).then(function () {
				log("change pin successful");
			}, function (err) {
				log("ERROR: " + err.message);
			});
		}
		function init(pinNumber) {
			if (checkConnection() == false) return;
			pin = new SCWS.Pin(tokenglobal, pinNumber);
			log("init pin...");
			pin.init().then(function () {
				log("Init pin successful");
			}, function (err) {
				log("ERROR: " + err.message);
			});
		}
		// Function to Init pin SO Currently no card support this.
		function initPinSo(pinNumber) {
			if (checkConnection() == false) return;
			pin = new SCWS.Pin(tokenglobal, pinNumber);
			log("init pin...");
			pin.initPinSo().then(function () {
				log("Init pin successful");
			}, function (err) {
				log("ERROR: " + err.message);
			});
		}
		function RequestCredentialLoop(pinNumber) {
			if (checkConnection() == false) return;
			pin = new SCWS.Pin(tokenglobal, pinNumber);
			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 startAutoLogin(pinNumber) {
			if (checkConnection() == false) return;
			pin = new SCWS.Pin(tokenglobal, pinNumber);
			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(pinNumber) {
			if (checkConnection() == false) return;
			pin = new SCWS.Pin(tokenglobal, pinNumber);
			log("stopAutoLogin ...");
			pin.stopAutoLogin().then(function () {
				disconnectAll().then(function () {
					log("stopAutoLogin successful");
				});
			}, function (err) {
				disconnectAll().then(function () {
					log("ERROR: " + err.message);
				});
			});
		}
		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 getAllObjects() {
			function pushObject(elt, index, objectList, object) {
				elt.id = index;
				objectList.appendChild(elt);
				window.listAllObject.push(object);
			}
			function deleteObjectFromList(object, objectList) {
				for (var i = 0; i < objectList.length; i++) {
					if (object === objectList[i]) {
						objectList.splice(i, 1);
						return;
					}
				}
			}
			if (checkConnection() == false) return;
			window.listAllObject = [];
			log("Getting all objects .....");
			var objectList = document.getElementById("objectlist");
			objectList.clearChildren();
			tokenglobal.getObjects().then(function (objects) {
				log("Got all objects");
				var objectListTemp = objects.slice();
				var index = 0;
				for (var i = 0; i < objectListTemp.length; i++) {
					var object = objectListTemp[i];
					if (object._type == "certificate") {
						var elt = document.createElement("option");
						elt.textContent = "Certificate: " + object.subject + " (" + object.issuer + ")";
						var ckId = object._ckId;
						pushObject(elt, index, objectList, object);
						index++;
						deleteObjectFromList(object, objects);
						for (var j = 0; j < objectListTemp.length; j++) {
							if (objectListTemp[j]._type != "certificate" && objectListTemp[j]._ckId == ckId) {
								var elt_key = document.createElement("option");
								elt_key.textContent = object.subject + " Key: " + objectListTemp[j]._keyAlg + " Size: " + objectListTemp[j]._keyLength + " private: " + objectListTemp[j]._private;
								pushObject(elt_key, index, objectList, objectListTemp[j]);
								index++;
								deleteObjectFromList(objectListTemp[j], objects);
							}
						}
					}
				}
				for (i = 0; i < objects.length; i++) {
					var object = objects[i];
					if (object._type != "certificate" && !object._ckId) {
						var elt = document.createElement("option");
						elt.textContent = "Label: " + object._ckLabel +" Object Type: " + object._type + '  /!\\ ne pas supprimer /!\\';
						pushObject(elt, index, objectList, object);
						index++;
					}
					else {
						if (!object._private) {
							var elt = document.createElement("option");
							elt.textContent = "Key: " + object._keyAlg + " Size: " + object._keyLength + " private: " + object._private;
							pushObject(elt, index, objectList, object);
							index++;
							var ckId = object._ckId;
							for (var j = 0; j < objects.length; j++) {
								if (objects[j]._ckId == ckId && objects[j]._private) {
									var elt_key = document.createElement("option");
									elt_key.textContent = "Key: " + objects[j]._keyAlg + " Size: " + objects[j]._keyLength + " private: " + objects[j]._private;
									pushObject(elt_key, index, objectList, objects[j]);
									index++;
									break;
								}
							}
						}
					}
				}
			}, function (err) {
				log("ERROR getting objects: " + err.message);
			});
		}
		function destroyObjects() {
			if (checkConnection() == false) return;
			var objectList = document.getElementById("objectlist");
			var listObjectsToDestroy = [];
			for (var i = 0; i < objectList.options.length; i++) {
				if (objectList.options[i].selected) {
					listObjectsToDestroy.push(listAllObject[i]);
				}
			}
			log("Destroying objects ....");
			SCWS.destroyObjects(listObjectsToDestroy).then(function (result) {
				log("Objects destroyed ....");
				getAllObjects();
			}, function (err) {
				log("ERROR destroying objects: " + err.message);
			});
		}
		function importCertificate(password) {
			if (checkConnection() == false) return;
			var blob = getBlobFromLocal("inputCert");
			var formData = new FormData();
			formData.append("file", blob);
			if (password)
				formData.append("password", password);
			log("Importing certificate ....");
			tokenglobal.import(formData, {
				container: document.getElementById("containername").value,
				label: document.getElementById("label").value
			}).then(function () {
				log("Certificate imported");
			}, function (err) {
				log("ERROR importing certificate: " + err.message);
			});
		}
		function getConstraints(pinNumber) {
			if (checkConnection() == false) return;
			pin = new SCWS.Pin(tokenglobal, pinNumber);
			log("Getting constraint ..... ");
			pin.getConstraints().then(function (constraints) {
				log("Constraint got ");
				log(JSON.stringify(constraints));
			}, function (err) {
				log("ERROR getting constraint: " + err.message);
			});
		}
		function getDetails() {
			if (checkConnection() == false) return;
			var objectList = document.getElementById("objectlist");
			log("Getting details ..... ");
			for (var i = 0; i < objectList.options.length; i++) {
				if (objectList.options[i].selected) {
					listAllObject[i].getDetails().then(function (details) {
						log("\n");
						log("Object:" + JSON.stringify(details));
						log("\n");
					}, function (err) {
						log("ERROR getting details: " + err.message);
					});
				}
			}
		}
		function createDataContainer() {
			if (checkConnection() == false) return;
			dataContainerAttr.application = document.getElementById("attrApp");
			dataContainerAttr.ckLabel = document.getElementById("attrLabel");
			dataContainerAttr.private = document.getElementById("attrPrivate").checked;
			dataContainerAttr.modifiable = document.getElementById("attrModifiable").checked;
			dataContainerAttr.value = base64ToArrayBuffer(document.getElementById("binInput").value.trim());
			
			tokenglobal.createDataContainer(dataContainerAttr).then(function (result) {
				log("Data container created");
			}, function (err) {
				log("ERROR createDataContainer: " + err.message);
			});
		}
		function nextPinChangeNotInAOD() {
			if (checkConnection() == false) return;
			tokenglobal.nextPinChangeNotInAOD().then(function () {
				log("nextPinChangeNotInAOD success");
			}, function (err) {
				log("ERROR nextPinChangeNotInAOD: " + err.message);
			});
		}
		// ==========================================================================================================
		// ==  Card Administration END
		// ==========================================================================================================
		// ==========================================================================================================
		// ==========================================================================================================
		// ==
		// ==  Document operation
		// ==
		// ==========================================================================================================
		// ==========================================================================================================
		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 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 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>  " + 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>  " + 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 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>  " + a );
					log("Decryption...");
					return pkey.decrypt(forge.util.binary.raw.decode(window.lastRet)).then(function (data) {
						data = SCWS.toHexString(data);
						log("Decryption done:<br>  " + data);
					});
				}
				catch (err) {
					log("ERROR: " + err.message);
				}
			}, options);
		}
		// ==========================================================================================================
		// ==  High level SignEncrypt
		// ==========================================================================================================
		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];
					}
				}
			}
			options["tsaUrl"] = document.getElementById("tsaurl").value
			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();
			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);
					if (Object.keys(verifResult.signatories[i].timestampToken).length !== 0) {
						log("\nTimestamp token: ");
						log("productionTime: " + verifResult.signatories[i].timestampToken.productionTime);
						log("dataIntegrity: " + verifResult.signatories[i].timestampToken.dataIntegrity);
						log("trusted: " + verifResult.signatories[i].timestampToken.trusted);
						log("producer: " + verifResult.signatories[i].timestampToken.producer._subjectName);
					}
				}
			}, 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); });
		}
		// ==========================================================================================================
		// ==  High level SignEncrypt END
		// ==========================================================================================================
		// ==========================================================================================================
		// ==  Document operation END
		// ==========================================================================================================
		// ==========================================================================================================
		// ==========================================================================================================
		// ==
		// ==  Format tools
		// ==
		// ==========================================================================================================
		// ==========================================================================================================
		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");
				pin.login(false, true).then(function () {
					token.initToken(constraints).then(function (response) {
						log("SUCCESS: Token initialized.");
					})
						.catch(function (error) {
							error("ERROR: Failed to init token... ", error);
						});
				})
					.catch(function (error) {
						error("ERROR: Failed to login... ", error);
					});
			})
		}
		// ==========================================================================================================
		// ==========================================================================================================
		// ==========================================================================================================
		// ==========================================================================================================
		// ==========================================================================================================
		// ==========================================================================================================
		// ==
		// ==  HTML
		// ==
		// ==========================================================================================================
		// ==========================================================================================================
	</script>
</head>
<body>
	<div id="infodiv" style="float: inline-end;width:300px;height:20rem;overflow:auto;border:1px solid gray;">
	</div>
	<h3>
		Readers Selection:
	</h3>
	<label for="readerSelect">Select a reader :</label>
	<select id="readerSelect" onchange="disconnect()">
		<option value="">-- Reader --</option>
	</select><br>
	<br>
	<div style="display: flex; gap: 15px; align-items: flex-start;">
		<input type="button" onclick="connect()" value="Connect Card" />
		<input type="button" onclick="disconnect()" value="Disconnect Card" />
		Card Connected : <div id="carre"></div>
	</div>
	<h3>
		Login:
	</h3>
	<div class="tabs">
		<div style="display: flex; gap: 15px; align-items: flex-start;" class="tab-registers">
			<button class="tab-btn active-tab" data-target="PinDlg">Login with Pin Dialog</button>
			<button class="tab-btn" data-target="Login">Direct Login</button>
			<button class="tab-btn" data-target="secureChannel">Secure Channel</button>
		</div><br>
		<div id="pinChoice" style="display: flex; gap: 15px; align-items: flex-start;">
			<label for="pinNumber">Select a PIN number :</label>
			<select id="pinNumber">
				<option value="0">0</option>
				<option value="1">1</option>
				<option value="2">2</option>
				<option value="3">3</option>
				<option value="4">4</option>
				<option value="5">5</option>
			</select>
			User Type: <label><input type="radio" name="UserType" value="User" checked="checked">User</label>
			<label><input type="radio" name="UserType" value="SO">Security officer</label>
		</div><br>
		<div class="tab-bodies">
			<div id="PinDlg" style="display:block;">
				<!-- ---------------------------------------------------------------------------------------- -->
				<!-- ---------------------------------------------------------------------------------------- -->
				<!-- -------------------------------- Login with Pin dialog --------------------------------- -->
				<!-- ---------------------------------------------------------------------------------------- -->
				<!-- ---------------------------------------------------------------------------------------- -->
				<div style="display: flex; gap: 20px; align-items: flex-start;">
					<input type="button"
						onclick="loginWithPinDialog(parseInt(document.getElementById('pinNumber').value), getUserType())"
						value="Login" />
					<input type="button"
						onclick="loginWithPinPad(parseInt(document.getElementById('pinNumber').value), getUserType(), document.getElementById('pin').value)"
						value="Login With PinPad" />
					<input type="button" onclick="logout(parseInt(document.getElementById('pinNumber').value))"
						value="Logout" />
				</div>
			</div>
			<div id="Login" style="display:none;">
				<!-- ------------------------------------------------------------------------------------------- -->
				<!-- ------------------------------------------------------------------------------------------- -->
				<!-- -------------------------------- Login without Pin dialog --------------------------------- -->
				<!-- ------------------------------------------------------------------------------------------- -->
				<!-- ------------------------------------------------------------------------------------------- -->
				Pin <span id="pinname"></span>:
				<div style="display: flex; gap: 20px; align-items: flex-start;">
					<input id="pin" type="password" />
					<input type="button"
						onclick="login(parseInt(document.getElementById('pinNumber').value), getUserType(), document.getElementById('pin').value)"
						value="Login" />
					<input type="button" onclick="logout(parseInt(document.getElementById('pinNumber').value))"
						value="Logout" />
				</div>
				<div style="display: flex; gap: 20px; align-items: flex-start;">
					<label for="pinSpecial">Select a Special PIN :</label>
					<select id="pinSpecial">
						<option value="ad">Admin</option>
						<option value="transport">Transport</option>
					</select>
					<input type="button"
						onclick="loginSpecial(document.getElementById('pinSpecial').value, document.getElementById('pin').value)"
						value="Login Special" />
					<!-- <input type="button"
						onclick="initPinSpecial(document.getElementById('pinSpecial').value, document.getElementById('pin').value)"
						value="Init Special" /> -->
				</div>
			</div>
			<div id="secureChannel" style="display:none;">
				<!-- ------------------------------------------------------------------------------------------- -->
				<!-- ------------------------------------------------------------------------------------------- -->
				<!-- ----------------------------------- Secure Channel ---------------------------------------- -->
				<!-- ------------------------------------------------------------------------------------------- -->
				<!-- ------------------------------------------------------------------------------------------- -->
				<div style="display: flex; gap: 20px; align-items: flex-start;">
					<label for="cardSelect">Select a card json :</label>
					<select id="cardSelect"
						onchange="selectObjectsAndUpdateAuthbuttons(document.getElementById('cardSelect'))">
						<option value="">-- Cards --</option>
					</select>
				</div><br>
				<div style="display: flex; gap: 20px; align-items: flex-start;">
					<input type="button" onclick="externalAuthenticate()" value="External Auth" id="extAuth" />
					<label for="externalCredential">Select a credential :</label>
					<select id="externalCredential"
						onchange="selectObjects(document.getElementById('externalCredential'))">
						<option value="Admin" selected="selected">Admin</option>
						<option value="unblock">unblock</option>
						<option value="fileManagement">fileManagement</option>
					</select>
				</div><br>
				<div style="display: flex; gap: 20px; align-items: flex-start;">
					<input type="button" onclick="mutualAuthenticate()" value="Mutual Auth" id="mutualAuth" />
					<input type="button" onclick="chipAuth()" value="Chip Auth" id="chipAuth" disabled />
				</div>
			</div>
		</div>
	</div>
	<h3>
		Operation:
	</h3>
	<div class="tabs">
		<div style="display: flex; gap: 30px; align-items: flex-start;" class="tab-registers">
			<button class="tab-btn active-tab" data-target="DocOpe">Document Operation</button>
			<button class="tab-btn" data-target="CardAdmin">Card Administration</button>
			<button class="tab-btn" data-target="Ftools">Format Tools</button>
		</div>
		<div class="tab-bodies">
			<!-- ------------------------------------------------------------------------------------ -->
			<!-- ------------------------------------------------------------------------------------ -->
			<!-- -------------------------------- Document Operation -------------------------------- -->
			<!-- ------------------------------------------------------------------------------------ -->
			<!-- ------------------------------------------------------------------------------------ -->
			<div id="DocOpe" style="display:block;">
				<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><br>
					<input type="button" onclick="requestCertificates()" value="request certificates" /><br /><br>
					<div id="side-by-side" style="display:flex; gap:40px;">
						<div style="display:flex; flex-direction: column; gap:8px;">
							<label for="certificatelist" style="font-weight:bold;">Certificate to use:</label>
							<select style="height:8rem;" size="4" id="certificatelist" onchange="chooseCert()"></select>
							<label for="certificatevalue" style="font-weight:bold; margin-top: 1rem;">Certificate
								value:</label>
							<div id="certificatevalue"
								style="white-space:pre; width:20em; height:8rem; overflow:auto; border:1px solid gray;">
							</div>
						</div>
						<div style="display:flex; flex-direction: column; gap:8px;">
							<label for="datafield" style="font-weight:bold;">Input data:</label>
							<textarea id="datafield" name="data" cols="40" rows="8"></textarea>
						</div>
					</div>
				</form>
				<h3>
					SignEncrypt low level entry points
				</h3>
				<div style="display: flex; gap: 30px; align-items: flex-start;">
					<div style="display: flex; gap: 30px; align-items: flex-start;">
						<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>
					</div><br>
					<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>
				</div>
				<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"
							checked="checked">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"
							checked="checked">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">
					TSA URL:<br>
					<input id="tsaurl" type="text">
				</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>
			</div>
			<!-- ------------------------------------------------------------------------------------- -->
			<!-- ------------------------------------------------------------------------------------- -->
			<!-- -------------------------------- Card Administration -------------------------------- -->
			<!-- ------------------------------------------------------------------------------------- -->
			<!-- ------------------------------------------------------------------------------------- -->
			<div id="CardAdmin" style="display:none;">
				<p>
				<form id="loginform">
					<div style="display: flex; gap: 15px; align-items: flex-start;">
						<label for="pinNumberAdministration">Select a PIN number :</label>
						<select id="pinNumberAdministration">
							<option value="0">0</option>
							<option value="1">1</option>
							<option value="2">2</option>
							<option value="3">3</option>
							<option value="4">4</option>
							<option value="5">5</option>
						</select>
					</div><br>
					<div style="display: flex; gap: 15px; align-items: flex-start;">
						<input type="button"
							onclick="change(parseInt(document.getElementById('pinNumberAdministration').value))"
							value="Change Pin" />
						<input type="button"
							onclick="init(parseInt(document.getElementById('pinNumberAdministration').value))"
							value="Init Pin" />
						<!-- <input type="button"
							onclick="initPinSo(parseInt(document.getElementById('pinNumberAdministration').value))"
							value="Init Pin SO" /> -->
						<input type="button"
							onclick="bioEnroll(parseInt(document.getElementById('pinNumberAdministration').value))"
							value="Bio Enroll" />
						<input type="button"
							onclick="getConstraints(parseInt(document.getElementById('pinNumberAdministration').value))"
							value="Get pin constraint" />
						<input type="button" onclick="nextPinChangeNotInAOD()" value="nextPinChangeNotInAOD" />
					</div><br>
					<div style="display: flex; gap: 15px; align-items: flex-start;">
						<label for="oldpin">Old Pin:</label>
						<input id="oldpin" type="password" />
						<label for="newpin">New Pin:</label>
						<input id="newpin" type="password" />
						<input type="button" onclick="changeWithoutDialog(parseInt(document.getElementById('pinNumberAdministration').value),
							document.getElementById('oldpin').value,
							document.getElementById('newpin').value)" value="Change Pin without dialog" />
					</div>
					<br>
				</form>
				<div style="display: flex; gap: 15px; align-items: flex-start;">
					<input type="button" onclick="getAllObjects()" value="Get Objects" />
					<input type="button" onclick="destroyObjects()" value="Destroy Objects" />
					<input type="button" onclick="getDetails()" value="Get Details" />
				</div>
				<div style="display:flex; flex-direction: column; gap:8px;">
					<label for="objectlist" style="font-weight:bold;">Objects on card:</label>
					<select multiple style="height:8rem;width: 30rem;" size="4" id="objectlist"
						onchange="selectObjects(document.getElementById('objectlist'))"></select>
				</div><br>
				<div style="display: flex; gap: 15px; align-items: flex-start;">
					<label for="inputCert">Input Certificate:</label>
					<input type="file" id="inputCert" name="inputCert" />
					<label for="password">Password:</label>
					<input id="password" type="text" />
					<input type="button" onclick="importCertificate(document.getElementById('password').value)"
						value="Import Certificate" />
				</div><br>
				<div style="display: flex; gap: 15px; align-items: flex-start;">
					<label for="keysize">Key size <span id="keypara"></span>:</label>
					<input id="keysize" type="number" />
					<label for="keyparam">Curve Name :</label>
					<select id="keyparam" onchange="checkEC()">
						<option value="" disabled selected>Choose a curve</option>
						<option value="prime256v1">prime256v1 (secp256r1)</option>
						<option value="secp384r1">secp384r1</option>
						<option value="secp521r1">secp521r1</option>
						<option value="secp256k1">secp256k1 PAS de TSL</option>
						<option value="brainpoolP256r1">brainpoolP256r1 PAS de TSL</option>
						<option value="brainpoolP384r1">brainpoolP384r1 PAS de TSL</option>
						<option value="brainpoolP384t1">brainpoolP384t1 PAS de TSL</option>
						<option value="brainpoolP160r1">brainpoolP160r1 PAS de TSL</option>
						<option value="brainpoolP160t1">brainpoolP160t1 PAS de TSL</option>
						<option value="secp224r1">secp224r1 PAS de TSL</option>
						<option value="secp224k1">secp224k1 PAS de TSL</option>
					</select>
				</div>
				<br>
				<script>
					const curveSizeMap = {
						"prime256v1": 256, "secp384r1": 384, "secp521r1": 521, "secp256k1": 256,
						"brainpoolP256r1": 256, "brainpoolP384r1": 384, "brainpoolP384t1": 384, "brainpoolP160r1": 160
						, "brainpoolP160t1": 160, "secp224r1": 224, "secp224k1": 224
					};
					document.getElementById("keyparam").addEventListener("change", function () {
						const selectedCurve = this.value;
						const size = curveSizeMap[selectedCurve] || '';
						document.getElementById("keysize").value = size;
					});
				</script>
				<div style="display: flex; gap: 15px; align-items: flex-start;">
					Container Name: <input id="containername" type="text" value="" />
					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>
				</div>
				Label: <input id="label" type="text" value="test" />
				<div style="margin-top: 1em;margin-left: 2rem;">
					<label for="csr">CSR:</label><br>
					<label for="cn">Common Name (CN):</label>
					<input id="cn" type="text" />
					<label for="ou">Organizational unit name (OU):</label>
					<input id="ou" type="text" />
					<label for="o">Organization name (O):</label>
					<input id="o" type="text" />
					<label for="l">Locality name (L):</label>
					<input id="l" type="text" />
					<label for="st">State or province (ST):</label>
					<input id="st" type="text" />
					<label for="c">Country (C):</label>
					<input id="c" type="text" />
					<label for="email">Email:</label>
					<input id="email" type="text" value="test@idopte.fr" />
				</div>
				<br>
				<input type="button" onclick="generateKeyPairAndImportCSR()" value="Generate key pair + Import CSR" />
				</p>
				<div style="display: flex; gap: 15px; align-items: flex-start;">
					<input type="button" onclick="createDataContainer()" value="Create Data container" />
					<input type="checkbox" id="attrPrivate" name="attrPrivate" />
					<label for="attrPrivate">Private</label>
					<input type="checkbox" id="attrModifiable" name="attrModifiable" checked />
					<label for="attrModifiable">Modifiable</label>
				</div>
				<div style="display: flex; gap: 15px; align-items: flex-start;">
					<label for="attrLabel">Label:</label>
					<input id="attrLabel" type="text" value="" />
					<label for="attrApp">App name:</label>
					<input id="attrApp" type="text" value="" />
					<label for="binInput">Value:</label>
					<textarea id="binInput" placeholder="Transforme base64 en buffer"></textarea>
				</div>
				<br>
				<div style="display: flex; gap: 15px; align-items: flex-start;">
					Counter (0 or less equals infinity) :<input id="auto-login-counter" type="number" />
					<input type="button"
						onclick="startAutoLogin(parseInt(document.getElementById('pinNumberAdministration').value))"
						value="startAutoLogin" />
					<input type="button"
						onclick="stopAutoLogin(parseInt(document.getElementById('pinNumberAdministration').value))"
						value="stopAutoLogin" /><br />
					<input type="button"
						onclick="RequestCredentialLoop(parseInt(document.getElementById('pinNumberAdministration').value))"
						value="Request credential loop" /><br>
				</div><br>
				<div style="display: flex; gap: 15px; align-items: flex-start;">
					<input type="button" onclick="removeTestP12FromStore()"
						value="SoftToken Clear test P12 From Store" />
					<input type="button" onclick="softtoken()" value="SoftToken" />
					<input type="button" onclick="getCertStores()" value="CertStores" />
				</div><br>
				<input type="button" onclick="changePage()" value="ChangePage" />
			</div>
			<!-- ------------------------------------------------------------------------------ -->
			<!-- ------------------------------------------------------------------------------ -->
			<!-- -------------------------------- Format Tools -------------------------------- -->
			<!-- ------------------------------------------------------------------------------ -->
			<!-- ------------------------------------------------------------------------------ -->
			<div id="Ftools" style="display:none;">
				<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>
			</div>
		</div>
	</div>
	<script>
		var buttons = document.querySelectorAll('.tab-btn');
		var tabs = document.querySelectorAll('.tab-bodies > div');
		for (var i = 0; i < buttons.length; i++) {
			buttons[i].onclick = (function (index) {
				return function () {
					var firstTab = index <= 2;
					for (var j = (firstTab ? 0 : 3); j < (firstTab ? 3 : buttons.length); j++) {
						buttons[j].className = buttons[j].className.replace(/\bactive-tab\b/, '');
					}
					buttons[index].className += ' active-tab';
					for (var k = firstTab ? 0 : 3; k < (firstTab ? 3 : tabs.length); k++) {
						tabs[k].style.display = 'none';
					}
					var target = buttons[index].getAttribute('data-target');
					document.getElementById(target).style.display = 'block';
					if (document.getElementById(target).id == "secureChannel")
						document.getElementById("pinChoice").style.display = 'none';
					else if (firstTab)
						document.getElementById("pinChoice").style.display = 'flex';
				};
			})(i);
		}
	</script>
	<style>
		.tabs .tab-registers {
			display: flex;
			background-color: RGB(255, 255, 255);
		}
		.tabs button {
			padding: 0.5em;
			background-color: RGB(255, 255, 255);
		}
		.tabs .tab-registers button:hover {
			cursor: pointer;
		}
		.tabs button.active-tab {
			background-color: rgb(235, 235, 235);
		}
		table {
			border-collapse: separate;
			border-spacing: 30px 0;
		}
		body {
			padding-left: 10px;
		}
		#carre {
			width: 15px;
			height: 15px;
			background-color: rgb(255, 2, 2);
			border: 1px solid gray;
		}
		input {
			font-family: 'Roboto', sans-serif;
			font-size: 14px;
		}
		input[type="text"],
		input[type="password"] {
			font-weight: bold;
			display: block;
			box-sizing: border-box;
			width: auto;
			max-width: 10em;
			border: none;
			border-bottom: 2px solid #c0c0c0;
			margin: 0.2em 0;
			padding: 0.2em 0.2em 0 0.2em;
		}
		input[type="text"]:focus,
		input[type="password"]:focus {
			border-bottom-color: #4c83e9;
			outline: none;
		}
		label input[type="text"],
		label input[type="password"] {
			display: inline-block;
			max-width: 10em;
		}
	</style>
</body>
</html>