Skip to content

Commit 97f5218

Browse files
committed
10 compatability
1 parent b3ab153 commit 97f5218

File tree

7 files changed

+152
-210
lines changed

7 files changed

+152
-210
lines changed

lib/mcapi/crypto/jwe-crypto.js

Lines changed: 22 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
const forge = require('node-forge');
2-
const fs = require('fs');
32
const utils = require('../utils/utils');
43
const nodeCrypto = require('crypto');
54

@@ -19,7 +18,7 @@ function JweCrypto(config) {
1918

2019
this.privateKey = getPrivateKey(config);
2120

22-
this.publicKeyFingerprint = config.publicKeyFingerprint || computePublicFingerprint.call(this, config);
21+
this.publicKeyFingerprint = config.publicKeyFingerprint || computePublicFingerprint(config, this.encryptionCertificate);
2322

2423
this.encryptedValueFieldName = config.encryptedValueFieldName;
2524

@@ -40,7 +39,7 @@ function JweCrypto(config) {
4039
"enc": "A256GCM"
4140
};
4241

43-
const encodedJweHeader = Buffer.from(JSON.stringify(jweHeader)).toString('base64url');
42+
const encodedJweHeader = Buffer.from(utils.toEncodedString(JSON.stringify(jweHeader), 'binary', 'base64url'));
4443

4544
const secretKey = nodeCrypto.randomBytes(32);
4645
const secretKeyBuffer = Buffer.from(secretKey, 'binary');
@@ -62,10 +61,10 @@ function JweCrypto(config) {
6261
cipherText += cipher.final('base64');
6362
const authTag = cipher.getAuthTag().toString("base64");
6463

65-
const encodedEncryptedSecretKey = Buffer.from(encryptedSecretKey, 'binary').toString('base64url');
66-
const encodedIv = Buffer.from(iv, 'binary').toString('base64url');
67-
const encodedEncryptedText = Buffer.from(cipherText, "base64").toString('base64url');
68-
const encodedAuthTag = Buffer.from(authTag, "base64").toString('base64url');
64+
const encodedEncryptedSecretKey = utils.toEncodedString(encryptedSecretKey, 'binary', 'base64url');
65+
const encodedIv = utils.toEncodedString(iv, 'binary', 'base64url');
66+
const encodedEncryptedText = utils.toEncodedString(cipherText, "base64", 'base64url');
67+
const encodedAuthTag = utils.toEncodedString(authTag, "base64", 'base64url');
6968

7069
const encryptedData = serialize(encodedJweHeader, encodedEncryptedSecretKey, encodedIv, encodedEncryptedText, encodedAuthTag);
7170
return { [this.encryptedValueFieldName] : encryptedData};
@@ -83,18 +82,16 @@ function JweCrypto(config) {
8382
}
8483

8584
const jweTokenParts = deSerialize(encryptedData);
86-
const jweHeader = Buffer.from(jweTokenParts[0], 'base64url').toString('utf8');
87-
const encryptedSecretKey = Buffer.from(jweTokenParts[1], 'base64url');
88-
const iv = Buffer.from(jweTokenParts[2], 'base64url');
89-
const encryptedText = Buffer.from(jweTokenParts[3], 'base64url');
90-
const authTag = Buffer.from(jweTokenParts[4], 'base64url');
91-
92-
// const node10 = utils.isNode10()
85+
const jweHeader = Buffer.from(jweTokenParts[0], 'base64').toString('utf8');
86+
const encryptedSecretKey = Buffer.from(jweTokenParts[1], 'base64');
87+
const iv = Buffer.from(jweTokenParts[2], 'base64');
88+
const encryptedText = Buffer.from(jweTokenParts[3], 'base64');
89+
const authTag = Buffer.from(jweTokenParts[4], 'base64');
9390

9491
let secretKey = nodeCrypto.privateDecrypt(
9592
{
9693
key: this.privateKey,
97-
padding: nodeCrypto.constants.RSA_PKCS1_OAEP_PADDING,
94+
padding: nodeCrypto.constants.RSA_NO_PADDING,
9895
oaepHash: "sha256"
9996
},
10097
Buffer.from(encryptedSecretKey, 'binary')
@@ -143,11 +140,12 @@ function deSerialize(jweToken) {
143140
* @private
144141
*/
145142
function readPublicCertificate(publicCertificatePath) {
146-
const certificateContent = fs.readFileSync(publicCertificatePath, 'binary');
147-
if (!certificateContent || certificateContent.length <= 1) {
148-
throw new Error('Public certificate content is not valid');
143+
const publicCertificate = utils.readPublicCertificate(publicCertificatePath);
144+
if(publicCertificate){
145+
return forge.pki.certificateToPem(publicCertificate);
146+
}else{
147+
return null;
149148
}
150-
return certificateContent;
151149
}
152150

153151
/**
@@ -166,52 +164,14 @@ function getPrivateKey(config) {
166164
* @private
167165
* @param config Configuration object
168166
*/
169-
function computePublicFingerprint(config) {
170-
let fingerprint = null;
171-
if (config && config.publicKeyFingerprintType) {
172-
switch (config.publicKeyFingerprintType) {
173-
case "certificate":
174-
fingerprint = publicCertificateFingerprint.call(this, this.encryptionCertificate);
175-
break;
176-
case "publicKey":
177-
fingerprint = publicKeyFingerprint.call(this, this.encryptionCertificate);
178-
break;
179-
}
180-
}
181-
return fingerprint;
182-
}
183-
184-
/**
185-
* Get SHA-256 certificate fingerprint from a X509 certificate
186-
*
187-
* @private
188-
* @param {string} publicCertificate PEM
189-
*/
190-
function publicCertificateFingerprint(publicCertificate) {
191-
if (!publicCertificate || publicCertificate.length <= 1) {
192-
throw new Error('Public certificate content is not valid');
193-
}
194-
let hexFingerprint = new nodeCrypto.X509Certificate(publicCertificate).fingerprint256;
195-
hexFingerprint = hexFingerprint.replaceAll(":", "");
196-
return Buffer.from(hexFingerprint, 'hex').toString('base64');
197-
}
198-
199-
/**
200-
* Get SHA-256 public Key fingerprint from a X509 certificate
201-
*
202-
* @private
203-
* @param {string} publicCertificate PEM
204-
*/
205-
function publicKeyFingerprint(publicKey) {
206-
if (!publicKey || publicKey.length <= 1) {
207-
throw new Error('Public certificate content is not valid');
167+
function computePublicFingerprint(config, encryptionCertificate) {
168+
if(config && encryptionCertificate) {
169+
return utils.computePublicFingerprint(config, forge.pki.certificateFromPem(encryptionCertificate), 'base64');
170+
} else {
171+
return null;
208172
}
209-
let hexFingerprint = new nodeCrypto.X509Certificate(publicKey).fingerprint256;
210-
hexFingerprint = hexFingerprint.replaceAll(":", "");
211-
return Buffer.from(hexFingerprint, 'hex').toString('base64');
212173
}
213174

214-
215175
/**
216176
* Check if the passed configuration is valid
217177
* @throws {Error} if the config is not valid

lib/mcapi/crypto/legacy-crypto.js

Lines changed: 16 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
const forge = require('node-forge');
2-
const fs = require('fs');
32
const utils = require('../utils/utils');
43

54
/**
@@ -15,14 +14,14 @@ function LegacyCrypto(config) {
1514
isValidConfig.call(this, config);
1615

1716
// Load public certificate (for encryption)
18-
this.encryptionCertificate = readPublicCertificate(config.encryptionCertificate);
17+
this.encryptionCertificate = utils.readPublicCertificate(config.encryptionCertificate);
1918

2019
// Load private key (for decryption)
2120
this.privateKey = utils.getPrivateKey(config);
2221

2322
this.encoding = config.dataEncoding;
2423

25-
this.publicKeyFingerprint = config.publicKeyFingerprint || computePublicFingerprint.call(this, config);
24+
this.publicKeyFingerprint = config.publicKeyFingerprint || utils.computePublicFingerprint(config, this.encryptionCertificate, this.encoding);
2625

2726
this.publicKeyFingerprintFieldName = config.publicKeyFingerprintFieldName;
2827

@@ -152,76 +151,6 @@ function createOAEPOptions(asymmetricCipher, oaepHashingAlgorithm) {
152151
}
153152
}
154153

155-
/**
156-
* @private
157-
*/
158-
function generateSecretKey(algorithm, size, digest) {
159-
if (algorithm === 'AES') {
160-
const key = forge.random.getBytesSync(size / 8);
161-
const md = createMessageDigest(digest);
162-
md.update(key);
163-
return md.digest().getBytes(16);
164-
}
165-
throw new Error('Unsupported symmetric algorithm');
166-
}
167-
168-
/**
169-
* @private
170-
*/
171-
function readPublicCertificate(publicCertificatePath) {
172-
const certificateContent = fs.readFileSync(publicCertificatePath);
173-
if (!certificateContent || certificateContent.length <= 1) {
174-
throw new Error('Public certificate content is not valid');
175-
}
176-
return forge.pki.certificateFromPem(certificateContent);
177-
}
178-
179-
/**
180-
* @private
181-
* @param config Configuration object
182-
*/
183-
function computePublicFingerprint(config) {
184-
let fingerprint = null;
185-
if (config && config.publicKeyFingerprintType) {
186-
switch (config.publicKeyFingerprintType) {
187-
case "certificate":
188-
fingerprint = publicCertificateFingerprint.call(this, this.encryptionCertificate);
189-
break;
190-
case "publicKey":
191-
fingerprint = publicKeyFingerprint.call(this, this.encryptionCertificate);
192-
break;
193-
}
194-
}
195-
return fingerprint;
196-
}
197-
198-
/**
199-
* Get SHA-256 certificate fingerprint from a X509 certificate
200-
*
201-
* @private
202-
* @param {string} publicCertificate PEM
203-
*/
204-
function publicCertificateFingerprint(publicCertificate) {
205-
const bytes = forge.asn1.toDer(forge.pki.certificateToAsn1(publicCertificate)).getBytes();
206-
const md = createMessageDigest('SHA-256');
207-
md.update(bytes);
208-
return utils.bytesToString(md.digest().getBytes(), this.encoding);
209-
}
210-
211-
/**
212-
* Get SHA-256 public Key fingerprint from a X509 certificate
213-
*
214-
* @private
215-
* @param {string} publicCertificate PEM
216-
*/
217-
function publicKeyFingerprint(publicCertificate) {
218-
return forge.pki.getPublicKeyFingerprint(publicCertificate.publicKey, {
219-
type: 'SubjectPublicKeyInfo',
220-
md: createMessageDigest('SHA-256'),
221-
encoding: 'hex'
222-
});
223-
}
224-
225154
/**
226155
* @private
227156
*/
@@ -236,6 +165,20 @@ function createMessageDigest(digest) {
236165
}
237166
}
238167

168+
169+
/**
170+
* @private
171+
*/
172+
function generateSecretKey(algorithm, size, digest) {
173+
if (algorithm === 'AES') {
174+
const key = forge.random.getBytesSync(size / 8);
175+
const md = createMessageDigest(digest);
176+
md.update(key);
177+
return md.digest().getBytes(16);
178+
}
179+
throw new Error('Unsupported symmetric algorithm');
180+
}
181+
239182
/**
240183
* Check if the passed configuration is valid
241184
* @throws {Error} if the config is not valid

lib/mcapi/utils/utils.js

Lines changed: 87 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,10 @@ const fs = require('fs');
77
* @version 1.0.0
88
*/
99

10-
/**
11-
* Check if a value is set
12-
* @param value value to check
13-
* @returns {boolean} True if set, false otherwise
14-
*/
1510
module.exports.isSet = function (value) {
1611
return typeof value !== "undefined" && value !== null && value.length !== 0;
1712
};
13+
1814
isSet = function (value) {
1915
return typeof value !== "undefined" && value !== null && value.length !== 0;
2016
};
@@ -39,6 +35,19 @@ module.exports.bytesToString = function (bytes, dataEncoding) {
3935
}
4036
};
4137

38+
function bytesToString(bytes, dataEncoding) {
39+
if (typeof bytes === "undefined" || bytes === null) {
40+
throw new Error("Input not valid");
41+
}
42+
switch (dataEncoding.toLowerCase()) {
43+
case 'hex':
44+
return forge.util.bytesToHex(bytes);
45+
case 'base64':
46+
default:
47+
return forge.util.encode64(bytes);
48+
}
49+
};
50+
4251
/**
4352
* Converts a (hex or base64) string into a 'binary' encoded string of bytes.
4453
*
@@ -59,6 +68,31 @@ module.exports.stringToBytes = function (value, dataEncoding) {
5968
}
6069
};
6170

71+
/**
72+
* Convert a string object from format to format.
73+
* Extends toString to support base64url like node 13+
74+
*
75+
* @param {Object|string} value string to be encoded
76+
* @param {Object|string} fromFormat values current format
77+
* @param {Object|string} toFormat values transformed format
78+
* @returns {string}
79+
*/
80+
module.exports.toEncodedString = function(value, fromFormat, toFormat) {
81+
let result = Buffer.from(value, fromFormat)
82+
if (toFormat === 'base64url') {
83+
result = result.toString('base64')
84+
result = result.replace((/\+/g), "-")
85+
result = result.replace((/\\/g), "_")
86+
return result.replace((/=/g), "")
87+
} else {
88+
return result.toString(toFormat)
89+
}
90+
}
91+
92+
module.exports.toByteArray = function(value, fromFormat) {
93+
return Buffer.from(value, fromFormat)
94+
}
95+
6296
/**
6397
* Convert a json object or json string to string
6498
*
@@ -144,17 +178,6 @@ function deleteRoot(obj, paths, properties){
144178
}
145179
}
146180

147-
// module.exports.isNode10 = function(){
148-
// const majorVersion = parseInt(parseInt(process.version.substring(1,3)));
149-
// if(majorVersion < 10){
150-
// throw new Error("Only node version 10+ supported");
151-
// }
152-
// if(majorVersion === 10){
153-
// return true;
154-
// }
155-
// return false;
156-
// };
157-
158181
module.exports.getPrivateKey = function(config){
159182
if (config.privateKey) {
160183
return loadPrivateKey(config.privateKey);
@@ -240,3 +263,51 @@ function loadPrivateKey(privateKeyPath) {
240263
return forge.pki.privateKeyFromAsn1(forge.asn1.fromDer(privateKeyContent));
241264
}
242265

266+
module.exports.readPublicCertificate = function(publicCertificatePath){
267+
const certificateContent = fs.readFileSync(publicCertificatePath);
268+
if (!certificateContent || certificateContent.length <= 1) {
269+
throw new Error('Public certificate content is not valid');
270+
}
271+
return forge.pki.certificateFromPem(certificateContent);
272+
};
273+
274+
module.exports.computePublicFingerprint = function(config, encryptionCertificate, encoding) {
275+
let fingerprint = null;
276+
if (config && config.publicKeyFingerprintType) {
277+
switch (config.publicKeyFingerprintType) {
278+
case "certificate":
279+
fingerprint = publicCertificateFingerprint(encryptionCertificate, encoding);
280+
break;
281+
case "publicKey":
282+
fingerprint = publicKeyFingerprint(encryptionCertificate);
283+
break;
284+
}
285+
}
286+
return fingerprint;
287+
};
288+
289+
function publicCertificateFingerprint(publicCertificate, encoding) {
290+
const bytes = forge.asn1.toDer(forge.pki.certificateToAsn1(publicCertificate)).getBytes();
291+
const md = createMessageDigest('SHA-256');
292+
md.update(bytes);
293+
return bytesToString(md.digest().getBytes(), encoding);
294+
}
295+
296+
function publicKeyFingerprint(publicCertificate) {
297+
return forge.pki.getPublicKeyFingerprint(publicCertificate.publicKey, {
298+
type: 'SubjectPublicKeyInfo',
299+
md: createMessageDigest('SHA-256'),
300+
encoding: 'hex'
301+
});
302+
}
303+
304+
function createMessageDigest(digest) {
305+
switch (digest.toUpperCase()) {
306+
case 'SHA-256':
307+
case 'SHA256':
308+
return forge.md.sha256.create();
309+
case 'SHA-512':
310+
case 'SHA512':
311+
return forge.md.sha512.create();
312+
}
313+
}

0 commit comments

Comments
 (0)