mastercard-api-core
Version:
Core functionality for MasterCard API
215 lines (168 loc) • 6.8 kB
JavaScript
var fs = require('fs');
var forge = require('node-forge');
var utils = require('../../utils');
var error = require('../../error');
function FieldLevelEncryption(opts) {
if (opts.publicCertificatePath != null) {
this.publicCertificate = _readPublicCertificate(opts.publicCertificatePath);
}
if (opts.keystorePath != null) {
this.privateKey = _getPrivateKey(opts.keystorePath, opts.privateKeyAlias, opts.privateKeyPassword);
} else if (opts.privateKeyPath != null) {
this.privateKey = _loadPrivateKey(opts.privateKeyPath);
}
this.config = opts.config;
this.publicKeyFingerprint = opts.publicKeyFingerprint;
}
function encryptPlainText(plainText) {
if (this.publicCertificate == null) {
throw new error.SDKError('Public certificate not found');
}
var config = this.config;
// generate a random IV
var iv = forge.random.getBytesSync(16);
// generate secret key
var secretKey = _generateSecretKey(config.symmetricCipher, config.symmetricKeysize, config.publicKeyFingerprintHashing);
// create cipher
var cipher = forge.cipher.createCipher(config.symmetricAlgorithm, secretKey);
// encrypt payload
cipher.start({ iv: iv });
cipher.update(forge.util.createBuffer(plainText, 'utf8'));
cipher.finish();
var encrypted = cipher.output;
// encrypt secret key with issuer key
var encryptedKey = this.publicCertificate.publicKey.encrypt(secretKey, config.asymmetricCipher, _createOAEPOptions(config.asymmetricCipher, config.oaepHashingAlgorithm));
// public key fingerprint
var fingerprint = this.publicKeyFingerprint;
if (fingerprint == null) {
var bytes = forge.asn1.toDer(forge.pki.certificateToAsn1(this.publicCertificate)).getBytes();
var md = _createMessageDigest(config.publicKeyFingerprintHashing);
md.update(bytes);
fingerprint = _bytesToString(md.digest().getBytes(), config.dataEncoding);
}
return {
cipherText: _bytesToString(encrypted.getBytes(), config.dataEncoding),
iv: _bytesToString(iv, config.dataEncoding),
encryptedKey: _bytesToString(encryptedKey, config.dataEncoding),
oaepHashingAlgorithm: config.oaepHashingAlgorithm,
publicKeyFingerprint: fingerprint,
}
}
function decryptCipherText(cipherText, iv, encryptedKey, oaepHashingAlgorithm) {
if (this.privateKey == null) {
throw new error.SDKError('Private key not found');
}
var config = this.config;
// read encrypted key
var encryptedKeyBytes = _stringToBytes(encryptedKey, config.dataEncoding);
var decryptedKey = this.privateKey.decrypt(encryptedKeyBytes, config.asymmetricCipher, _createOAEPOptions(config.asymmetricCipher, oaepHashingAlgorithm));
// read iv
var iv = _stringToBytes(iv, config.dataEncoding);
// create decipher
var cipherTextBytes = _stringToBytes(cipherText, config.dataEncoding);
var decipher = forge.cipher.createDecipher(config.symmetricAlgorithm, decryptedKey);
// decrypt payload
decipher.start({ iv: iv });
decipher.update(forge.util.createBuffer(cipherTextBytes, 'binary'));
decipher.finish();
var decrypted = decipher.output.data;
return decrypted;
}
function _createOAEPOptions(asymmetricCipher, oaepHashingAlgorithm) {
if (asymmetricCipher.includes('OAEP') && oaepHashingAlgorithm != null) {
var mdForOaep = _createMessageDigest(oaepHashingAlgorithm);
return {
md: mdForOaep,
mgf1: {
md: mdForOaep
}
};
}
}
function _generateSecretKey(algorithm, size, digest) {
if (algorithm === 'AES') {
const key = forge.random.getBytesSync(size / 8);
var md = _createMessageDigest(digest);
md.update(key);
return md.digest().getBytes(16);
}
throw new error.SDKError('Unsupported symmetric algorithm [' + symmetricCipher + ']');
}
function _createMessageDigest(digest) {
switch (digest.toUpperCase()) {
case 'SHA-256':
case 'SHA256':
return forge.md.sha256.create();
case 'SHA-512':
case 'SHA512':
return forge.md.sha512.create();
}
}
function _bytesToString(bytes, dataEncoding) {
switch (dataEncoding.toLowerCase()) {
case 'hex':
return forge.util.bytesToHex(bytes);
case 'base64':
default:
return forge.util.encode64(bytes);
}
}
function _stringToBytes(value, dataEncoding) {
switch (dataEncoding.toLowerCase()) {
case 'hex':
return forge.util.hexToBytes(value);
case 'base64':
default:
return forge.util.decode64(value);
}
}
function _readPublicCertificate(publicCertificatePath) {
var certificateContent = fs.readFileSync(publicCertificatePath);
if (!certificateContent || certificateContent.length <= 1) {
throw new error.SDKError('Public certificate content is empty');
}
var publicCertificate = forge.pki.certificateFromPem(certificateContent);
return publicCertificate;
}
function _loadPrivateKey(privateKeyPath) {
var privateKeyContent = fs.readFileSync(privateKeyPath, 'binary');
if (!privateKeyContent || privateKeyContent.length <= 1) {
throw new error.SDKError('Private key content is empty');
}
return forge.pki.privateKeyFromAsn1(forge.asn1.fromDer(privateKeyContent));
}
function _getPrivateKey(p12Path, alias, password) {
var p12Content = fs.readFileSync(p12Path, 'binary');
if (!p12Content || p12Content.length <= 1) {
throw new error.SDKError('p12 keystore content is empty');
}
if (!utils.isSet(alias)) {
throw new error.SDKError('Key alias is not set');
}
if (!utils.isSet(password)) {
throw new error.SDKError('Keystore password is not set');
}
// Get asn1 from DER
var p12Asn1 = forge.asn1.fromDer(p12Content, false);
// Get p12 using the password
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, false, password);
// Get Key from p12
var keyObj = p12.getBags({
friendlyName: alias,
bagType: forge.pki.oids.pkcs8ShroudedKeyBag
}).friendlyName[0];
if (!utils.isSet(keyObj)) {
throw new error.SDKError("No key found for alias [" + alias + "]")
}
return keyObj.key;
}
function getTriggeringEndPath() {
if (this.config == null || this.config.triggeringEndPath == null) {
return [];
}
return this.config.triggeringEndPath;
}
FieldLevelEncryption.prototype.getTriggeringEndPath = getTriggeringEndPath;
FieldLevelEncryption.prototype.encryptPlainText = encryptPlainText;
FieldLevelEncryption.prototype.decryptCipherText = decryptCipherText;
module.exports = FieldLevelEncryption;