1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 | var forge = require('./forge');
|
21 | require('./aes');
|
22 | require('./asn1');
|
23 | require('./des');
|
24 | require('./md');
|
25 | require('./oids');
|
26 | require('./pbkdf2');
|
27 | require('./pem');
|
28 | require('./random');
|
29 | require('./rc2');
|
30 | require('./rsa');
|
31 | require('./util');
|
32 |
|
33 | if(typeof BigInteger === 'undefined') {
|
34 | var BigInteger = forge.jsbn.BigInteger;
|
35 | }
|
36 |
|
37 |
|
38 | var asn1 = forge.asn1;
|
39 |
|
40 |
|
41 | var pki = forge.pki = forge.pki || {};
|
42 | module.exports = pki.pbe = forge.pbe = forge.pbe || {};
|
43 | var oids = pki.oids;
|
44 |
|
45 |
|
46 |
|
47 | var encryptedPrivateKeyValidator = {
|
48 | name: 'EncryptedPrivateKeyInfo',
|
49 | tagClass: asn1.Class.UNIVERSAL,
|
50 | type: asn1.Type.SEQUENCE,
|
51 | constructed: true,
|
52 | value: [{
|
53 | name: 'EncryptedPrivateKeyInfo.encryptionAlgorithm',
|
54 | tagClass: asn1.Class.UNIVERSAL,
|
55 | type: asn1.Type.SEQUENCE,
|
56 | constructed: true,
|
57 | value: [{
|
58 | name: 'AlgorithmIdentifier.algorithm',
|
59 | tagClass: asn1.Class.UNIVERSAL,
|
60 | type: asn1.Type.OID,
|
61 | constructed: false,
|
62 | capture: 'encryptionOid'
|
63 | }, {
|
64 | name: 'AlgorithmIdentifier.parameters',
|
65 | tagClass: asn1.Class.UNIVERSAL,
|
66 | type: asn1.Type.SEQUENCE,
|
67 | constructed: true,
|
68 | captureAsn1: 'encryptionParams'
|
69 | }]
|
70 | }, {
|
71 |
|
72 | name: 'EncryptedPrivateKeyInfo.encryptedData',
|
73 | tagClass: asn1.Class.UNIVERSAL,
|
74 | type: asn1.Type.OCTETSTRING,
|
75 | constructed: false,
|
76 | capture: 'encryptedData'
|
77 | }]
|
78 | };
|
79 |
|
80 |
|
81 |
|
82 | var PBES2AlgorithmsValidator = {
|
83 | name: 'PBES2Algorithms',
|
84 | tagClass: asn1.Class.UNIVERSAL,
|
85 | type: asn1.Type.SEQUENCE,
|
86 | constructed: true,
|
87 | value: [{
|
88 | name: 'PBES2Algorithms.keyDerivationFunc',
|
89 | tagClass: asn1.Class.UNIVERSAL,
|
90 | type: asn1.Type.SEQUENCE,
|
91 | constructed: true,
|
92 | value: [{
|
93 | name: 'PBES2Algorithms.keyDerivationFunc.oid',
|
94 | tagClass: asn1.Class.UNIVERSAL,
|
95 | type: asn1.Type.OID,
|
96 | constructed: false,
|
97 | capture: 'kdfOid'
|
98 | }, {
|
99 | name: 'PBES2Algorithms.params',
|
100 | tagClass: asn1.Class.UNIVERSAL,
|
101 | type: asn1.Type.SEQUENCE,
|
102 | constructed: true,
|
103 | value: [{
|
104 | name: 'PBES2Algorithms.params.salt',
|
105 | tagClass: asn1.Class.UNIVERSAL,
|
106 | type: asn1.Type.OCTETSTRING,
|
107 | constructed: false,
|
108 | capture: 'kdfSalt'
|
109 | }, {
|
110 | name: 'PBES2Algorithms.params.iterationCount',
|
111 | tagClass: asn1.Class.UNIVERSAL,
|
112 | type: asn1.Type.INTEGER,
|
113 | constructed: false,
|
114 | capture: 'kdfIterationCount'
|
115 | }, {
|
116 | name: 'PBES2Algorithms.params.keyLength',
|
117 | tagClass: asn1.Class.UNIVERSAL,
|
118 | type: asn1.Type.INTEGER,
|
119 | constructed: false,
|
120 | optional: true,
|
121 | capture: 'keyLength'
|
122 | }, {
|
123 |
|
124 | name: 'PBES2Algorithms.params.prf',
|
125 | tagClass: asn1.Class.UNIVERSAL,
|
126 | type: asn1.Type.SEQUENCE,
|
127 | constructed: true,
|
128 | optional: true,
|
129 | value: [{
|
130 | name: 'PBES2Algorithms.params.prf.algorithm',
|
131 | tagClass: asn1.Class.UNIVERSAL,
|
132 | type: asn1.Type.OID,
|
133 | constructed: false,
|
134 | capture: 'prfOid'
|
135 | }]
|
136 | }]
|
137 | }]
|
138 | }, {
|
139 | name: 'PBES2Algorithms.encryptionScheme',
|
140 | tagClass: asn1.Class.UNIVERSAL,
|
141 | type: asn1.Type.SEQUENCE,
|
142 | constructed: true,
|
143 | value: [{
|
144 | name: 'PBES2Algorithms.encryptionScheme.oid',
|
145 | tagClass: asn1.Class.UNIVERSAL,
|
146 | type: asn1.Type.OID,
|
147 | constructed: false,
|
148 | capture: 'encOid'
|
149 | }, {
|
150 | name: 'PBES2Algorithms.encryptionScheme.iv',
|
151 | tagClass: asn1.Class.UNIVERSAL,
|
152 | type: asn1.Type.OCTETSTRING,
|
153 | constructed: false,
|
154 | capture: 'encIv'
|
155 | }]
|
156 | }]
|
157 | };
|
158 |
|
159 | var pkcs12PbeParamsValidator = {
|
160 | name: 'pkcs-12PbeParams',
|
161 | tagClass: asn1.Class.UNIVERSAL,
|
162 | type: asn1.Type.SEQUENCE,
|
163 | constructed: true,
|
164 | value: [{
|
165 | name: 'pkcs-12PbeParams.salt',
|
166 | tagClass: asn1.Class.UNIVERSAL,
|
167 | type: asn1.Type.OCTETSTRING,
|
168 | constructed: false,
|
169 | capture: 'salt'
|
170 | }, {
|
171 | name: 'pkcs-12PbeParams.iterations',
|
172 | tagClass: asn1.Class.UNIVERSAL,
|
173 | type: asn1.Type.INTEGER,
|
174 | constructed: false,
|
175 | capture: 'iterations'
|
176 | }]
|
177 | };
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 |
|
188 |
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 |
|
202 |
|
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 |
|
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 |
|
219 | pki.encryptPrivateKeyInfo = function(obj, password, options) {
|
220 |
|
221 | options = options || {};
|
222 | options.saltSize = options.saltSize || 8;
|
223 | options.count = options.count || 2048;
|
224 | options.algorithm = options.algorithm || 'aes128';
|
225 | options.prfAlgorithm = options.prfAlgorithm || 'sha1';
|
226 |
|
227 |
|
228 | var salt = forge.random.getBytesSync(options.saltSize);
|
229 | var count = options.count;
|
230 | var countBytes = asn1.integerToDer(count);
|
231 | var dkLen;
|
232 | var encryptionAlgorithm;
|
233 | var encryptedData;
|
234 | if(options.algorithm.indexOf('aes') === 0 || options.algorithm === 'des') {
|
235 |
|
236 | var ivLen, encOid, cipherFn;
|
237 | switch(options.algorithm) {
|
238 | case 'aes128':
|
239 | dkLen = 16;
|
240 | ivLen = 16;
|
241 | encOid = oids['aes128-CBC'];
|
242 | cipherFn = forge.aes.createEncryptionCipher;
|
243 | break;
|
244 | case 'aes192':
|
245 | dkLen = 24;
|
246 | ivLen = 16;
|
247 | encOid = oids['aes192-CBC'];
|
248 | cipherFn = forge.aes.createEncryptionCipher;
|
249 | break;
|
250 | case 'aes256':
|
251 | dkLen = 32;
|
252 | ivLen = 16;
|
253 | encOid = oids['aes256-CBC'];
|
254 | cipherFn = forge.aes.createEncryptionCipher;
|
255 | break;
|
256 | case 'des':
|
257 | dkLen = 8;
|
258 | ivLen = 8;
|
259 | encOid = oids['desCBC'];
|
260 | cipherFn = forge.des.createEncryptionCipher;
|
261 | break;
|
262 | default:
|
263 | var error = new Error('Cannot encrypt private key. Unknown encryption algorithm.');
|
264 | error.algorithm = options.algorithm;
|
265 | throw error;
|
266 | }
|
267 |
|
268 |
|
269 | var prfAlgorithm = 'hmacWith' + options.prfAlgorithm.toUpperCase();
|
270 | var md = prfAlgorithmToMessageDigest(prfAlgorithm);
|
271 |
|
272 |
|
273 | var dk = forge.pkcs5.pbkdf2(password, salt, count, dkLen, md);
|
274 | var iv = forge.random.getBytesSync(ivLen);
|
275 | var cipher = cipherFn(dk);
|
276 | cipher.start(iv);
|
277 | cipher.update(asn1.toDer(obj));
|
278 | cipher.finish();
|
279 | encryptedData = cipher.output.getBytes();
|
280 |
|
281 |
|
282 | var params = createPbkdf2Params(salt, countBytes, dkLen, prfAlgorithm);
|
283 |
|
284 | encryptionAlgorithm = asn1.create(
|
285 | asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
286 | asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
|
287 | asn1.oidToDer(oids['pkcs5PBES2']).getBytes()),
|
288 | asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
289 |
|
290 | asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
291 | asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
|
292 | asn1.oidToDer(oids['pkcs5PBKDF2']).getBytes()),
|
293 |
|
294 | params
|
295 | ]),
|
296 |
|
297 | asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
298 | asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
|
299 | asn1.oidToDer(encOid).getBytes()),
|
300 |
|
301 | asn1.create(
|
302 | asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, iv)
|
303 | ])
|
304 | ])
|
305 | ]);
|
306 | } else if(options.algorithm === '3des') {
|
307 |
|
308 | dkLen = 24;
|
309 |
|
310 | var saltBytes = new forge.util.ByteBuffer(salt);
|
311 | var dk = pki.pbe.generatePkcs12Key(password, saltBytes, 1, count, dkLen);
|
312 | var iv = pki.pbe.generatePkcs12Key(password, saltBytes, 2, count, dkLen);
|
313 | var cipher = forge.des.createEncryptionCipher(dk);
|
314 | cipher.start(iv);
|
315 | cipher.update(asn1.toDer(obj));
|
316 | cipher.finish();
|
317 | encryptedData = cipher.output.getBytes();
|
318 |
|
319 | encryptionAlgorithm = asn1.create(
|
320 | asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
321 | asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
|
322 | asn1.oidToDer(oids['pbeWithSHAAnd3-KeyTripleDES-CBC']).getBytes()),
|
323 |
|
324 | asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
325 |
|
326 | asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, salt),
|
327 |
|
328 | asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
|
329 | countBytes.getBytes())
|
330 | ])
|
331 | ]);
|
332 | } else {
|
333 | var error = new Error('Cannot encrypt private key. Unknown encryption algorithm.');
|
334 | error.algorithm = options.algorithm;
|
335 | throw error;
|
336 | }
|
337 |
|
338 |
|
339 | var rval = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
340 |
|
341 | encryptionAlgorithm,
|
342 |
|
343 | asn1.create(
|
344 | asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, encryptedData)
|
345 | ]);
|
346 | return rval;
|
347 | };
|
348 |
|
349 |
|
350 |
|
351 |
|
352 |
|
353 |
|
354 |
|
355 |
|
356 |
|
357 | pki.decryptPrivateKeyInfo = function(obj, password) {
|
358 | var rval = null;
|
359 |
|
360 |
|
361 | var capture = {};
|
362 | var errors = [];
|
363 | if(!asn1.validate(obj, encryptedPrivateKeyValidator, capture, errors)) {
|
364 | var error = new Error('Cannot read encrypted private key. ' +
|
365 | 'ASN.1 object is not a supported EncryptedPrivateKeyInfo.');
|
366 | error.errors = errors;
|
367 | throw error;
|
368 | }
|
369 |
|
370 |
|
371 | var oid = asn1.derToOid(capture.encryptionOid);
|
372 | var cipher = pki.pbe.getCipher(oid, capture.encryptionParams, password);
|
373 |
|
374 |
|
375 | var encrypted = forge.util.createBuffer(capture.encryptedData);
|
376 |
|
377 | cipher.update(encrypted);
|
378 | if(cipher.finish()) {
|
379 | rval = asn1.fromDer(cipher.output);
|
380 | }
|
381 |
|
382 | return rval;
|
383 | };
|
384 |
|
385 |
|
386 |
|
387 |
|
388 |
|
389 |
|
390 |
|
391 |
|
392 |
|
393 | pki.encryptedPrivateKeyToPem = function(epki, maxline) {
|
394 |
|
395 | var msg = {
|
396 | type: 'ENCRYPTED PRIVATE KEY',
|
397 | body: asn1.toDer(epki).getBytes()
|
398 | };
|
399 | return forge.pem.encode(msg, {maxline: maxline});
|
400 | };
|
401 |
|
402 |
|
403 |
|
404 |
|
405 |
|
406 |
|
407 |
|
408 |
|
409 |
|
410 | pki.encryptedPrivateKeyFromPem = function(pem) {
|
411 | var msg = forge.pem.decode(pem)[0];
|
412 |
|
413 | if(msg.type !== 'ENCRYPTED PRIVATE KEY') {
|
414 | var error = new Error('Could not convert encrypted private key from PEM; ' +
|
415 | 'PEM header type is "ENCRYPTED PRIVATE KEY".');
|
416 | error.headerType = msg.type;
|
417 | throw error;
|
418 | }
|
419 | if(msg.procType && msg.procType.type === 'ENCRYPTED') {
|
420 | throw new Error('Could not convert encrypted private key from PEM; ' +
|
421 | 'PEM is encrypted.');
|
422 | }
|
423 |
|
424 |
|
425 | return asn1.fromDer(msg.body);
|
426 | };
|
427 |
|
428 |
|
429 |
|
430 |
|
431 |
|
432 |
|
433 |
|
434 |
|
435 |
|
436 |
|
437 |
|
438 |
|
439 |
|
440 |
|
441 |
|
442 |
|
443 |
|
444 |
|
445 |
|
446 |
|
447 |
|
448 |
|
449 |
|
450 |
|
451 |
|
452 |
|
453 |
|
454 | pki.encryptRsaPrivateKey = function(rsaKey, password, options) {
|
455 |
|
456 | options = options || {};
|
457 | if(!options.legacy) {
|
458 |
|
459 | var rval = pki.wrapRsaPrivateKey(pki.privateKeyToAsn1(rsaKey));
|
460 | rval = pki.encryptPrivateKeyInfo(rval, password, options);
|
461 | return pki.encryptedPrivateKeyToPem(rval);
|
462 | }
|
463 |
|
464 |
|
465 | var algorithm;
|
466 | var iv;
|
467 | var dkLen;
|
468 | var cipherFn;
|
469 | switch(options.algorithm) {
|
470 | case 'aes128':
|
471 | algorithm = 'AES-128-CBC';
|
472 | dkLen = 16;
|
473 | iv = forge.random.getBytesSync(16);
|
474 | cipherFn = forge.aes.createEncryptionCipher;
|
475 | break;
|
476 | case 'aes192':
|
477 | algorithm = 'AES-192-CBC';
|
478 | dkLen = 24;
|
479 | iv = forge.random.getBytesSync(16);
|
480 | cipherFn = forge.aes.createEncryptionCipher;
|
481 | break;
|
482 | case 'aes256':
|
483 | algorithm = 'AES-256-CBC';
|
484 | dkLen = 32;
|
485 | iv = forge.random.getBytesSync(16);
|
486 | cipherFn = forge.aes.createEncryptionCipher;
|
487 | break;
|
488 | case '3des':
|
489 | algorithm = 'DES-EDE3-CBC';
|
490 | dkLen = 24;
|
491 | iv = forge.random.getBytesSync(8);
|
492 | cipherFn = forge.des.createEncryptionCipher;
|
493 | break;
|
494 | case 'des':
|
495 | algorithm = 'DES-CBC';
|
496 | dkLen = 8;
|
497 | iv = forge.random.getBytesSync(8);
|
498 | cipherFn = forge.des.createEncryptionCipher;
|
499 | break;
|
500 | default:
|
501 | var error = new Error('Could not encrypt RSA private key; unsupported ' +
|
502 | 'encryption algorithm "' + options.algorithm + '".');
|
503 | error.algorithm = options.algorithm;
|
504 | throw error;
|
505 | }
|
506 |
|
507 |
|
508 | var dk = forge.pbe.opensslDeriveBytes(password, iv.substr(0, 8), dkLen);
|
509 | var cipher = cipherFn(dk);
|
510 | cipher.start(iv);
|
511 | cipher.update(asn1.toDer(pki.privateKeyToAsn1(rsaKey)));
|
512 | cipher.finish();
|
513 |
|
514 | var msg = {
|
515 | type: 'RSA PRIVATE KEY',
|
516 | procType: {
|
517 | version: '4',
|
518 | type: 'ENCRYPTED'
|
519 | },
|
520 | dekInfo: {
|
521 | algorithm: algorithm,
|
522 | parameters: forge.util.bytesToHex(iv).toUpperCase()
|
523 | },
|
524 | body: cipher.output.getBytes()
|
525 | };
|
526 | return forge.pem.encode(msg);
|
527 | };
|
528 |
|
529 |
|
530 |
|
531 |
|
532 |
|
533 |
|
534 |
|
535 |
|
536 |
|
537 | pki.decryptRsaPrivateKey = function(pem, password) {
|
538 | var rval = null;
|
539 |
|
540 | var msg = forge.pem.decode(pem)[0];
|
541 |
|
542 | if(msg.type !== 'ENCRYPTED PRIVATE KEY' &&
|
543 | msg.type !== 'PRIVATE KEY' &&
|
544 | msg.type !== 'RSA PRIVATE KEY') {
|
545 | var error = new Error('Could not convert private key from PEM; PEM header type ' +
|
546 | 'is not "ENCRYPTED PRIVATE KEY", "PRIVATE KEY", or "RSA PRIVATE KEY".');
|
547 | error.headerType = error;
|
548 | throw error;
|
549 | }
|
550 |
|
551 | if(msg.procType && msg.procType.type === 'ENCRYPTED') {
|
552 | var dkLen;
|
553 | var cipherFn;
|
554 | switch(msg.dekInfo.algorithm) {
|
555 | case 'DES-CBC':
|
556 | dkLen = 8;
|
557 | cipherFn = forge.des.createDecryptionCipher;
|
558 | break;
|
559 | case 'DES-EDE3-CBC':
|
560 | dkLen = 24;
|
561 | cipherFn = forge.des.createDecryptionCipher;
|
562 | break;
|
563 | case 'AES-128-CBC':
|
564 | dkLen = 16;
|
565 | cipherFn = forge.aes.createDecryptionCipher;
|
566 | break;
|
567 | case 'AES-192-CBC':
|
568 | dkLen = 24;
|
569 | cipherFn = forge.aes.createDecryptionCipher;
|
570 | break;
|
571 | case 'AES-256-CBC':
|
572 | dkLen = 32;
|
573 | cipherFn = forge.aes.createDecryptionCipher;
|
574 | break;
|
575 | case 'RC2-40-CBC':
|
576 | dkLen = 5;
|
577 | cipherFn = function(key) {
|
578 | return forge.rc2.createDecryptionCipher(key, 40);
|
579 | };
|
580 | break;
|
581 | case 'RC2-64-CBC':
|
582 | dkLen = 8;
|
583 | cipherFn = function(key) {
|
584 | return forge.rc2.createDecryptionCipher(key, 64);
|
585 | };
|
586 | break;
|
587 | case 'RC2-128-CBC':
|
588 | dkLen = 16;
|
589 | cipherFn = function(key) {
|
590 | return forge.rc2.createDecryptionCipher(key, 128);
|
591 | };
|
592 | break;
|
593 | default:
|
594 | var error = new Error('Could not decrypt private key; unsupported ' +
|
595 | 'encryption algorithm "' + msg.dekInfo.algorithm + '".');
|
596 | error.algorithm = msg.dekInfo.algorithm;
|
597 | throw error;
|
598 | }
|
599 |
|
600 |
|
601 | var iv = forge.util.hexToBytes(msg.dekInfo.parameters);
|
602 | var dk = forge.pbe.opensslDeriveBytes(password, iv.substr(0, 8), dkLen);
|
603 | var cipher = cipherFn(dk);
|
604 | cipher.start(iv);
|
605 | cipher.update(forge.util.createBuffer(msg.body));
|
606 | if(cipher.finish()) {
|
607 | rval = cipher.output.getBytes();
|
608 | } else {
|
609 | return rval;
|
610 | }
|
611 | } else {
|
612 | rval = msg.body;
|
613 | }
|
614 |
|
615 | if(msg.type === 'ENCRYPTED PRIVATE KEY') {
|
616 | rval = pki.decryptPrivateKeyInfo(asn1.fromDer(rval), password);
|
617 | } else {
|
618 |
|
619 | rval = asn1.fromDer(rval);
|
620 | }
|
621 |
|
622 | if(rval !== null) {
|
623 | rval = pki.privateKeyFromAsn1(rval);
|
624 | }
|
625 |
|
626 | return rval;
|
627 | };
|
628 |
|
629 |
|
630 |
|
631 |
|
632 |
|
633 |
|
634 |
|
635 |
|
636 |
|
637 |
|
638 |
|
639 |
|
640 |
|
641 |
|
642 | pki.pbe.generatePkcs12Key = function(password, salt, id, iter, n, md) {
|
643 | var j, l;
|
644 |
|
645 | if(typeof md === 'undefined' || md === null) {
|
646 | if(!('sha1' in forge.md)) {
|
647 | throw new Error('"sha1" hash algorithm unavailable.');
|
648 | }
|
649 | md = forge.md.sha1.create();
|
650 | }
|
651 |
|
652 | var u = md.digestLength;
|
653 | var v = md.blockLength;
|
654 | var result = new forge.util.ByteBuffer();
|
655 |
|
656 |
|
657 | var passBuf = new forge.util.ByteBuffer();
|
658 | if(password !== null && password !== undefined) {
|
659 | for(l = 0; l < password.length; l++) {
|
660 | passBuf.putInt16(password.charCodeAt(l));
|
661 | }
|
662 | passBuf.putInt16(0);
|
663 | }
|
664 |
|
665 |
|
666 | var p = passBuf.length();
|
667 | var s = salt.length();
|
668 |
|
669 | |
670 |
|
671 | var D = new forge.util.ByteBuffer();
|
672 | D.fillWithByte(id, v);
|
673 |
|
674 | |
675 |
|
676 |
|
677 |
|
678 | var Slen = v * Math.ceil(s / v);
|
679 | var S = new forge.util.ByteBuffer();
|
680 | for(l = 0; l < Slen; l++) {
|
681 | S.putByte(salt.at(l % s));
|
682 | }
|
683 |
|
684 | |
685 |
|
686 |
|
687 |
|
688 | var Plen = v * Math.ceil(p / v);
|
689 | var P = new forge.util.ByteBuffer();
|
690 | for(l = 0; l < Plen; l++) {
|
691 | P.putByte(passBuf.at(l % p));
|
692 | }
|
693 |
|
694 |
|
695 | var I = S;
|
696 | I.putBuffer(P);
|
697 |
|
698 |
|
699 | var c = Math.ceil(n / u);
|
700 |
|
701 |
|
702 | for(var i = 1; i <= c; i++) {
|
703 |
|
704 | var buf = new forge.util.ByteBuffer();
|
705 | buf.putBytes(D.bytes());
|
706 | buf.putBytes(I.bytes());
|
707 | for(var round = 0; round < iter; round++) {
|
708 | md.start();
|
709 | md.update(buf.getBytes());
|
710 | buf = md.digest();
|
711 | }
|
712 |
|
713 | |
714 |
|
715 | var B = new forge.util.ByteBuffer();
|
716 | for(l = 0; l < v; l++) {
|
717 | B.putByte(buf.at(l % u));
|
718 | }
|
719 |
|
720 | |
721 |
|
722 |
|
723 | var k = Math.ceil(s / v) + Math.ceil(p / v);
|
724 | var Inew = new forge.util.ByteBuffer();
|
725 | for(j = 0; j < k; j++) {
|
726 | var chunk = new forge.util.ByteBuffer(I.getBytes(v));
|
727 | var x = 0x1ff;
|
728 | for(l = B.length() - 1; l >= 0; l--) {
|
729 | x = x >> 8;
|
730 | x += B.at(l) + chunk.at(l);
|
731 | chunk.setAt(l, x & 0xff);
|
732 | }
|
733 | Inew.putBuffer(chunk);
|
734 | }
|
735 | I = Inew;
|
736 |
|
737 |
|
738 | result.putBuffer(buf);
|
739 | }
|
740 |
|
741 | result.truncate(result.length() - n);
|
742 | return result;
|
743 | };
|
744 |
|
745 |
|
746 |
|
747 |
|
748 |
|
749 |
|
750 |
|
751 |
|
752 |
|
753 |
|
754 | pki.pbe.getCipher = function(oid, params, password) {
|
755 | switch(oid) {
|
756 | case pki.oids['pkcs5PBES2']:
|
757 | return pki.pbe.getCipherForPBES2(oid, params, password);
|
758 |
|
759 | case pki.oids['pbeWithSHAAnd3-KeyTripleDES-CBC']:
|
760 | case pki.oids['pbewithSHAAnd40BitRC2-CBC']:
|
761 | return pki.pbe.getCipherForPKCS12PBE(oid, params, password);
|
762 |
|
763 | default:
|
764 | var error = new Error('Cannot read encrypted PBE data block. Unsupported OID.');
|
765 | error.oid = oid;
|
766 | error.supportedOids = [
|
767 | 'pkcs5PBES2',
|
768 | 'pbeWithSHAAnd3-KeyTripleDES-CBC',
|
769 | 'pbewithSHAAnd40BitRC2-CBC'
|
770 | ];
|
771 | throw error;
|
772 | }
|
773 | };
|
774 |
|
775 |
|
776 |
|
777 |
|
778 |
|
779 |
|
780 |
|
781 |
|
782 |
|
783 |
|
784 |
|
785 |
|
786 |
|
787 | pki.pbe.getCipherForPBES2 = function(oid, params, password) {
|
788 |
|
789 | var capture = {};
|
790 | var errors = [];
|
791 | if(!asn1.validate(params, PBES2AlgorithmsValidator, capture, errors)) {
|
792 | var error = new Error('Cannot read password-based-encryption algorithm ' +
|
793 | 'parameters. ASN.1 object is not a supported EncryptedPrivateKeyInfo.');
|
794 | error.errors = errors;
|
795 | throw error;
|
796 | }
|
797 |
|
798 |
|
799 | oid = asn1.derToOid(capture.kdfOid);
|
800 | if(oid !== pki.oids['pkcs5PBKDF2']) {
|
801 | var error = new Error('Cannot read encrypted private key. ' +
|
802 | 'Unsupported key derivation function OID.');
|
803 | error.oid = oid;
|
804 | error.supportedOids = ['pkcs5PBKDF2'];
|
805 | throw error;
|
806 | }
|
807 | oid = asn1.derToOid(capture.encOid);
|
808 | if(oid !== pki.oids['aes128-CBC'] &&
|
809 | oid !== pki.oids['aes192-CBC'] &&
|
810 | oid !== pki.oids['aes256-CBC'] &&
|
811 | oid !== pki.oids['des-EDE3-CBC'] &&
|
812 | oid !== pki.oids['desCBC']) {
|
813 | var error = new Error('Cannot read encrypted private key. ' +
|
814 | 'Unsupported encryption scheme OID.');
|
815 | error.oid = oid;
|
816 | error.supportedOids = [
|
817 | 'aes128-CBC', 'aes192-CBC', 'aes256-CBC', 'des-EDE3-CBC', 'desCBC'];
|
818 | throw error;
|
819 | }
|
820 |
|
821 |
|
822 | var salt = capture.kdfSalt;
|
823 | var count = forge.util.createBuffer(capture.kdfIterationCount);
|
824 | count = count.getInt(count.length() << 3);
|
825 | var dkLen;
|
826 | var cipherFn;
|
827 | switch(pki.oids[oid]) {
|
828 | case 'aes128-CBC':
|
829 | dkLen = 16;
|
830 | cipherFn = forge.aes.createDecryptionCipher;
|
831 | break;
|
832 | case 'aes192-CBC':
|
833 | dkLen = 24;
|
834 | cipherFn = forge.aes.createDecryptionCipher;
|
835 | break;
|
836 | case 'aes256-CBC':
|
837 | dkLen = 32;
|
838 | cipherFn = forge.aes.createDecryptionCipher;
|
839 | break;
|
840 | case 'des-EDE3-CBC':
|
841 | dkLen = 24;
|
842 | cipherFn = forge.des.createDecryptionCipher;
|
843 | break;
|
844 | case 'desCBC':
|
845 | dkLen = 8;
|
846 | cipherFn = forge.des.createDecryptionCipher;
|
847 | break;
|
848 | }
|
849 |
|
850 |
|
851 | var md = prfOidToMessageDigest(capture.prfOid);
|
852 |
|
853 |
|
854 | var dk = forge.pkcs5.pbkdf2(password, salt, count, dkLen, md);
|
855 | var iv = capture.encIv;
|
856 | var cipher = cipherFn(dk);
|
857 | cipher.start(iv);
|
858 |
|
859 | return cipher;
|
860 | };
|
861 |
|
862 |
|
863 |
|
864 |
|
865 |
|
866 |
|
867 |
|
868 |
|
869 |
|
870 |
|
871 |
|
872 |
|
873 |
|
874 | pki.pbe.getCipherForPKCS12PBE = function(oid, params, password) {
|
875 |
|
876 | var capture = {};
|
877 | var errors = [];
|
878 | if(!asn1.validate(params, pkcs12PbeParamsValidator, capture, errors)) {
|
879 | var error = new Error('Cannot read password-based-encryption algorithm ' +
|
880 | 'parameters. ASN.1 object is not a supported EncryptedPrivateKeyInfo.');
|
881 | error.errors = errors;
|
882 | throw error;
|
883 | }
|
884 |
|
885 | var salt = forge.util.createBuffer(capture.salt);
|
886 | var count = forge.util.createBuffer(capture.iterations);
|
887 | count = count.getInt(count.length() << 3);
|
888 |
|
889 | var dkLen, dIvLen, cipherFn;
|
890 | switch(oid) {
|
891 | case pki.oids['pbeWithSHAAnd3-KeyTripleDES-CBC']:
|
892 | dkLen = 24;
|
893 | dIvLen = 8;
|
894 | cipherFn = forge.des.startDecrypting;
|
895 | break;
|
896 |
|
897 | case pki.oids['pbewithSHAAnd40BitRC2-CBC']:
|
898 | dkLen = 5;
|
899 | dIvLen = 8;
|
900 | cipherFn = function(key, iv) {
|
901 | var cipher = forge.rc2.createDecryptionCipher(key, 40);
|
902 | cipher.start(iv, null);
|
903 | return cipher;
|
904 | };
|
905 | break;
|
906 |
|
907 | default:
|
908 | var error = new Error('Cannot read PKCS #12 PBE data block. Unsupported OID.');
|
909 | error.oid = oid;
|
910 | throw error;
|
911 | }
|
912 |
|
913 |
|
914 | var md = prfOidToMessageDigest(capture.prfOid);
|
915 | var key = pki.pbe.generatePkcs12Key(password, salt, 1, count, dkLen, md);
|
916 | md.start();
|
917 | var iv = pki.pbe.generatePkcs12Key(password, salt, 2, count, dIvLen, md);
|
918 |
|
919 | return cipherFn(key, iv);
|
920 | };
|
921 |
|
922 |
|
923 |
|
924 |
|
925 |
|
926 |
|
927 |
|
928 |
|
929 |
|
930 |
|
931 |
|
932 |
|
933 | pki.pbe.opensslDeriveBytes = function(password, salt, dkLen, md) {
|
934 | if(typeof md === 'undefined' || md === null) {
|
935 | if(!('md5' in forge.md)) {
|
936 | throw new Error('"md5" hash algorithm unavailable.');
|
937 | }
|
938 | md = forge.md.md5.create();
|
939 | }
|
940 | if(salt === null) {
|
941 | salt = '';
|
942 | }
|
943 | var digests = [hash(md, password + salt)];
|
944 | for(var length = 16, i = 1; length < dkLen; ++i, length += 16) {
|
945 | digests.push(hash(md, digests[i - 1] + password + salt));
|
946 | }
|
947 | return digests.join('').substr(0, dkLen);
|
948 | };
|
949 |
|
950 | function hash(md, bytes) {
|
951 | return md.start().update(bytes).digest().getBytes();
|
952 | }
|
953 |
|
954 | function prfOidToMessageDigest(prfOid) {
|
955 |
|
956 | var prfAlgorithm;
|
957 | if(!prfOid) {
|
958 | prfAlgorithm = 'hmacWithSHA1';
|
959 | } else {
|
960 | prfAlgorithm = pki.oids[asn1.derToOid(prfOid)];
|
961 | if(!prfAlgorithm) {
|
962 | var error = new Error('Unsupported PRF OID.');
|
963 | error.oid = prfOid;
|
964 | error.supported = [
|
965 | 'hmacWithSHA1', 'hmacWithSHA224', 'hmacWithSHA256', 'hmacWithSHA384',
|
966 | 'hmacWithSHA512'];
|
967 | throw error;
|
968 | }
|
969 | }
|
970 | return prfAlgorithmToMessageDigest(prfAlgorithm);
|
971 | }
|
972 |
|
973 | function prfAlgorithmToMessageDigest(prfAlgorithm) {
|
974 | var factory = forge.md;
|
975 | switch(prfAlgorithm) {
|
976 | case 'hmacWithSHA224':
|
977 | factory = forge.md.sha512;
|
978 | case 'hmacWithSHA1':
|
979 | case 'hmacWithSHA256':
|
980 | case 'hmacWithSHA384':
|
981 | case 'hmacWithSHA512':
|
982 | prfAlgorithm = prfAlgorithm.substr(8).toLowerCase();
|
983 | break;
|
984 | default:
|
985 | var error = new Error('Unsupported PRF algorithm.');
|
986 | error.algorithm = prfAlgorithm;
|
987 | error.supported = [
|
988 | 'hmacWithSHA1', 'hmacWithSHA224', 'hmacWithSHA256', 'hmacWithSHA384',
|
989 | 'hmacWithSHA512'];
|
990 | throw error;
|
991 | }
|
992 | if(!factory || !(prfAlgorithm in factory)) {
|
993 | throw new Error('Unknown hash algorithm: ' + prfAlgorithm);
|
994 | }
|
995 | return factory[prfAlgorithm].create();
|
996 | }
|
997 |
|
998 | function createPbkdf2Params(salt, countBytes, dkLen, prfAlgorithm) {
|
999 | var params = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
1000 |
|
1001 | asn1.create(
|
1002 | asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, salt),
|
1003 |
|
1004 | asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
|
1005 | countBytes.getBytes())
|
1006 | ]);
|
1007 |
|
1008 | if(prfAlgorithm !== 'hmacWithSHA1') {
|
1009 | params.value.push(
|
1010 |
|
1011 | asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
|
1012 | forge.util.hexToBytes(dkLen.toString(16))),
|
1013 |
|
1014 | asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
|
1015 |
|
1016 | asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
|
1017 | asn1.oidToDer(pki.oids[prfAlgorithm]).getBytes()),
|
1018 |
|
1019 | asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
|
1020 | ]));
|
1021 | }
|
1022 | return params;
|
1023 | }
|