Skip to content

Commit 1df2774

Browse files
authored
Merge pull request #20 from xdmnl/master
Fix autopadding issue with for 3DES algorithm
2 parents 9b247a2 + 98af895 commit 1df2774

File tree

2 files changed

+59
-134
lines changed

2 files changed

+59
-134
lines changed

lib/xmlenc.js

Lines changed: 28 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ function encryptKeyInfoWithScheme(symmetricKey, options, scheme, callback) {
1010
var rsa_pub = pki.publicKeyFromPem(options.rsa_pub);
1111
var encrypted = rsa_pub.encrypt(symmetricKey.toString('binary'), scheme);
1212
var base64EncodedEncryptedKey = new Buffer(encrypted, 'binary').toString('base64');
13-
13+
1414
var params = {
1515
encryptedKey: base64EncodedEncryptedKey,
1616
encryptionPublicCert: '<X509Data><X509Certificate>' + utils.pemToCert(options.pem.toString()) + '</X509Certificate></X509Data>',
1717
keyEncryptionMethod: options.keyEncryptionAlgorighm
1818
};
19-
19+
2020
var result = utils.renderTemplate('keyinfo', params);
2121
callback(null, result);
2222
} catch (e) {
@@ -31,7 +31,7 @@ function encryptKeyInfo(symmetricKey, options, callback) {
3131
return callback(new Error('must provide options.rsa_pub with public key RSA'));
3232
if (!options.pem)
3333
return callback(new Error('must provide options.pem with certificate'));
34-
34+
3535
if (!options.keyEncryptionAlgorighm)
3636
return callback(new Error('encryption without encrypted key is not supported yet'));
3737

@@ -122,8 +122,6 @@ function decrypt(xml, options, callback) {
122122
return callback(new Error('must provide XML to encrypt'));
123123
if (!options.key)
124124
return callback(new Error('key option is mandatory and you should provide a valid RSA private key'));
125-
126-
var decrypted;
127125

128126
try {
129127
var doc = typeof xml === 'string' ? new xmldom.DOMParser().parseFromString(xml) : xml;
@@ -132,59 +130,23 @@ function decrypt(xml, options, callback) {
132130
var encryptionMethod = xpath.select("//*[local-name(.)='EncryptedData']/*[local-name(.)='EncryptionMethod']", doc)[0];
133131
var encryptionAlgorithm = encryptionMethod.getAttribute('Algorithm');
134132

135-
var decipher;
136-
var padding;
137133
var encryptedContent = xpath.select("//*[local-name(.)='EncryptedData']/*[local-name(.)='CipherData']/*[local-name(.)='CipherValue']", doc)[0];
138-
134+
139135
var encrypted = new Buffer(encryptedContent.textContent, 'base64');
140-
136+
141137
switch (encryptionAlgorithm) {
142138
case 'http://www.w3.org/2001/04/xmlenc#aes128-cbc':
143-
decipher = crypto.createDecipheriv('aes-128-cbc', symmetricKey, encrypted.slice(0, 16));
144-
145-
decipher.setAutoPadding(false);
146-
decrypted = decipher.update(encrypted.slice(16), null, 'binary') + decipher.final('binary');
147-
148-
// Remove padding bytes equal to the value of the last byte of the returned data.
149-
padding = decrypted.charCodeAt(decrypted.length - 1);
150-
if (1 <= padding && padding <= 16) {
151-
decrypted = decrypted.substr(0, decrypted.length - padding);
152-
} else {
153-
callback(new Error('padding length invalid'));
154-
return;
155-
}
156-
157-
decrypted = new Buffer(decrypted, 'binary').toString('utf8');
158-
break;
139+
return callback(null, decryptWithAlgorithm('aes-128-cbc', symmetricKey, 16, encrypted));
159140
case 'http://www.w3.org/2001/04/xmlenc#aes256-cbc':
160-
decipher = crypto.createDecipheriv('aes-256-cbc', symmetricKey, encrypted.slice(0, 16));
161-
162-
decipher.setAutoPadding(false);
163-
decrypted = decipher.update(encrypted.slice(16), null, 'binary') + decipher.final('binary');
164-
165-
// Remove padding bytes equal to the value of the last byte of the returned data.
166-
padding = decrypted.charCodeAt(decrypted.length - 1);
167-
if (1 <= padding && padding <= 16) {
168-
decrypted = decrypted.substr(0, decrypted.length - padding);
169-
} else {
170-
callback(new Error('padding length invalid'));
171-
return;
172-
}
173-
decrypted = new Buffer(decrypted, 'binary').toString('utf8');
174-
break;
141+
return callback(null, decryptWithAlgorithm('aes-256-cbc', symmetricKey, 16, encrypted));
175142
case 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc':
176-
decipher = crypto.createDecipheriv('des-ede3-cbc', symmetricKey, encrypted.slice(0,8));
177-
decrypted = decipher.update(encrypted.slice(8), null, 'binary') + decipher.final('binary');
178-
decrypted = new Buffer(decrypted, 'binary').toString('utf8');
179-
break;
143+
return callback(null, decryptWithAlgorithm('des-ede3-cbc', symmetricKey, 8, encrypted));
180144
default:
181145
return callback(new Error('encryption algorithm ' + encryptionAlgorithm + ' not supported'));
182146
}
183147
} catch (e) {
184148
return callback(e);
185149
}
186-
187-
callback(null, decrypted);
188150
}
189151

190152
function decryptKeyInfo(doc, options) {
@@ -193,7 +155,7 @@ function decryptKeyInfo(doc, options) {
193155
var keyRetrievalMethodUri;
194156
var keyInfo = xpath.select("//*[local-name(.)='KeyInfo' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0];
195157
var keyEncryptionMethod = xpath.select("//*[local-name(.)='KeyInfo']/*[local-name(.)='EncryptedKey']/*[local-name(.)='EncryptionMethod']", doc)[0];
196-
158+
197159
if (!keyEncryptionMethod) { // try with EncryptedData->KeyInfo->RetrievalMethod
198160
var keyRetrievalMethod = xpath.select("//*[local-name(.)='EncryptedData']/*[local-name(.)='KeyInfo']/*[local-name(.)='RetrievalMethod']", doc)[0];
199161
keyRetrievalMethodUri = keyRetrievalMethod ? keyRetrievalMethod.getAttribute('URI') : null;
@@ -235,14 +197,32 @@ function encryptWithAlgorithm(algorithm, symmetricKey, ivLength, content, encodi
235197
// create a random iv for algorithm
236198
crypto.randomBytes(ivLength, function(err, iv) {
237199
if (err) return callback(err);
238-
200+
239201
var cipher = crypto.createCipheriv(algorithm, symmetricKey, iv);
240202
// encrypted content
241203
var encrypted = cipher.update(content, encoding, 'binary') + cipher.final('binary');
242204
return callback(null, Buffer.concat([iv, new Buffer(encrypted, 'binary')]));
243205
});
244206
}
245207

208+
function decryptWithAlgorithm(algorithm, symmetricKey, ivLength, content) {
209+
var decipher = crypto.createDecipheriv(algorithm, symmetricKey, content.slice(0,ivLength));
210+
decipher.setAutoPadding(false);
211+
212+
var decrypted = decipher.update(content.slice(ivLength), null, 'binary') + decipher.final('binary');
213+
214+
// Remove padding bytes equal to the value of the last byte of the returned data.
215+
var padding = decrypted.charCodeAt(decrypted.length - 1);
216+
if (1 <= padding && padding <= ivLength) {
217+
decrypted = decrypted.substr(0, decrypted.length - padding);
218+
} else {
219+
callback(new Error('padding length invalid'));
220+
return;
221+
}
222+
223+
return new Buffer(decrypted, 'binary').toString('utf8');
224+
}
225+
246226
exports = module.exports = {
247227
decrypt: decrypt,
248228
encrypt: encrypt,

test/xmlenc.encryptedkey.js

Lines changed: 31 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -8,110 +8,55 @@ var xpath = require('xpath');
88

99
describe('encrypt', function() {
1010

11-
it('should encrypt and decrypt xml (aes256-cbc)', function (done) {
12-
// cert created with:
13-
// openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/CN=auth0.auth0.com/O=Auth0 LLC/C=US/ST=Washington/L=Redmond' -keyout auth0.key -out auth0.pem
14-
// pub key extracted from (only the RSA public key between BEGIN PUBLIC KEY and END PUBLIC KEY)
15-
// openssl x509 -in "test-auth0.pem" -pubkey
16-
17-
var options = {
18-
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
19-
pem: fs.readFileSync(__dirname + '/test-auth0.pem'),
20-
key: fs.readFileSync(__dirname + '/test-auth0.key'),
11+
var algorithms = [{
12+
name: 'aes-256-cbc',
13+
encryptionOptions: {
2114
encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes256-cbc',
2215
keyEncryptionAlgorighm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'
23-
};
24-
25-
xmlenc.encrypt('content to encrypt', options, function(err, result) {
26-
xmlenc.decrypt(result, { key: fs.readFileSync(__dirname + '/test-auth0.key')}, function(err, decrypted) {
27-
assert.equal(decrypted, 'content to encrypt');
28-
done();
29-
});
30-
});
31-
});
32-
33-
it('should encrypt and decrypt xml (aes256-cbc with utf8 chars)', function (done) {
34-
// cert created with:
35-
// openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/CN=auth0.auth0.com/O=Auth0 LLC/C=US/ST=Washington/L=Redmond' -keyout auth0.key -out auth0.pem
36-
// pub key extracted from (only the RSA public key between BEGIN PUBLIC KEY and END PUBLIC KEY)
37-
// openssl x509 -in "test-auth0.pem" -pubkey
38-
39-
var options = {
40-
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
41-
pem: fs.readFileSync(__dirname + '/test-auth0.pem'),
42-
key: fs.readFileSync(__dirname + '/test-auth0.key'),
43-
encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes256-cbc',
44-
keyEncryptionAlgorighm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'
45-
};
46-
47-
xmlenc.encrypt('Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge', options, function(err, result) {
48-
xmlenc.decrypt(result, { key: fs.readFileSync(__dirname + '/test-auth0.key')}, function(err, decrypted) {
49-
assert.equal(decrypted, 'Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge');
50-
done();
51-
});
52-
});
53-
});
54-
55-
it('should encrypt and decrypt xml (aes128-cbc) with utf8 chars', function (done) {
56-
// cert created with:
57-
// openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/CN=auth0.auth0.com/O=Auth0 LLC/C=US/ST=Washington/L=Redmond' -keyout auth0.key -out auth0.pem
58-
// pub key extracted from (only the RSA public key between BEGIN PUBLIC KEY and END PUBLIC KEY)
59-
// openssl x509 -in "test-auth0.pem" -pubkey
60-
61-
var options = {
62-
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
63-
pem: fs.readFileSync(__dirname + '/test-auth0.pem'),
64-
key: fs.readFileSync(__dirname + '/test-auth0.key'),
16+
}
17+
}, {
18+
name: 'aes-128-cbc',
19+
encryptionOptions: {
6520
encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes128-cbc',
6621
keyEncryptionAlgorighm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'
67-
};
22+
}
23+
}, {
24+
name: 'des-ede3-cbc',
25+
encryptionOptions: {
26+
encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc',
27+
keyEncryptionAlgorighm: 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'
28+
}
29+
}];
6830

69-
xmlenc.encrypt('Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge', options, function (err, result) {
70-
xmlenc.decrypt(result, { key: fs.readFileSync(__dirname + '/test-auth0.key')}, function (err, decrypted) {
71-
assert.equal(decrypted, 'Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge');
72-
done();
31+
algorithms.forEach(function (algorithm) {
32+
describe(algorithm.name, function () {
33+
it('should encrypt and decrypt xml', function (done) {
34+
_shouldEncryptAndDecrypt('content to encrypt', algorithm.encryptionOptions, done);
7335
});
74-
});
75-
});
76-
77-
it('should encrypt and decrypt xml (aes128-cbc)', function (done) {
78-
var options = {
79-
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
80-
pem: fs.readFileSync(__dirname + '/test-auth0.pem'),
81-
key: fs.readFileSync(__dirname + '/test-auth0.key'),
82-
encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes128-cbc',
83-
keyEncryptionAlgorighm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'
84-
};
8536

86-
xmlenc.encrypt('content to encrypt', options, function (err, result) {
87-
xmlenc.decrypt(result, { key: fs.readFileSync(__dirname + '/test-auth0.key')}, function (err, decrypted) {
88-
assert.equal(decrypted, 'content to encrypt');
89-
done();
37+
it('should encrypt and decrypt xml with utf8 chars', function (done) {
38+
_shouldEncryptAndDecrypt('Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge', algorithm.encryptionOptions, done);
9039
});
9140
});
9241
});
9342

94-
it('should encrypt and decrypt xml (encryption: http://www.w3.org/2001/04/xmlenc#tripledes-cbc, keyEncryption: http://www.w3.org/2001/04/xmlenc#rsa-1_5)', function (done) {
43+
function _shouldEncryptAndDecrypt(content, options, done) {
9544
// cert created with:
9645
// openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/CN=auth0.auth0.com/O=Auth0 LLC/C=US/ST=Washington/L=Redmond' -keyout auth0.key -out auth0.pem
9746
// pub key extracted from (only the RSA public key between BEGIN PUBLIC KEY and END PUBLIC KEY)
98-
// openssl x509 -in "test-auth0.pem" -pubkey
47+
// openssl x509 -in "test-auth0.pem" -pubkey
9948

100-
var options = {
101-
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
102-
pem: fs.readFileSync(__dirname + '/test-auth0.pem'),
103-
key: fs.readFileSync(__dirname + '/test-auth0.key'),
104-
encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc',
105-
keyEncryptionAlgorighm: 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'
106-
};
49+
options.rsa_pub = fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
50+
options.pem = fs.readFileSync(__dirname + '/test-auth0.pem'),
51+
options.key = fs.readFileSync(__dirname + '/test-auth0.key'),
10752

108-
xmlenc.encrypt('content to encrypt', options, function(err, result) {
109-
xmlenc.decrypt(result, { key: fs.readFileSync(__dirname + '/test-auth0.key')}, function(err, decrypted) {
110-
assert.equal(decrypted, 'content to encrypt');
53+
xmlenc.encrypt(content, options, function(err, result) {
54+
xmlenc.decrypt(result, { key: fs.readFileSync(__dirname + '/test-auth0.key')}, function (err, decrypted) {
55+
assert.equal(decrypted, content);
11156
done();
11257
});
11358
});
114-
});
59+
}
11560

11661
it('should encrypt and decrypt keyinfo', function (done) {
11762
var options = {
@@ -122,7 +67,7 @@ describe('encrypt', function() {
12267

12368
crypto.randomBytes(32, function(err, randomBytes) {
12469
if (err) return done(err);
125-
xmlenc.encryptKeyInfo(randomBytes, options, function(err, result) {
70+
xmlenc.encryptKeyInfo(randomBytes, options, function(err, result) {
12671
if (err) return done(err);
12772
var decryptedRandomBytes = xmlenc.decryptKeyInfo(result, { key: fs.readFileSync(__dirname + '/test-auth0.key')});
12873

0 commit comments

Comments
 (0)