Skip to content

Commit

Permalink
Add support for oaepHash=sha256
Browse files Browse the repository at this point in the history
  • Loading branch information
Leonardo Zanivan committed Jan 15, 2025
1 parent a08c621 commit dfc6ff3
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 30 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
[![Build Status](https://travis-ci.org/auth0/node-xml-encryption.png)](https://travis-ci.org/auth0/node-xml-encryption)

W3C XML Encryption implementation for node.js (http://www.w3.org/TR/xmlenc-core/)
W3C XML Encryption implementation for Node.js (http://www.w3.org/TR/xmlenc-core/)

Supports node >= 12 < 18

node 18 not supported due to https://github.com/nodejs/node/issues/52017 for Triple DES algorithms.
Node 18+ does not support Triple DES algorithms due to https://github.com/nodejs/node/issues/52017

## Usage

Expand All @@ -20,6 +18,7 @@ var options = {
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',
keyEncryptionDigest: 'sha1',
disallowEncryptionWithInsecureAlgorithm: true,
warnInsecureAlgorithm: true
};
Expand Down
4 changes: 2 additions & 2 deletions lib/templates/keyinfo.tpl.xml.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
var escapehtml = require('escape-html');

module.exports = ({ encryptionPublicCert, encryptedKey, keyEncryptionMethod }) => `
module.exports = ({ encryptionPublicCert, encryptedKey, keyEncryptionMethod, keyEncryptionDigest }) => `
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<e:EncryptedKey xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:EncryptionMethod Algorithm="${escapehtml(keyEncryptionMethod)}">
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#${escapehtml(keyEncryptionDigest)}" />
</e:EncryptionMethod>
<KeyInfo>
${encryptionPublicCert}
Expand Down
37 changes: 26 additions & 11 deletions lib/xmlenc.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,22 @@ const insecureAlgorithms = [
//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) {
const padding = scheme === 'RSA-OAEP' ? crypto.constants.RSA_PKCS1_OAEP_PADDING : crypto.constants.RSA_PKCS1_PADDING;
function encryptKeyInfoWithScheme(symmetricKey, options, padding, callback) {
const symmetricKeyBuffer = Buffer.isBuffer(symmetricKey) ? symmetricKey : Buffer.from(symmetricKey, 'utf-8');

try {
var encrypted = crypto.publicEncrypt({
key: options.rsa_pub,
oaepHash: padding == crypto.constants.RSA_PKCS1_OAEP_PADDING ? options.keyEncryptionDigest : undefined,
padding: padding
}, symmetricKeyBuffer);
var base64EncodedEncryptedKey = encrypted.toString('base64');

var params = {
encryptedKey: base64EncodedEncryptedKey,
encryptionPublicCert: '<X509Data><X509Certificate>' + utils.pemToCert(options.pem.toString()) + '</X509Certificate></X509Data>',
keyEncryptionMethod: options.keyEncryptionAlgorithm
keyEncryptionMethod: options.keyEncryptionAlgorithm,
keyEncryptionDigest: options.keyEncryptionDigest,
};

var result = utils.renderTemplate('keyinfo', params);
Expand All @@ -47,13 +48,14 @@ function encryptKeyInfo(symmetricKey, options, callback) {
&& insecureAlgorithms.indexOf(options.keyEncryptionAlgorithm) >= 0) {
return callback(new Error('encryption algorithm ' + options.keyEncryptionAlgorithm + 'is not secure'));
}
options.keyEncryptionDigest = options.keyEncryptionDigest || 'sha1';
switch (options.keyEncryptionAlgorithm) {
case 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p':
return encryptKeyInfoWithScheme(symmetricKey, options, 'RSA-OAEP', callback);
return encryptKeyInfoWithScheme(symmetricKey, options, crypto.constants.RSA_PKCS1_OAEP_PADDING, callback);

case 'http://www.w3.org/2001/04/xmlenc#rsa-1_5':
utils.warnInsecureAlgorithm(options.keyEncryptionAlgorithm, options.warnInsecureAlgorithm);
return encryptKeyInfoWithScheme(symmetricKey, options, 'RSAES-PKCS1-V1_5', callback);
return encryptKeyInfoWithScheme(symmetricKey, options, crypto.constants.RSA_PKCS1_PADDING, callback);

default:
return callback(new Error('encryption key algorithm not supported'));
Expand Down Expand Up @@ -235,6 +237,20 @@ function decryptKeyInfo(doc, options) {
throw new Error('cant find encryption algorithm');
}

let oaepHash = 'sha1';
const keyDigestMethod = xpath.select("//*[local-name(.)='KeyInfo']/*[local-name(.)='EncryptedKey']/*[local-name(.)='EncryptionMethod']/*[local-name(.)='DigestMethod']", doc)[0];
if (keyDigestMethod) {
const keyDigestMethodAlgorithm = keyDigestMethod.getAttribute('Algorithm');
switch (keyDigestMethodAlgorithm) {
case 'http://www.w3.org/2000/09/xmldsig#sha256':
oaepHash = 'sha256';
break;
case 'http://www.w3.org/2000/09/xmldsig#sha512':
oaepHash = 'sha512';
break;
}
}

var keyEncryptionAlgorithm = keyEncryptionMethod.getAttribute('Algorithm');
if (options.disallowDecryptionWithInsecureAlgorithm
&& insecureAlgorithms.indexOf(keyEncryptionAlgorithm) >= 0) {
Expand All @@ -246,19 +262,18 @@ function decryptKeyInfo(doc, options) {

switch (keyEncryptionAlgorithm) {
case 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p':
return decryptKeyInfoWithScheme(encryptedKey, options, 'RSA-OAEP');
return decryptKeyInfoWithScheme(encryptedKey, options, crypto.constants.RSA_PKCS1_OAEP_PADDING, oaepHash);
case 'http://www.w3.org/2001/04/xmlenc#rsa-1_5':
utils.warnInsecureAlgorithm(keyEncryptionAlgorithm, options.warnInsecureAlgorithm);
return decryptKeyInfoWithScheme(encryptedKey, options, 'RSAES-PKCS1-V1_5');
return decryptKeyInfoWithScheme(encryptedKey, options, crypto.constants.RSA_PKCS1_PADDING);
default:
throw new Error('key encryption algorithm ' + keyEncryptionAlgorithm + ' not supported');
}
}

function decryptKeyInfoWithScheme(encryptedKey, options, scheme) {
var padding = scheme === 'RSA-OAEP' ? crypto.constants.RSA_PKCS1_OAEP_PADDING : crypto.constants.RSA_PKCS1_PADDING;
var key = Buffer.from(encryptedKey.textContent, 'base64');
var decrypted = crypto.privateDecrypt({ key: options.key, padding: padding}, key);
function decryptKeyInfoWithScheme(encryptedKey, options, padding, oaepHash) {
const key = Buffer.from(encryptedKey.textContent, 'base64');
const decrypted = crypto.privateDecrypt({ key: options.key, padding, oaepHash}, key);
return Buffer.from(decrypted, 'binary');
}

Expand Down
7 changes: 2 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "xml-encryption",
"version": "3.0.2",
"version": "3.1.0",
"devDependencies": {
"mocha": "^7.1.2",
"should": "^11.2.1",
Expand Down Expand Up @@ -30,8 +30,5 @@
],
"scripts": {
"test": "mocha"
},
"engines": {
"node": ">=12 < 18"
}
}
22 changes: 18 additions & 4 deletions test/xmlenc.encryptedkey.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
var assert = require('assert');
var fs = require('fs');
var should = require('should');
var sinon = require('sinon');
var xmlenc = require('../lib');
var xpath = require('xpath');

describe('encrypt', function() {
let consoleSpy = null;
Expand Down Expand Up @@ -39,6 +37,21 @@ describe('encrypt', function() {
encryptionAlgorithm: 'http://www.w3.org/2009/xmlenc11#aes128-gcm',
keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'
}
},
{
name: 'aes-128-gcm with sha256',
encryptionOptions: {
encryptionAlgorithm: 'http://www.w3.org/2009/xmlenc11#aes128-gcm',
keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p',
keyEncryptionDigest: 'sha256'
}
}, {
name: 'aes-128-gcm with sha512',
encryptionOptions: {
encryptionAlgorithm: 'http://www.w3.org/2009/xmlenc11#aes128-gcm',
keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p',
keyEncryptionDigest: 'sha512'
}
}, {
name: 'des-ede3-cbc',
encryptionOptions: {
Expand Down Expand Up @@ -72,6 +85,7 @@ describe('encrypt', function() {

xmlenc.encrypt(content, options, function(err, result) {
xmlenc.decrypt(result, { key: fs.readFileSync(__dirname + '/test-auth0.key'), warnInsecureAlgorithm: false}, function (err, decrypted) {
if (err) return done(err);
assert.equal(decrypted, content);
done();
});
Expand All @@ -92,7 +106,7 @@ describe('encrypt', function() {
assert(err);
assert(!result);
//should not pop up warns due to options.warnInsecureAlgorithm = false;
consoleSpy.called.should.equal(false);
assert.equal(consoleSpy.called, false);
done();
});
});
Expand Down Expand Up @@ -222,7 +236,7 @@ describe('encrypt', function() {

xmlenc.encryptKeyInfo(plaintext, options, function(err, encryptedKeyInfo) {
if (err) return done(err);
consoleSpy.called.should.equal(true);
assert.equal(consoleSpy.called, true);
assert.throws(
function(){xmlenc.decryptKeyInfo(
encryptedKeyInfo,
Expand Down

0 comments on commit dfc6ff3

Please sign in to comment.