bug 964: Usability improvement: if there's only one keysize to select, pre-fill
[cacert-devel.git] / www / keygenIE.js
1 var CAcert_keygen_IE = function () {
2
3 /// Makes a new DOM text node
4 var textnode = function (text) {
5 return document.createTextNode(text);
6 }
7
8
9 /// makes a new <p> element
10 var paragraph = function (text) {
11 var paragraph = document.createElement("p");
12 paragraph.appendChild(textnode(text));
13 return paragraph;
14 }
15
16
17 /// makes a new <pre> elemtent
18 var pre = function (text) {
19 var pre = document.createElement("pre");
20 pre.appendChild(textnode(text));
21 return pre;
22 }
23
24
25 /// makes a new <option> element
26 var option = function (text, value) {
27 var option = document.createElement("option");
28 if (value !== undefined) {
29 option.setAttribute("value", value);
30 }
31 option.appendChild(textnode(text));
32 return option;
33 }
34
35
36 /// Removes all child nodes from the element
37 var removeChildren = function (element) {
38 element.innerHTML = "";
39 }
40
41
42 /// Show error message to user from exception
43 var showError = function (message, exception) {
44 window.alert(message +
45 "\n\nError: " + exception.message +
46 " (0x" + (1 << 32 + exception.number).toString(16) + " / " +
47 exception.number + ")");
48 }
49
50
51
52 // Get important elements from the DOM
53 var form = document.getElementById("CertReqForm");
54 var securityLevel = document.getElementById("SecurityLevel");
55 var customSettings = document.getElementById("customSettings");
56 var provider = document.getElementById("CspProvider");
57 var algorithm = document.getElementById("algorithm");
58 var algorithmParagraph = document.getElementById("algorithmParagraph");
59 var keySize = document.getElementById("keySize");
60 var keySizeMin = document.getElementById("keySizeMin");
61 var keySizeMax = document.getElementById("keySizeMax");
62 var keySizeStep = document.getElementById("keySizeStep");
63 var genReq = document.getElementById("GenReq");
64 var csr = document.getElementById("CSR");
65 var noActiveX = document.getElementById("noActiveX");
66 var generatingKeyNotice = document.getElementById("generatingKeyNotice");
67 var createRequestErrorChooseAlgorithm = document.getElementById("createRequestErrorChooseAlgorithm");
68 var createRequestError = document.getElementById("createRequestError");
69 var invalidKeySizeError = document.getElementById("invalidKeySizeError");
70 var unsupportedPlatformError = document.getElementById("unsupportedPlatformError");
71
72
73 /// Initialise the CertEnroll code (Vista and higher)
74 /// returns false if initialisation fails
75 var initCertEnroll = function () {
76 var factory = null;
77 var providerList = null;
78 var cspStats = null;
79
80 // Try to initialise the ActiveX element. Requires permissions by the user
81 try {
82 factory = new ActiveXObject(
83 "X509Enrollment.CX509EnrollmentWebClassFactory");
84 if (!factory) {
85 throw {
86 name: "NoObjectError",
87 message: "Got null at object creation"
88 };
89 }
90
91 // also try to create a useless object here so the library gets
92 // initialised and we don't need to check everytime later
93 factory.CreateObject("X509Enrollment.CObjectId");
94
95 form.style.display = "";
96 noActiveX.style.display = "none";
97 } catch (e) {
98 return false;
99 }
100
101
102 /// Get the selected provider
103 var getProvider = function () {
104 var providerIndex = provider.options[provider.selectedIndex].value;
105 return providerList.ItemByIndex(providerIndex);
106 }
107
108
109 /// Get the selected algorithm
110 var getAlgorithm = function () {
111 var algorithmIndex = algorithm.options[algorithm.selectedIndex].value;
112 return alg = cspStats.ItemByIndex(algorithmIndex).CspAlgorithm;
113 }
114
115
116 /// Get the selected key size
117 var getKeySize = function () {
118 var alg = getAlgorithm();
119
120 var bits = parseInt(keySize.value, 10);
121 if ( bits < alg.MinLength || bits > alg.MaxLength ||
122 (alg.IncrementLength &&
123 (bits - alg.MinLength) % alg.IncrementLength !== 0)
124 )
125 {
126 return false;
127 }
128
129 return bits;
130 }
131
132
133 /// Fill the key size list
134 var getKeySizeList = function () {
135 if (!cspStats) {
136 return false;
137 }
138
139 var alg = getAlgorithm();
140
141 // HTML5 attributes
142 keySize.setAttribute("min", alg.MinLength);
143 keySize.setAttribute("max", alg.MaxLength);
144 keySize.setAttribute("step", alg.IncrementLength);
145 keySize.setAttribute("value", alg.DefaultLength);
146 keySize.value = ""+alg.DefaultLength;
147
148 // ugly, but buggy otherwise if done with text nodes
149 keySizeMin.innerHTML = alg.MinLength;
150 keySizeMax.innerHTML = alg.MaxLength;
151 keySizeStep.innerHTML = alg.IncrementLength;
152
153 return true;
154 }
155
156
157 /// Fill the algorithm list
158 var getAlgorithmList = function () {
159 var i;
160
161 if (!providerList) {
162 return false;
163 }
164
165 var csp = getProvider();
166
167 cspStats = providerList.GetCspStatusesFromOperations(
168 0x1c, //XCN_NCRYPT_ANY_ASYMMETRIC_OPERATION
169 //0x10, //XCN_NCRYPT_SIGNATURE_OPERATION
170 //0x8, //XCN_NCRYPT_SECRET_AGREEMENT_OPERATION
171 //0x4, //XCN_NCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION
172 csp
173 );
174
175 removeChildren(algorithm);
176 for (i = 0; i < cspStats.Count; i++) {
177 var alg = cspStats.ItemByIndex(i).CspAlgorithm;
178 algorithm.appendChild(option(alg.Name, i));
179 }
180
181 return getKeySizeList();
182 }
183
184
185 /// Fill the crypto provider list
186 var getProviderList = function () {
187 var i;
188
189 var csps = factory.CreateObject("X509Enrollment.CCspInformations");
190
191 // Get provider information
192 csps.AddAvailableCsps();
193
194 removeChildren(provider);
195
196 for (i = 0; i < csps.Count; i++) {
197 var csp = csps.ItemByIndex(i);
198 provider.appendChild(option(csp.Name, i));
199 }
200
201 providerList = csps;
202
203 return getAlgorithmList();
204 }
205
206
207 /// Generate a key and create and submit the actual CSR
208 var createCSR = function () {
209 var providerName, algorithmOid, bits;
210
211 var level = securityLevel.options[securityLevel.selectedIndex];
212 if (level.value === "custom") {
213 providerName = getProvider().Name;
214 var alg = getAlgorithm();
215 algorithmOid = alg.GetAlgorithmOid(0, 0)
216 bits = getKeySize();
217 if (!bits) {
218 window.alert(invalidKeySizeError.innerHTML);
219 return false;
220 }
221
222 } else {
223
224 providerName = "Microsoft Software Key Storage Provider";
225
226 algorithmOid = factory.CreateObject("X509Enrollment.CObjectId");
227 algorithmOid.InitializeFromValue("1.2.840.113549.1.1.1"); // RSA
228 //"1.2.840.10040.4.1" == DSA
229 //"1.2.840.10046.2.1" == DH
230
231 if (level.value === "high") {
232 bits = 4096;
233 } else { // medium
234 bits = 2048;
235 }
236 }
237
238
239 var privateKey = factory.CreateObject("X509Enrollment.CX509PrivateKey");
240 privateKey.ProviderName = providerName;
241 privateKey.Algorithm = algorithmOid;
242 privateKey.Length = bits;
243 privateKey.KeyUsage = 0xffffff; // XCN_NCRYPT_ALLOW_ALL_USAGES
244
245 var request = factory.CreateObject(
246 "X509Enrollment.CX509CertificateRequestPkcs10");
247 request.InitializeFromPrivateKey(
248 1, // ContextUser
249 privateKey,
250 "" // don't use a template
251 );
252
253 var enroll = factory.CreateObject("X509Enrollment.CX509Enrollment");
254 enroll.InitializeFromRequest(request);
255
256 generatingKeyNotice.style.display = "";
257
258
259 // The request needs to be created after we return so the "please wait"
260 // message gets rendered
261 var createCSRHandler = function () {
262 try {
263 csr.value = enroll.CreateRequest(0x1); //XCN_CRYPT_STRING_BASE64
264 form.submit();
265 } catch (e) {
266 showError(createRequestErrorChooseAlgorithm.innerHTML, e);
267 }
268
269 generatingKeyNotice.style.display = "none";
270 }
271
272 window.setTimeout(createCSRHandler, 0);
273
274 // Always return false, form is submitted by deferred method
275 return false;
276 }
277
278
279 /// Call if securityLevel has changed
280 var refreshSecurityLevel = function () {
281 var level = securityLevel.options[securityLevel.selectedIndex];
282 if (level.value === "custom") {
283 getProviderList();
284 customSettings.style.display = "";
285 } else {
286 customSettings.style.display = "none";
287 }
288 }
289
290 securityLevel.onchange = refreshSecurityLevel;
291 provider.onchange = getAlgorithmList;
292 algorithm.onchange = getKeySizeList;
293 genReq.onclick = createCSR;
294
295 return true;
296 } // end of initCertEnroll()
297
298
299 /// Initialise Xenroll code (XP and lower)
300 /// returns false if initialisation fails
301 var initXEnroll = function () {
302 cenroll = null;
303
304 providerTypes = Array(
305 1, //PROV_RSA_FULL
306 2, //PROV_RSA_SIG
307 3, //PROV_DSS
308 4, //PROV_FORTEZZA
309 5, //PROV_MS_EXCHANGE
310 6, //PROV_SSL
311 12, //PROV_RSA_SCHANNEL
312 13, //PROV_DSS_DH
313 14, //PROV_EC_ECDSA_SIG
314 15, //PROV_EC_ECNRA_SIG
315 16, //PROV_EC_ECDSA_FULL
316 17, //PROV_EC_ECNRA_FULL
317 18, //PROV_DH_SCHANNEL
318 20, //PROV_SPYRUS_LYNKS
319 21, //PROV_RNG
320 22, //PROV_INTEL_SEC
321 23, //PROV_REPLACE_OWF
322 24 //PROV_RSA_AES
323 );
324
325 algClasses = Array(
326 1 << 13, //ALG_CLASS_SIGNATURE
327 //2 << 13, //ALG_CLASS_MSG_ENCRYPT
328 //3 << 13, //ALG_CLASS_DATA_ENCRYPT
329 //4 << 13, //ALG_CLASS_HASH
330 5 << 13 //ALG_CLASS_KEY_EXCHANGE
331 );
332
333 // Try to initialise the ActiveX element.
334 try {
335 cenroll = new ActiveXObject("CEnroll.CEnroll");
336
337 if (!cenroll) {
338 throw {
339 name: "NoObjectError",
340 message: "Got null at object creation"
341 };
342 }
343
344 form.style.display = "";
345 //algorithmParagraph.style.display = "none";
346 algorithm.disabled = true;
347 noActiveX.style.display = "none";
348 } catch (e) {
349 return false;
350 }
351
352
353 /// Get the name of the selected provider
354 var getProviderName = function () {
355 return provider.options[provider.selectedIndex].text;
356 }
357
358
359 /// Get the type of the selected provider
360 var getProviderType = function () {
361 return parseInt(provider.options[provider.selectedIndex].value, 10);
362 }
363
364
365 var refreshProvider = function () {
366 cenroll.ProviderName = getProviderName();
367 cenroll.ProviderType = getProviderType();
368 }
369
370
371 /// Get the ID of the selected algorithm
372 var getAlgorithmId = function () {
373 return parseInt(
374 algorithm.options[algorithm.selectedIndex].value, 10);
375 }
376
377
378 /// Minimum bit length for exchange keys
379 var getMinExKeyLength = function () {
380 refreshProvider();
381
382 try {
383 return cenroll.GetKeyLen(true, true);
384 } catch (e) {
385 return false;
386 }
387 }
388
389
390 /// Maximum bit length for exchange keys
391 var getMaxExKeyLength = function () {
392 refreshProvider();
393
394 try {
395 return cenroll.GetKeyLen(false, true);
396 } catch (e) {
397 return false;
398 }
399 }
400
401
402 /// Step size for exchange keys
403 /// This might not be available on older platforms
404 var getStepExKeyLength = function () {
405 refreshProvider();
406
407 try {
408 return cenroll.GetKeyLenEx(3, 1);
409 } catch (e) {
410 return false;
411 }
412 }
413
414
415 /// Minimum bit length for signature keys
416 var getMinSigKeyLength = function () {
417 refreshProvider();
418
419 try {
420 return cenroll.GetKeyLen(true, false);
421 } catch (e) {
422 return false;
423 }
424 }
425
426
427 /// Maximum bit length for signature keys
428 var getMaxSigKeyLength = function () {
429 refreshProvider();
430
431 try {
432 return cenroll.GetKeyLen(false, false);
433 } catch (e) {
434 return false;
435 }
436 }
437
438
439 /// Step size for signature keys
440 /// This might not be available on older platforms
441 var getStepSigKeyLength = function () {
442 refreshProvider();
443
444 try {
445 return cenroll.GetKeyLenEx(3, 2);
446 } catch (e) {
447 return false;
448 }
449 }
450
451
452 /// Get the selected key size
453 var getKeySize = function () {
454 var bits = parseInt(keySize.value, 10);
455 if ( bits < getMinSigKeyLength() || bits > getMaxSigKeyLength() ||
456 (getStepSigKeyLength() &&
457 (bits - getMinSigKeyLength()) %
458 getStepSigKeyLength() !== 0)
459 )
460 {
461 return false;
462 }
463
464 return bits;
465 }
466
467
468 var getKeySizeLimits = function () {
469 // HTML5 attributes
470 keySize.setAttribute("min", getMinSigKeyLength());
471 keySize.setAttribute("max", getMaxSigKeyLength());
472 if (getStepSigKeyLength()) {
473 keySize.setAttribute("step", getStepSigKeyLength());
474 }
475 //keySize.setAttribute("value", alg.DefaultLength);
476 //keySize.value = ""+alg.DefaultLength;
477
478 // ugly, but buggy otherwise if done with text nodes
479 keySizeMin.innerHTML = getMinSigKeyLength();
480 keySizeMax.innerHTML = getMaxSigKeyLength();
481 keySizeStep.innerHTML = getStepSigKeyLength();
482
483 if (getMinSigKeyLength() === getMaxSigKeyLength()) {
484 keySize.value = getMaxSigKeyLength();
485 }
486
487 return true;
488 }
489
490
491 /// Fill the algorithm selection box
492 var getAlgorithmList = function () {
493 var i, j;
494
495 refreshProvider();
496
497 removeChildren(algorithm);
498
499 for (i = 0; i < algClasses.length; ++i) {
500 for (j = 0; true; ++j) {
501 try {
502 var algId = cenroll.EnumAlgs(j, algClasses[i]);
503 var algName = cenroll.GetAlgName(algId);
504 algorithm.appendChild(option(algName, algId));
505 } catch (e) {
506 break;
507 }
508 }
509 }
510
511 getKeySizeLimits();
512 }
513
514
515 /// Fill the provider selection box
516 var getProviderList = function () {
517 var i, j;
518
519 removeChildren(provider);
520
521 for (i = 0; i < providerTypes.length; ++i) {
522 cenroll.providerType = providerTypes[i];
523
524 var providerName = "invalid";
525 for (j = 0; true; ++j) {
526 try {
527 providerName = cenroll.enumProviders(j, 0);
528 provider.appendChild(
529 option(providerName, providerTypes[i]));
530 } catch (e) {
531 break;
532 }
533 }
534 }
535
536 return getAlgorithmList();
537 //return getKeySizeLimits();
538 }
539
540
541 var createCSR = function () {
542 var providerName, bits;
543
544 var level = securityLevel.options[securityLevel.selectedIndex];
545 if (level.value === "custom") {
546
547 refreshProvider();
548
549 bits = getKeySize();
550 if (bits === false) {
551 window.alert(invalidKeySizeError.innerHTML);
552 return false;
553 }
554
555 } else {
556
557 cenroll.ProviderName = "Microsoft Enhanced Cryptographic Provider v1.0";
558 cenroll.ProviderType = 1; //PROV_RSA_FULL
559
560 if (level.value === "high") {
561 bits = 4096;
562 } else { // medium
563 bits = 2048;
564 }
565 }
566
567 cenroll.GenKeyFlags = bits << 16; // keysize is encoded in the uper 16 bits
568 //cenroll.GenKeyFlags = cenroll.GenKeyFlags | 0x1; //CRYPT_EXPORTABLE
569
570 generatingKeyNotice.style.display = "";
571
572
573 // The request needs to be created after we return so the "please wait"
574 // message gets rendered
575 var createCSRHandler = function () {
576 try {
577 csr.value = cenroll.createPKCS10("", "1.3.6.1.5.5.7.3.2");
578 form.submit();
579 } catch (e) {
580 showError(createRequestError.innerHTML, e);
581 }
582
583 generatingKeyNotice.style.display = "none";
584 cenroll.Reset();
585 }
586
587 window.setTimeout(createCSRHandler, 0);
588
589 // Always return false, form is submitted by deferred method
590 return false;
591 }
592
593
594 /// Call if securityLevel has changed
595 var refreshSecurityLevel = function () {
596 var level = securityLevel.options[securityLevel.selectedIndex];
597 if (level.value === "custom") {
598 getProviderList();
599 customSettings.style.display = "";
600 } else {
601 customSettings.style.display = "none";
602 }
603 }
604
605 securityLevel.onchange = refreshSecurityLevel;
606 provider.onchange = getAlgorithmList;
607 //provider.onchange = getKeySizeLimits;
608 algorithm.onchange = getKeySizeLimits;
609 genReq.onclick = createCSR;
610
611 return true;
612 };
613
614
615 // Run the init functions until one is successful
616 if (initCertEnroll()) {
617 form.style.display = "";
618 noActiveX.style.display = "none";
619 } else if (initXEnroll()) {
620 form.style.display = "";
621 noActiveX.style.display = "none";
622 } else {
623 window.alert(unsupportedPlatformError.innerHTML);
624 }
625 } ();