Skip to content

Commit e63d8ce

Browse files
committed
Support padding for tripledes-cbc algorithm
1 parent 9b247a2 commit e63d8ce

File tree

2 files changed

+63
-51
lines changed

2 files changed

+63
-51
lines changed

lib/xmlenc.js

Lines changed: 33 additions & 43 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,7 +122,7 @@ 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-
125+
126126
var decrypted;
127127

128128
try {
@@ -132,58 +132,48 @@ function decrypt(xml, options, callback) {
132132
var encryptionMethod = xpath.select("//*[local-name(.)='EncryptedData']/*[local-name(.)='EncryptionMethod']", doc)[0];
133133
var encryptionAlgorithm = encryptionMethod.getAttribute('Algorithm');
134134

135-
var decipher;
136-
var padding;
135+
var algorithm;
136+
var ivLength;
137137
var encryptedContent = xpath.select("//*[local-name(.)='EncryptedData']/*[local-name(.)='CipherData']/*[local-name(.)='CipherValue']", doc)[0];
138-
138+
139139
var encrypted = new Buffer(encryptedContent.textContent, 'base64');
140-
140+
141141
switch (encryptionAlgorithm) {
142142
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');
143+
algorithm = 'aes-128-cbc';
144+
ivLength = 16;
158145
break;
159146
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');
147+
algorithm = 'aes-256-cbc';
148+
ivLength = 16;
174149
break;
175150
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');
151+
algorithm = 'des-ede3-cbc';
152+
ivLength = 8;
179153
break;
180154
default:
181155
return callback(new Error('encryption algorithm ' + encryptionAlgorithm + ' not supported'));
182156
}
157+
158+
var decipher = crypto.createDecipheriv(algorithm, symmetricKey, encrypted.slice(0,ivLength));
159+
decipher.setAutoPadding(false);
160+
161+
decrypted = decipher.update(encrypted.slice(ivLength), null, 'binary') + decipher.final('binary');
162+
163+
// Remove padding bytes equal to the value of the last byte of the returned data.
164+
var padding = decrypted.charCodeAt(decrypted.length - 1);
165+
if (1 <= padding && padding <= ivLength) {
166+
decrypted = decrypted.substr(0, decrypted.length - padding);
167+
} else {
168+
callback(new Error('padding length invalid'));
169+
return;
170+
}
171+
172+
decrypted = new Buffer(decrypted, 'binary').toString('utf8');
183173
} catch (e) {
184174
return callback(e);
185175
}
186-
176+
187177
callback(null, decrypted);
188178
}
189179

