Skip to content

Commit

Permalink
Merge pull request #58 from gkwang/disallow-insecure
Browse files Browse the repository at this point in the history
Add support to disallow insecure algorithms
  • Loading branch information
gkwang authored Jan 6, 2020
2 parents d134bc0 + ac2ca8d commit 091bc47
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 14 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ var options = {
rsa_pub: fs.readFileSync(__dirname + '/your_rsa.pub'),
pem: fs.readFileSync(__dirname + '/your_public_cert.pem'),
encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes256-cbc',
keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'
keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p',
disallowInsecureEncryptionAlgorithm: true
};

xmlenc.encrypt('content to encrypt', options, function(err, result) {
Expand Down Expand Up @@ -51,6 +52,7 @@ Result:
~~~js
var options = {
key: fs.readFileSync(__dirname + '/your_private_key.key'),
disallowInsecureDecryptionAlgorithm: true;
};

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

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

* EncryptedData using:
* http://www.w3.org/2001/04/xmlenc#aes128-cbc
* http://www.w3.org/2001/04/xmlenc#aes256-cbc
* http://www.w3.org/2001/04/xmlenc#tripledes-cbc
* http://www.w3.org/2001/04/xmlenc#tripledes-cbc (Insecure Algorithm)

Insecure Algorithms can be disabled via disallowInsecureEncryptionAlgorithm/disallowInsecureDecryptionAlgorithm flags when encrypting/decrypting. This flag is off by default in 0.x versions.

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

Expand Down
40 changes: 29 additions & 11 deletions lib/xmlenc.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ var xpath = require('xpath');
var utils = require('./utils');
var pki = require('node-forge').pki;

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

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

if (!options.keyEncryptionAlgorighm)
return callback(new Error('encryption without encrypted key is not supported yet'));

if (options.disallowEncryptionWithInsecureAlgorithm
&& insecureAlgorithms.indexOf(options.keyEncryptionAlgorighm) >= 0) {
return callback(new Error('encryption algorithm ' + options.keyEncryptionAlgorighm + 'is not secure'));
}
switch (options.keyEncryptionAlgorighm) {
case 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p':
return encryptKeyInfoWithScheme(symmetricKey, options, 'RSA-OAEP', callback);
Expand All @@ -55,7 +63,10 @@ function encrypt(content, options, callback) {
return callback(new Error('rsa_pub option is mandatory and you should provide a valid RSA public key'));
if (!options.pem)
return callback(new Error('pem option is mandatory and you should provide a valid x509 certificate encoded as PEM'));

if (options.disallowEncryptionWithInsecureAlgorithm
&& insecureAlgorithms.indexOf(options.keyEncryptionAlgorighm) >= 0) {
return callback(new Error('encryption algorithm ' + options.keyEncryptionAlgorighm + 'is not secure'));
}
options.input_encoding = options.input_encoding || 'utf8';

function generate_symmetric_key(cb) {
Expand Down Expand Up @@ -144,17 +155,20 @@ function decrypt(xml, options, callback) {
return callback(new Error('must provide XML to encrypt'));
if (!options.key)
return callback(new Error('key option is mandatory and you should provide a valid RSA private key'));

try {
var doc = typeof xml === 'string' ? new xmldom.DOMParser().parseFromString(xml) : xml;

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

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

var encrypted = new Buffer(encryptedContent.textContent, 'base64');
var encrypted = Buffer.from(encryptedContent.textContent, 'base64');

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

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

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

function decryptKeyInfoWithScheme(encryptedKey, options, scheme) {
var key = new Buffer(encryptedKey.textContent, 'base64').toString('binary');
var key = Buffer.from(encryptedKey.textContent, 'base64').toString('binary');
var private_key = pki.privateKeyFromPem(options.key);
var decrypted = private_key.decrypt(key, scheme);
return new Buffer(decrypted, 'binary');
return Buffer.from(decrypted, 'binary');
}

function encryptWithAlgorithm(algorithm, symmetricKey, ivLength, content, encoding, callback) {
Expand All @@ -218,7 +236,7 @@ function encryptWithAlgorithm(algorithm, symmetricKey, ivLength, content, encodi
var cipher = crypto.createCipheriv(algorithm, symmetricKey, iv);
// encrypted content
var encrypted = cipher.update(content, encoding, 'binary') + cipher.final('binary');
return callback(null, Buffer.concat([iv, new Buffer(encrypted, 'binary')]));
return callback(null, Buffer.concat([iv, Buffer.from(encrypted, 'binary')]));
});
}

Expand All @@ -237,7 +255,7 @@ function decryptWithAlgorithm(algorithm, symmetricKey, ivLength, content) {
return;
}

return new Buffer(decrypted, 'binary').toString('utf8');
return Buffer.from(decrypted, 'binary').toString('utf8');
}

exports = module.exports = {
Expand Down
78 changes: 78 additions & 0 deletions test/xmlenc.encryptedkey.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,45 @@ describe('encrypt', function() {
});
}

describe('des-ede3-cbc fails', function() {
it('should fail encryption when disallowInsecureEncryptionAlgorithm is set', function(done) {
const options = {
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
pem: fs.readFileSync(__dirname + '/test-auth0.pem'),
key: fs.readFileSync(__dirname + '/test-auth0.key'),
disallowInsecureEncryptionAlgorithm: true,
encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes128-cbc',
keyEncryptionAlgorighm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'
}
//options.encryptionAlgorithm = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc',
//options.keyEncryptionAlgorighm = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5';
xmlenc.encrypt('encrypt me', options, function(err, result) {
assert(err);
done();
});
});

it('should fail decryption when disallowInsecureDecryptionAlgorithm is set', function(done) {
const options = {
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
pem: fs.readFileSync(__dirname + '/test-auth0.pem'),
key: fs.readFileSync(__dirname + '/test-auth0.key'),
encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes128-cbc',
keyEncryptionAlgorighm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'
}
xmlenc.encrypt('encrypt me', options, function(err, result) {
xmlenc.decrypt(result,
{ key: fs.readFileSync(__dirname + '/test-auth0.key'),
disallowInsecureDecryptionAlgorithm: true},
function (err, decrypted) {
assert(err);
done();
});
});
});
});


it('should encrypt and decrypt keyinfo', function (done) {
var options = {
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
Expand Down Expand Up @@ -94,4 +133,43 @@ describe('encrypt', function() {
});
});

it('should fail encrypt when disallowInsecureDecryptionAlgorithm is set', function (done) {
var options = {
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
pem: fs.readFileSync(__dirname + '/test-auth0.pem'),
keyEncryptionAlgorighm: 'http://www.w3.org/2001/04/xmlenc#rsa-1_5',
disallowInsecureEncryptionAlgorithm: true
};

var plaintext = 'The quick brown fox jumps over the lazy dog';

xmlenc.encryptKeyInfo(plaintext, options, function(err, encryptedKeyInfo) {
assert(err);
done();
});
});

it('should encrypt and fail decrypt due to insecure algorithms', function (done) {
var options = {
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
pem: fs.readFileSync(__dirname + '/test-auth0.pem'),
keyEncryptionAlgorighm: 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'
};

var plaintext = 'The quick brown fox jumps over the lazy dog';

xmlenc.encryptKeyInfo(plaintext, options, function(err, encryptedKeyInfo) {
if (err) return done(err);

assert.throws(
function(){xmlenc.decryptKeyInfo(
encryptedKeyInfo,
{key: fs.readFileSync(__dirname + '/test-auth0.key'),
disallowDecryptionWithInsecureAlgorithm: true})},
Error,
"Error thrown due to disallowing insecure algorithms.");

done();
});
});
});

0 comments on commit 091bc47

Please sign in to comment.