node-ebics-client/lib/Client.js
2018-05-31 12:08:07 +03:00

235 lines
5.8 KiB
JavaScript

'use strict';
const fs = require("fs");
const crypto = require("crypto");
const $request = require("request");
const BN = require('bn.js');
const xpath = require("xpath");
const NodeRSA = require("node-rsa");
const DOMParser = require("xmldom").DOMParser;
const Key = require('./Key');
const XMLSign = require('./middleware/XMLSign');
const ParseResponse = require('./middleware/ParseResponse');
const BankLetter = require('./BankLetter');
const EBICSINI = require('./orders/INI');
const EBICSHIA = require('./orders/HIA');
const EBICSHPB = require('./orders/HPB');
const EBICSHKD = require('./orders/HKD');
const EBICSHAA = require('./orders/HAA');
const EBICSHAC = require('./orders/HAC');
const EBICSHTD = require('./orders/HTD');
const EBICSC52 = require('./orders/C52');
const utils = {
exponent: {
// str = 65537 => AQAB
toBase64(str) {
return new BN(str).toBuffer().toString('base64');
},
// str = AQAB => 65537
fromBase64(str) {
return new BN(Buffer.from(str, 'base64'), 2).toNumber();
}
}
}
module.exports = class Client {
constructor(keysContent, passphrase, url, hostId, userId, partnerId) {
this.keysContent = keysContent;
this.passphrase = passphrase;
this.url = url;
this.hostId = hostId;
this.userId = userId;
this.partnerId = partnerId;
this.encryptAlgorithm = 'aes-256-cbc';
this.keys = keysContent ? this.extractKeys() : {};
};
a() {
return this.keys["A006"];
};
e() {
return this.keys["E002"];
};
x() {
return this.keys["X002"];
}
bankX() {
return this.keys[`${this.hostId}.X002`];
}
bankE() {
return this.keys[`${this.hostId}.E002`];
}
encrypt(data) {
const cipher = crypto.createCipher(this.encryptAlgorithm, this.passphrase);
const encrypted = cipher.update(data, 'utf8', 'hex') + cipher.final('hex');
return Buffer.from(encrypted).toString('base64');
};
decrypt(data) {
data = (new Buffer(data, 'base64')).toString();
const decipher = crypto.createDecipher(this.encryptAlgorithm, this.passphrase);
const decrypted = decipher.update(data, 'hex', 'utf8') + decipher.final('utf8');
return decrypted;
};
static setup(passphrase, url, hostId, userId, partnerId, keysize = 2048) {
const client = new Client(null, passphrase, url, hostId, userId, partnerId);
for (let key in {A006: '', X002: '', E002: ''}) {
client.keys[key] = new Key(new NodeRSA({ b: keysize }));
}
return client;
};
saveIniLetter(bankName, path) {
const letter = new BankLetter(this, bankName);
try {
fs.writeFileSync(path, letter.generate());
} catch (error) {
throw error;
}
};
saveKeys(path) {
const data = {};
for (let key in this.keys) {
data[key] = this.encrypt(this.keys[key].toPem());
};
try {
fs.writeFileSync(path, JSON.stringify(data));
} catch(error) {
throw error;
}
};
extractKeys() {
const keys = {};
const jsonData = JSON.parse(this.keysContent);
for (let key in jsonData) {
keys[key] = new Key(this.decrypt(jsonData[key]));
}
return keys;
}
async download(order) {
const res = await this.ebicsRequest(order.toXML());
const ttt = res.toXML(); // keep this for debugging purposes
order.transactionId = res.transactionId();
if (res.isSegmented() && res.isLastSegment()) {
const receipt = await this.ebicsRequest(order.toReceiptXML());
const receiptXML = order.toReceiptXML(); // keep this for debugging purposes
const rX = receipt.toXML(); // keep this for debugging purposes
}
return res.orderData();
};
async upload(order) {
let res = await this.ebicsRequest(order.toXML());
order.transactionId = res.transactionId();
const orderId = res.orderId();
res = await this.ebicsRequest(order.toTransferXML());
return res.transactionId();
}
async downloadAndUnzip(order) {
}
ebicsRequest(order) {
return new Promise((resolve, reject) => {
$request.post({
url : this.url,
body : XMLSign.go(this, order),
headers: { 'content-type': 'text/xml;charset=UTF-8' }
}, (err, res, data) => {
return err ? reject(err): resolve(ParseResponse.go(this, data));
});
});
};
async INI() {
return this.ebicsRequest((new EBICSINI(this)).toXML());
};
async HIA() {
return this.ebicsRequest((new EBICSHIA(this)).toXML());
};
async HPB() {
const data = await this.download(new EBICSHPB(this));
const doc = new DOMParser().parseFromString(data, 'text/xml');
const sel = xpath.useNamespaces({'xmlns': "urn:org:ebics:H004"});
const keyNodes = sel("//xmlns:PubKeyValue", doc);
// console.log(keyNodes);
function xmlLastChild (node) {
let y = node.lastChild;
while (y.nodeType != 1) y = y.previousSibling;
return y;
};
for (let i = 0; i < keyNodes.length; i++) {
const type = xmlLastChild(keyNodes[i].parentNode).textContent;
const modulus = xpath.select("//*[local-name(.)='Modulus']", keyNodes[i])[0].textContent;
const exponent = xpath.select("//*[local-name(.)='Exponent']", keyNodes[i])[0].textContent;
const mod = new BN(Buffer.from(modulus, 'base64'), 2).toBuffer();
const exp = utils.exponent.fromBase64(exponent);
const bank = new NodeRSA();
bank.importKey({ n: mod, e: exp }, 'components-public');
this.keys[`${this.hostId}.${type}`] = new Key(bank);
}
return [this.bankX(), this.bankE()];
};
HKD() {
return this.download(new EBICSHKD(this));
};
HAA() {
return this.download(new EBICSHAA(this));
};
HTD() {
return this.download(new EBICSHTD(this));
};
HAC(from = null, to = null) {
return this.download(new EBICSHAC(this, from, to));
};
C52(from, to) {
return this.downloadAndUnzip(new EBICSC52(this, from, to));
}
};