@@ -193,7 +183,7 @@ function decryptKeyInfo(doc, options) {
193183
var keyRetrievalMethodUri;
194184
var keyInfo = xpath.select("//*[local-name(.)='KeyInfo' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc)[0];
195185
var keyEncryptionMethod = xpath.select("//*[local-name(.)='KeyInfo']/*[local-name(.)='EncryptedKey']/*[local-name(.)='EncryptionMethod']", doc)[0];
196-
186+
197187
if (!keyEncryptionMethod) { // try with EncryptedData->KeyInfo->RetrievalMethod
198188
var keyRetrievalMethod = xpath.select("//*[local-name(.)='EncryptedData']/*[local-name(.)='KeyInfo']/*[local-name(.)='RetrievalMethod']", doc)[0];
199189
keyRetrievalMethodUri = keyRetrievalMethod ? keyRetrievalMethod.getAttribute('URI') : null;
@@ -235,7 +225,7 @@ function encryptWithAlgorithm(algorithm, symmetricKey, ivLength, content, encodi
235225
// create a random iv for algorithm
236226
crypto.randomBytes(ivLength, function(err, iv) {
237227
if (err) return callback(err);
238-
228+
239229
var cipher = crypto.createCipheriv(algorithm, symmetricKey, iv);
240230
// encrypted content
241231
var encrypted = cipher.update(content, encoding, 'binary') + cipher.final('binary');

test/xmlenc.encryptedkey.js

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ describe('encrypt', function() {
1212
// cert created with:
1313
// 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
1414
// 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
15+
// openssl x509 -in "test-auth0.pem" -pubkey
1616

1717
var options = {
1818
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
@@ -22,7 +22,7 @@ describe('encrypt', function() {
2222
keyEncryptionAlgorighm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'
2323
};
2424

25-
xmlenc.encrypt('content to encrypt', options, function(err, result) {
25+
xmlenc.encrypt('content to encrypt', options, function(err, result) {
2626
xmlenc.decrypt(result, { key: fs.readFileSync(__dirname + '/test-auth0.key')}, function(err, decrypted) {
2727
assert.equal(decrypted, 'content to encrypt');
2828
done();
@@ -34,7 +34,7 @@ describe('encrypt', function() {
3434
// cert created with:
3535
// 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
3636
// 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
37+
// openssl x509 -in "test-auth0.pem" -pubkey
3838

3939
var options = {
4040
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
@@ -44,7 +44,7 @@ describe('encrypt', function() {
4444
keyEncryptionAlgorighm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'
4545
};
4646

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) {
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) {
4848
xmlenc.decrypt(result, { key: fs.readFileSync(__dirname + '/test-auth0.key')}, function(err, decrypted) {
4949
assert.equal(decrypted, 'Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge');
5050
done();
@@ -56,7 +56,7 @@ describe('encrypt', function() {
5656
// cert created with:
5757
// 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
5858
// 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
59+
// openssl x509 -in "test-auth0.pem" -pubkey
6060

6161
var options = {
6262
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
@@ -95,7 +95,7 @@ describe('encrypt', function() {
9595
// cert created with:
9696
// 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
9797
// 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
98+
// openssl x509 -in "test-auth0.pem" -pubkey
9999

100100
var options = {
101101
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
@@ -105,14 +105,36 @@ describe('encrypt', function() {
105105
keyEncryptionAlgorighm: 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'
106106
};
107107

108-
xmlenc.encrypt('content to encrypt', options, function(err, result) {
108+
xmlenc.encrypt('content to encrypt', options, function(err, result) {
109109
xmlenc.decrypt(result, { key: fs.readFileSync(__dirname + '/test-auth0.key')}, function(err, decrypted) {
110110
assert.equal(decrypted, 'content to encrypt');
111111
done();
112112
});
113113
});
114114
});
115115

116+
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) {
117+
// cert created with:
118+
// 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
119+
// pub key extracted from (only the RSA public key between BEGIN PUBLIC KEY and END PUBLIC KEY)
120+
// openssl x509 -in "test-auth0.pem" -pubkey
121+
122+
var options = {
123+
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
124+
pem: fs.readFileSync(__dirname + '/test-auth0.pem'),
125+
key: fs.readFileSync(__dirname + '/test-auth0.key'),
126+
encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc',
127+
keyEncryptionAlgorighm: 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'
128+
};
129+
130+
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) {
131+
xmlenc.decrypt(result, { key: fs.readFileSync(__dirname + '/test-auth0.key')}, function (err, decrypted) {
132+
assert.equal(decrypted, 'Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge Gnügge');
133+
done();
134+
});
135+
});
136+
});
137+
116138
it('should encrypt and decrypt keyinfo', function (done) {
117139
var options = {
118140
rsa_pub: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'),
@@ -122,7 +144,7 @@ describe('encrypt', function() {
122144

123145
crypto.randomBytes(32, function(err, randomBytes) {
124146
if (err) return done(err);
125-
xmlenc.encryptKeyInfo(randomBytes, options, function(err, result) {
147+
xmlenc.encryptKeyInfo(randomBytes, options, function(err, result) {
126148
if (err) return done(err);
127149
var decryptedRandomBytes = xmlenc.decryptKeyInfo(result, { key: fs.readFileSync(__dirname + '/test-auth0.key')});
128150

0 commit comments

Comments
 (0)