UNPKG

mastercard-api-core

Version:
215 lines (168 loc) 6.8 kB
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;