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