Skip to content

Commit

Permalink
Major changes. Separating responsibilities. Orders builder, serializer.
Browse files Browse the repository at this point in the history
  • Loading branch information
Vladislav Hristov authored and Vladislav Hristov committed Jun 11, 2018
1 parent 90f5154 commit ff9a3a1
Show file tree
Hide file tree
Showing 29 changed files with 985 additions and 1,024 deletions.
4 changes: 2 additions & 2 deletions lib/BankLetter.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ const registerHelpers = () => {
handlebars.registerHelper('keyModulusBits', k => k.key.getKeySize());
// return Buffer.byteLength(new BN(k.key.keyPair.e).toBuffer()) * 8;

handlebars.registerHelper('keyExponent', k => k.e());
handlebars.registerHelper('keyExponent', k => k.e('hex'));

handlebars.registerHelper('keyModulus', k => k.n().toUpperCase().match(/.{1,2}/g).join(' '));
handlebars.registerHelper('keyModulus', k => k.n('hex').toUpperCase().match(/.{1,2}/g).join(' '));

handlebars.registerHelper('sha256', (k) => {
const digest = Buffer.from(k.publicDigest(), 'base64').toString('HEX');
Expand Down
238 changes: 62 additions & 176 deletions lib/Client.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,21 @@
'use strict';

const fs = require('fs');
const path = require('path');
// const fs = require('fs');
// const path = require('path');

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');

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 defaultIniTemplatePath = path.join(__dirname, '../templates/ini.hbs');
// const BankLetter = require('./BankLetter');

const OrderSerializer = require('./orders/H004/OrderSerializer');


/* const defaultIniTemplatePath = path.join(__dirname, '../templates/ini.hbs');
const utils = {
mapObject: (o = {}, predicate = v => v) => Object.entries(o).reduce((r, [key, value]) => { r[key] = value; return r; }, o),
exponent: {
// str = 65537 => AQAB
toBase64(str) {
Expand All @@ -37,67 +26,28 @@ const utils = {
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;
/* constructor({
url,
hostId,
userId,
partnerId,
keyManager = new FsKeyManager({ path: './keys.ebics', passphrase: 'node-ebics' }),
}) {
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');
this.keyManager = keyManager;
} */

return Buffer.from(encrypted).toString('base64');
}

decrypt(data) {
data = (new Buffer(data, 'base64')).toString(); // eslint-disable-line no-buffer-constructor

const decipher = crypto.createDecipher(this.encryptAlgorithm, this.passphrase);
const decrypted = decipher.update(data, 'hex', 'utf8') + decipher.final('utf8');

return decrypted;
constructor({ url }) {
this.url = url;
}

static setup(passphrase, url, hostId, userId, partnerId, keysize = 2048) {
const client = new Client(null, passphrase, url, hostId, userId, partnerId);

Object.keys({ A006: '', X002: '', E002: '' }).forEach((key) => {
client.keys[key] = new Key(new NodeRSA({ b: keysize }));
});
// for (const key in Object.keys({ A006: '', X002: '', E002: '' }))
// client.keys[key] = new Key(new NodeRSA({ b: keysize }));


return client;
}
/*
saveIniLetter(bankName, target, template) {
const letter = new BankLetter({
Expand All @@ -112,137 +62,73 @@ module.exports = class Client {
throw error;
}
}
*/

saveKeys(target) {
const data = {};

Object.keys(this.keys).forEach((key) => {
data[key] = this.encrypt(this.keys[key].toPem());
});
// for (const key in this.keys)
// data[key] = this.encrypt(this.keys[key].toPem());

try {
fs.writeFileSync(target, JSON.stringify(data));
} catch (error) {
throw error;
}
}

extractKeys() {
const keys = {};
const jsonData = JSON.parse(this.keysContent);

Object.keys(jsonData).forEach((key) => {
keys[key] = new Key(this.decrypt(jsonData[key]));
});
// for (const key in jsonData)
// keys[key] = new Key(this.decrypt(jsonData[key]));
async initialization(order) {
const res = await this.ebicsRequest(OrderSerializer.serialize(order));
const xml = res.orderData();


return keys;
return {
orderData: xml,
orderId: res.orderId(),
returnCode: res.returnCode(),
reportText: res.reportText(),
bankKeys: res.bankKeys(),
};
}

async download(order) {
const res = await this.ebicsRequest(order.toXML());

const ttt = res.toXML(); // TODO: keep this for debugging purposes
const res = await this.ebicsRequest(OrderSerializer.serialize(order));

order.transactionId = res.transactionId();

if (res.isSegmented() && res.isLastSegment()) {
const receipt = await this.ebicsRequest(order.toReceiptXML());
if (res.isSegmented() && res.isLastSegment())
await this.ebicsRequest(OrderSerializer.serialize(order));

const receiptXML = order.toReceiptXML(); // TODO: keep this for debugging purposes
const rX = receipt.toXML(); // TODO: keep this for debugging purposes
}

return res.orderData();
// return res.orderData();
return {
orderData: res.orderData(),
orderId: res.orderId(),
returnCode: res.returnCode(),
reportText: res.reportText(),
};
}

async upload(order) {
let res = await this.ebicsRequest(order.toXML());
order.transactionId = res.transactionId();
// const orderId = res.orderId();
let res = await this.ebicsRequest(OrderSerializer.serialize(order));
const transactionId = res.transactionId();
const orderId = res.orderId();

order.transactionId = transactionId;

res = await this.ebicsRequest(order.toTransferXML());
res = await this.ebicsRequest(OrderSerializer.serialize(order));

return res.transactionId();
return [transactionId, orderId];
}

async downloadAndUnzip(order) { // eslint-disable-line

}

ebicsRequest(order) {
ebicsRequest(serializedOrder) {
const { keys } = serializedOrder;

return new Promise((resolve, reject) => {
const s = XMLSign.go(keys, serializedOrder.toXML());
$request.post({
url: this.url,
body: XMLSign.go(this, order),
body: s, // XMLSign.go(this, serializedOrder),
headers: { 'content-type': 'text/xml;charset=UTF-8' },
}, (err, res, data) => (err ? reject(err) : resolve(ParseResponse.go(this, data))));
}, (err, res, data) => (err ? reject(err) : resolve(ParseResponse.go(keys, 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));
}
request(order) {
if (order.type.toLowerCase() === 'ini') return this.initialization(order);
if (order.type.toLowerCase() === 'payment') return this.upload(order);
if (order.type.toLowerCase() === 'status') return this.download(order);

C52(from, to) {
return this.downloadAndUnzip(new EBICSC52(this, from, to));
throw Error('Invalid order type');
}
};
76 changes: 0 additions & 76 deletions lib/Key.js

This file was deleted.

0 comments on commit ff9a3a1

Please sign in to comment.