UNPKG

31 kBJavaScriptView Raw
1/**
2 * Password-based encryption functions.
3 *
4 * @author Dave Longley
5 * @author Stefan Siegl <stesie@brokenpipe.de>
6 *
7 * Copyright (c) 2010-2013 Digital Bazaar, Inc.
8 * Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
9 *
10 * An EncryptedPrivateKeyInfo:
11 *
12 * EncryptedPrivateKeyInfo ::= SEQUENCE {
13 * encryptionAlgorithm EncryptionAlgorithmIdentifier,
14 * encryptedData EncryptedData }
15 *
16 * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
17 *
18 * EncryptedData ::= OCTET STRING
19 */
20var forge = require('./forge');
21require('./aes');
22require('./asn1');
23require('./des');
24require('./md');
25require('./oids');
26require('./pbkdf2');
27require('./pem');
28require('./random');
29require('./rc2');
30require('./rsa');
31require('./util');
32
33if(typeof BigInteger === 'undefined') {
34 var BigInteger = forge.jsbn.BigInteger;
35}
36
37// shortcut for asn.1 API
38var asn1 = forge.asn1;
39
40/* Password-based encryption implementation. */
41var pki = forge.pki = forge.pki || {};
42module.exports = pki.pbe = forge.pbe = forge.pbe || {};
43var oids = pki.oids;
44
45// validator for an EncryptedPrivateKeyInfo structure
46// Note: Currently only works w/algorithm params
47var 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 // encryptedData
72 name: 'EncryptedPrivateKeyInfo.encryptedData',
73 tagClass: asn1.Class.UNIVERSAL,
74 type: asn1.Type.OCTETSTRING,
75 constructed: false,
76 capture: 'encryptedData'
77 }]
78};
79
80// validator for a PBES2Algorithms structure
81// Note: Currently only works w/PBKDF2 + AES encryption schemes
82var 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 // prf
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
159var 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 * Encrypts a ASN.1 PrivateKeyInfo object, producing an EncryptedPrivateKeyInfo.
181 *
182 * PBES2Algorithms ALGORITHM-IDENTIFIER ::=
183 * { {PBES2-params IDENTIFIED BY id-PBES2}, ...}
184 *
185 * id-PBES2 OBJECT IDENTIFIER ::= {pkcs-5 13}
186 *
187 * PBES2-params ::= SEQUENCE {
188 * keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
189 * encryptionScheme AlgorithmIdentifier {{PBES2-Encs}}
190 * }
191 *
192 * PBES2-KDFs ALGORITHM-IDENTIFIER ::=
193 * { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... }
194 *
195 * PBES2-Encs ALGORITHM-IDENTIFIER ::= { ... }
196 *
197 * PBKDF2-params ::= SEQUENCE {
198 * salt CHOICE {
199 * specified OCTET STRING,
200 * otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
201 * },
202 * iterationCount INTEGER (1..MAX),
203 * keyLength INTEGER (1..MAX) OPTIONAL,
204 * prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1
205 * }
206 *
207 * @param obj the ASN.1 PrivateKeyInfo object.
208 * @param password the password to encrypt with.
209 * @param options:
210 * algorithm the encryption algorithm to use
211 * ('aes128', 'aes192', 'aes256', '3des'), defaults to 'aes128'.
212 * count the iteration count to use.
213 * saltSize the salt size to use.
214 * prfAlgorithm the PRF message digest algorithm to use
215 * ('sha1', 'sha224', 'sha256', 'sha384', 'sha512')
216 *
217 * @return the ASN.1 EncryptedPrivateKeyInfo.
218 */
219pki.encryptPrivateKeyInfo = function(obj, password, options) {
220 // set default options
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 // generate PBE params
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 // do PBES2
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 // get PRF message digest
269 var prfAlgorithm = 'hmacWith' + options.prfAlgorithm.toUpperCase();
270 var md = prfAlgorithmToMessageDigest(prfAlgorithm);
271
272 // encrypt private key using pbe SHA-1 and AES/DES
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 // get PBKDF2-params
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 // keyDerivationFunc
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 // PBKDF2-params
294 params
295 ]),
296 // encryptionScheme
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 // iv
301 asn1.create(
302 asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, iv)
303 ])
304 ])
305 ]);
306 } else if(options.algorithm === '3des') {
307 // Do PKCS12 PBE
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 // pkcs-12PbeParams
324 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
325 // salt
326 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, salt),
327 // iteration count
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 // EncryptedPrivateKeyInfo
339 var rval = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
340 // encryptionAlgorithm
341 encryptionAlgorithm,
342 // encryptedData
343 asn1.create(
344 asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, encryptedData)
345 ]);
346 return rval;
347};
348
349/**
350 * Decrypts a ASN.1 PrivateKeyInfo object.
351 *
352 * @param obj the ASN.1 EncryptedPrivateKeyInfo object.
353 * @param password the password to decrypt with.
354 *
355 * @return the ASN.1 PrivateKeyInfo on success, null on failure.
356 */
357pki.decryptPrivateKeyInfo = function(obj, password) {
358 var rval = null;
359
360 // get PBE params
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 // get cipher
371 var oid = asn1.derToOid(capture.encryptionOid);
372 var cipher = pki.pbe.getCipher(oid, capture.encryptionParams, password);
373
374 // get encrypted data
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 * Converts a EncryptedPrivateKeyInfo to PEM format.
387 *
388 * @param epki the EncryptedPrivateKeyInfo.
389 * @param maxline the maximum characters per line, defaults to 64.
390 *
391 * @return the PEM-formatted encrypted private key.
392 */
393pki.encryptedPrivateKeyToPem = function(epki, maxline) {
394 // convert to DER, then PEM-encode
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 * Converts a PEM-encoded EncryptedPrivateKeyInfo to ASN.1 format. Decryption
404 * is not performed.
405 *
406 * @param pem the EncryptedPrivateKeyInfo in PEM-format.
407 *
408 * @return the ASN.1 EncryptedPrivateKeyInfo.
409 */
410pki.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 // convert DER to ASN.1 object
425 return asn1.fromDer(msg.body);
426};
427
428/**
429 * Encrypts an RSA private key. By default, the key will be wrapped in
430 * a PrivateKeyInfo and encrypted to produce a PKCS#8 EncryptedPrivateKeyInfo.
431 * This is the standard, preferred way to encrypt a private key.
432 *
433 * To produce a non-standard PEM-encrypted private key that uses encapsulated
434 * headers to indicate the encryption algorithm (old-style non-PKCS#8 OpenSSL
435 * private key encryption), set the 'legacy' option to true. Note: Using this
436 * option will cause the iteration count to be forced to 1.
437 *
438 * Note: The 'des' algorithm is supported, but it is not considered to be
439 * secure because it only uses a single 56-bit key. If possible, it is highly
440 * recommended that a different algorithm be used.
441 *
442 * @param rsaKey the RSA key to encrypt.
443 * @param password the password to use.
444 * @param options:
445 * algorithm: the encryption algorithm to use
446 * ('aes128', 'aes192', 'aes256', '3des', 'des').
447 * count: the iteration count to use.
448 * saltSize: the salt size to use.
449 * legacy: output an old non-PKCS#8 PEM-encrypted+encapsulated
450 * headers (DEK-Info) private key.
451 *
452 * @return the PEM-encoded ASN.1 EncryptedPrivateKeyInfo.
453 */
454pki.encryptRsaPrivateKey = function(rsaKey, password, options) {
455 // standard PKCS#8
456 options = options || {};
457 if(!options.legacy) {
458 // encrypt PrivateKeyInfo
459 var rval = pki.wrapRsaPrivateKey(pki.privateKeyToAsn1(rsaKey));
460 rval = pki.encryptPrivateKeyInfo(rval, password, options);
461 return pki.encryptedPrivateKeyToPem(rval);
462 }
463
464 // legacy non-PKCS#8
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 // encrypt private key using OpenSSL legacy key derivation
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 * Decrypts an RSA private key.
531 *
532 * @param pem the PEM-formatted EncryptedPrivateKeyInfo to decrypt.
533 * @param password the password to use.
534 *
535 * @return the RSA key on success, null on failure.
536 */
537pki.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 // use OpenSSL legacy key derivation
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 // decryption already performed above
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 * Derives a PKCS#12 key.
631 *
632 * @param password the password to derive the key material from, null or
633 * undefined for none.
634 * @param salt the salt, as a ByteBuffer, to use.
635 * @param id the PKCS#12 ID byte (1 = key material, 2 = IV, 3 = MAC).
636 * @param iter the iteration count.
637 * @param n the number of bytes to derive from the password.
638 * @param md the message digest to use, defaults to SHA-1.
639 *
640 * @return a ByteBuffer with the bytes derived from the password.
641 */
642pki.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 /* Convert password to Unicode byte buffer + trailing 0-byte. */
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 /* Length of salt and password in BYTES. */
666 var p = passBuf.length();
667 var s = salt.length();
668
669 /* 1. Construct a string, D (the "diversifier"), by concatenating
670 v copies of ID. */
671 var D = new forge.util.ByteBuffer();
672 D.fillWithByte(id, v);
673
674 /* 2. Concatenate copies of the salt together to create a string S of length
675 v * ceil(s / v) bytes (the final copy of the salt may be trunacted
676 to create S).
677 Note that if the salt is the empty string, then so is S. */
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 /* 3. Concatenate copies of the password together to create a string P of
685 length v * ceil(p / v) bytes (the final copy of the password may be
686 truncated to create P).
687 Note that if the password is the empty string, then so is P. */
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 /* 4. Set I=S||P to be the concatenation of S and P. */
695 var I = S;
696 I.putBuffer(P);
697
698 /* 5. Set c=ceil(n / u). */
699 var c = Math.ceil(n / u);
700
701 /* 6. For i=1, 2, ..., c, do the following: */
702 for(var i = 1; i <= c; i++) {
703 /* a) Set Ai=H^r(D||I). (l.e. the rth hash of D||I, H(H(H(...H(D||I)))) */
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 /* b) Concatenate copies of Ai to create a string B of length v bytes (the
714 final copy of Ai may be truncated to create B). */
715 var B = new forge.util.ByteBuffer();
716 for(l = 0; l < v; l++) {
717 B.putByte(buf.at(l % u));
718 }
719
720 /* c) Treating I as a concatenation I0, I1, ..., Ik-1 of v-byte blocks,
721 where k=ceil(s / v) + ceil(p / v), modify I by setting
722 Ij=(Ij+B+1) mod 2v for each j. */
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 /* Add Ai to A. */
738 result.putBuffer(buf);
739 }
740
741 result.truncate(result.length() - n);
742 return result;
743};
744
745/**
746 * Get new Forge cipher object instance.
747 *
748 * @param oid the OID (in string notation).
749 * @param params the ASN.1 params object.
750 * @param password the password to decrypt with.
751 *
752 * @return new cipher object instance.
753 */
754pki.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 * Get new Forge cipher object instance according to PBES2 params block.
777 *
778 * The returned cipher instance is already started using the IV
779 * from PBES2 parameter block.
780 *
781 * @param oid the PKCS#5 PBKDF2 OID (in string notation).
782 * @param params the ASN.1 PBES2-params object.
783 * @param password the password to decrypt with.
784 *
785 * @return new cipher object instance.
786 */
787pki.pbe.getCipherForPBES2 = function(oid, params, password) {
788 // get PBE params
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 // check oids
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 // set PBE params
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 // get PRF message digest
851 var md = prfOidToMessageDigest(capture.prfOid);
852
853 // decrypt private key using pbe with chosen PRF and AES/DES
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 * Get new Forge cipher object instance for PKCS#12 PBE.
864 *
865 * The returned cipher instance is already started using the key & IV
866 * derived from the provided password and PKCS#12 PBE salt.
867 *
868 * @param oid The PKCS#12 PBE OID (in string notation).
869 * @param params The ASN.1 PKCS#12 PBE-params object.
870 * @param password The password to decrypt with.
871 *
872 * @return the new cipher object instance.
873 */
874pki.pbe.getCipherForPKCS12PBE = function(oid, params, password) {
875 // get PBE params
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 // get PRF message digest
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 * OpenSSL's legacy key derivation function.
924 *
925 * See: http://www.openssl.org/docs/crypto/EVP_BytesToKey.html
926 *
927 * @param password the password to derive the key from.
928 * @param salt the salt to use, null for none.
929 * @param dkLen the number of bytes needed for the derived key.
930 * @param [options] the options to use:
931 * [md] an optional message digest object to use.
932 */
933pki.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
950function hash(md, bytes) {
951 return md.start().update(bytes).digest().getBytes();
952}
953
954function prfOidToMessageDigest(prfOid) {
955 // get PRF algorithm, default to SHA-1
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
973function 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
998function createPbkdf2Params(salt, countBytes, dkLen, prfAlgorithm) {
999 var params = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
1000 // salt
1001 asn1.create(
1002 asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, salt),
1003 // iteration count
1004 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
1005 countBytes.getBytes())
1006 ]);
1007 // when PRF algorithm is not SHA-1 default, add key length and PRF algorithm
1008 if(prfAlgorithm !== 'hmacWithSHA1') {
1009 params.value.push(
1010 // key length
1011 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
1012 forge.util.hexToBytes(dkLen.toString(16))),
1013 // AlgorithmIdentifier
1014 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
1015 // algorithm
1016 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
1017 asn1.oidToDer(pki.oids[prfAlgorithm]).getBytes()),
1018 // parameters (null)
1019 asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
1020 ]));
1021 }
1022 return params;
1023}