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