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