Skip to content

Commit 091bc47

Browse files
authored
Merge pull request #58 from gkwang/disallow-insecure
Add support to disallow insecure algorithms
2 parents d134bc0 + ac2ca8d commit 091bc47

File tree

3 files changed

+114
-14
lines changed

3 files changed

+114
-14
lines changed

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ var options = {
1515
rsa_pub: fs.readFileSync(__dirname + '/your_rsa.pub'),
1616
pem: fs.readFileSync(__dirname + '/your_public_cert.pem'),
1717
encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes256-cbc',
18-
keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'
18+
keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p',
19+
disallowInsecureEncryptionAlgorithm: true
1920
};
2021

2122
xmlenc.encrypt('content to encrypt', options, function(err, result) {
@@ -51,6 +52,7 @@ Result:
5152
~~~js
5253
var options = {
5354
key: fs.readFileSync(__dirname + '/your_private_key.key'),
55+
disallowInsecureDecryptionAlgorithm: true;
5456
};
5557

5658
xmlenc.decrypt('<xenc:EncryptedData ..... </xenc:EncryptedData>', options, function(err, result) {
@@ -68,12 +70,14 @@ Currently the library supports:
6870

6971
* EncryptedKey to transport symmetric key using:
7072
* http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p
71-
* http://www.w3.org/2001/04/xmlenc#rsa-1_5
73+
* http://www.w3.org/2001/04/xmlenc#rsa-1_5 (Insecure Algorithm)
7274

7375
* EncryptedData using:
7476
* http://www.w3.org/2001/04/xmlenc#aes128-cbc
7577
* http://www.w3.org/2001/04/xmlenc#aes256-cbc
76-
* http://www.w3.org/2001/04/xmlenc#tripledes-cbc
78+
* http://www.w3.org/2001/04/xmlenc#tripledes-cbc (Insecure Algorithm)
79+
80+
Insecure Algorithms can be disabled via disallowInsecureEncryptionAlgorithm/disallowInsecureDecryptionAlgorithm flags when encrypting/decrypting. This flag is off by default in 0.x versions.
7781

7882
However, you can fork and implement your own algorithm. The code supports adding more algorithms easily
7983

lib/xmlenc.js

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@ var xpath = require('xpath');
44
var utils = require('./utils');
55
var pki = require('node-forge').pki;
66

7+
const insecureAlgorithms = [
8+
//https://www.w3.org/TR/xmlenc-core1/#rsav15note
9+
'http://www.w3.org/2001/04/xmlenc#rsa-1_5',
10+
//https://csrc.nist.gov/News/2017/Update-to-Current-Use-and-Deprecation-of-TDEA
11+
'http://www.w3.org/2001/04/xmlenc#tripledes-cbc'];
712
function encryptKeyInfoWithScheme(symmetricKey, options, scheme, callback) {
813
try {
914
var rsa_pub = pki.publicKeyFromPem(options.rsa_pub);
1015
var encrypted = rsa_pub.encrypt(symmetricKey.toString('binary'), scheme);
11-
var base64EncodedEncryptedKey = new Buffer(encrypted, 'binary').toString('base64');
16+
var base64EncodedEncryptedKey = Buffer.from(encrypted, 'binary').toString('base64');
1217

1318
var params = {
1419
encryptedKey: base64EncodedEncryptedKey,
@@ -33,7 +38,10 @@ function encryptKeyInfo(symmetricKey, options, callback) {
3338

3439
if (!options.keyEncryptionAlgorighm)
3540
return callback(new Error('encryption without encrypted key is not supported yet'));
36-
41+
if (options.disallowEncryptionWithInsecureAlgorithm
42+
&& insecureAlgorithms.indexOf(options.keyEncryptionAlgorighm) >= 0) {
43+
return callback(new Error('encryption algorithm ' + options.keyEncryptionAlgorighm + 'is not secure'));
44+
}
3745
switch (options.keyEncryptionAlgorighm) {
3846
case 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p':
3947
return encryptKeyInfoWithScheme(symmetricKey, options, 'RSA-OAEP', callback);
@@ -55,7 +63,10 @@ function encrypt(content, options, callback) {
5563
return callback(new Error('rsa_pub option is mandatory and you should provide a valid RSA public key'));
5664
if (!options.pem)
5765
return callback(new Error('pem option is mandatory and you should provide a valid x509 certificate encoded as PEM'));
58-
66+
if (options.disallowEncryptionWithInsecureAlgorithm
67+
&& insecureAlgorithms.indexOf(options.keyEncryptionAlgorighm) >= 0) {
68+
return callback(new Error('encryption algorithm ' + options.keyEncryptionAlgorighm + 'is not secure'));
69+
}
5970
options.input_encoding = options.input_encoding || 'utf8';
6071

6172
function generate_symmetric_key(cb) {
@@ -144,17 +155,20 @@ function decrypt(xml, options, callback) {
144155
return callback(new Error('must provide XML to encrypt'));
145156
if (!options.key)
146157
return callback(new Error('key option is mandatory and you should provide a valid RSA private key'));
147-
148158
try {
149159
var doc = typeof xml === 'string' ? new xmldom.DOMParser().parseFromString(xml) : xml;
150160

151161
var symmetricKey = decryptKeyInfo(doc, options);
152162
var encryptionMethod = xpath.select("//*[local-name(.)='EncryptedData']/*[local-name(.)='EncryptionMethod']", doc)[0];
153163
var encryptionAlgorithm = encryptionMethod.getAttribute('Algorithm');
154164

165+
if (options.disallowDecryptionWithInsecureAlgorithm
166+
&& insecureAlgorithms.indexOf(encryptionAlgorithm) >= 0) {
167+
throw new Error('encryption algorithm ' + encryptionAlgorithm + ' is not secure, fail to decrypt');
168+
}
155169
var encryptedContent = xpath.select("//*[local-name(.)='EncryptedData']/*[local-name(.)='CipherData']/*[local-name(.)='CipherValue']", doc)[0];
156170

157-
var encrypted = new Buffer(encryptedContent.textContent, 'base64');
171+
var encrypted = Buffer.from(encryptedContent.textContent, 'base64');
158172

159173
switch (encryptionAlgorithm) {
160174
case 'http://www.w3.org/2001/04/xmlenc#aes128-cbc':
@@ -188,12 +202,16 @@ function decryptKeyInfo(doc, options) {
188202
throw new Error('cant find encryption algorithm');
189203
}
190204

191-
var keyEncryptionAlgorighm = keyEncryptionMethod.getAttribute('Algorithm');
205+
var keyEncryptionAlgorithm = keyEncryptionMethod.getAttribute('Algorithm');
206+
if (options.disallowDecryptionWithInsecureAlgorithm
207+
&& insecureAlgorithms.indexOf(keyEncryptionAlgorithm) >= 0) {
208+
throw new Error('encryption algorithm ' + keyEncryptionAlgorithm + ' is not secure, fail to decrypt');
209+
}
192210
var encryptedKey = keyRetrievalMethodUri ?
193211
xpath.select("//*[local-name(.)='EncryptedKey' and @Id='" + keyRetrievalMethodUri.substring(1) + "']/*[local-name(.)='CipherData']/*[local-name(.)='CipherValue']", keyInfo)[0] :
194212
xpath.select("//*[local-name(.)='CipherValue']", keyInfo)[0];
195213

196-
switch (keyEncryptionAlgorighm) {
214+
switch (keyEncryptionAlgorithm) {
197215
case 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p':
198216
return decryptKeyInfoWithScheme(encryptedKey, options, 'RSA-OAEP');
199217
case 'http://www.w3.org/2001/04/xmlenc#rsa-1_5':
@@ -204,10 +222,10 @@ function decryptKeyInfo(doc, options) {
204222
}
205223

206224
function decryptKeyInfoWithScheme(encryptedKey, options, scheme) {
207-
var key = new Buffer(encryptedKey.textContent, 'base64').toString('binary');
225+
var key = Buffer.from(encryptedKey.textContent, 'base64').toString('binary');
208226
var private_key = pki.privateKeyFromPem(options.key);
209227
var decrypted = private_key.decrypt(key, scheme);
210-
return new Buffer(decrypted, 'binary');
228+
return Buffer.from(decrypted, 'binary');
211229
}
212230

213231
function encryptWithAlgorithm(algorithm, symmetricKey, ivLength, content, encoding, callback) {
@@ -218,7 +236,7 @@ function encryptWithAlgorithm(algorithm, symmetricKey, ivLength, content, encodi
218236
var cipher = crypto.createCipheriv(algorithm, symmetricKey, iv);
219237
// encrypted content
220238
var encrypted = cipher.update(content, encoding, 'binary') + cipher.final('binary');
221-
return callback(null, Buffer.concat([iv, new Buffer(encrypted, 'binary')]));
239+
return callback(null, Buffer.concat([iv, Buffer.from(encrypted, 'binary')]));
222240
});
223241
}
224242

@@ -237,7 +255,7 @@ function decryptWithAlgorithm(algorithm, symmetricKey, ivLength, content) {
237255
return;
238256
}
239257

240-
return new Buffer(decrypted, 'binary').toString('utf8');
258+
return Buffer.from(decrypted, 'binary').toString('utf8');
241259
}
242260

243261
exports = module.exports = {

test/xmlenc.encryptedkey.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,45 @@ describe('encrypt', function() {
5555
});
5656
}
5757

58+
describe('des-ede3-cbc fails', function() {
59+
it('should fail encryption when disallowInsecureEncryptionAlgorithm is set', function(done) {
60+
const options = {
61+
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
62+
pem: fs.readFileSync(__dirname + '/test-auth0.pem'),
63+
key: fs.readFileSync(__dirname + '/test-auth0.key'),
64+
disallowInsecureEncryptionAlgorithm: true,
65+
encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes128-cbc',
66+
keyEncryptionAlgorighm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'
67+
}
68+
//options.encryptionAlgorithm = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc',
69+
//options.keyEncryptionAlgorighm = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5';
70+
xmlenc.encrypt('encrypt me', options, function(err, result) {
71+
assert(err);
72+
done();
73+
});
74+
});
75+
76+
it('should fail decryption when disallowInsecureDecryptionAlgorithm is set', function(done) {
77+
const options = {
78+
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
79+
pem: fs.readFileSync(__dirname + '/test-auth0.pem'),
80+
key: fs.readFileSync(__dirname + '/test-auth0.key'),
81+
encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes128-cbc',
82+
keyEncryptionAlgorighm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'
83+
}
84+
xmlenc.encrypt('encrypt me', options, function(err, result) {
85+
xmlenc.decrypt(result,
86+
{ key: fs.readFileSync(__dirname + '/test-auth0.key'),
87+
disallowInsecureDecryptionAlgorithm: true},
88+
function (err, decrypted) {
89+
assert(err);
90+
done();
91+
});
92+
});
93+
});
94+
});
95+
96+
5897
it('should encrypt and decrypt keyinfo', function (done) {
5998
var options = {
6099
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
@@ -94,4 +133,43 @@ describe('encrypt', function() {
94133
});
95134
});
96135

136+
it('should fail encrypt when disallowInsecureDecryptionAlgorithm is set', function (done) {
137+
var options = {
138+
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
139+
pem: fs.readFileSync(__dirname + '/test-auth0.pem'),
140+
keyEncryptionAlgorighm: 'http://www.w3.org/2001/04/xmlenc#rsa-1_5',
141+
disallowInsecureEncryptionAlgorithm: true
142+
};
143+
144+
var plaintext = 'The quick brown fox jumps over the lazy dog';
145+
146+
xmlenc.encryptKeyInfo(plaintext, options, function(err, encryptedKeyInfo) {
147+
assert(err);
148+
done();
149+
});
150+
});
151+
152+
it('should encrypt and fail decrypt due to insecure algorithms', function (done) {
153+
var options = {
154+
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
155+
pem: fs.readFileSync(__dirname + '/test-auth0.pem'),
156+
keyEncryptionAlgorighm: 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'
157+
};
158+
159+
var plaintext = 'The quick brown fox jumps over the lazy dog';
160+
161+
xmlenc.encryptKeyInfo(plaintext, options, function(err, encryptedKeyInfo) {
162+
if (err) return done(err);
163+
164+
assert.throws(
165+
function(){xmlenc.decryptKeyInfo(
166+
encryptedKeyInfo,
167+
{key: fs.readFileSync(__dirname + '/test-auth0.key'),
168+
disallowDecryptionWithInsecureAlgorithm: true})},
169+
Error,
170+
"Error thrown due to disallowing insecure algorithms.");
171+
172+
done();
173+
});
174+
});
97175
});

0 commit comments

Comments
 (0)