various modular fixes

This commit is contained in:
AngelPashov 2018-06-01 16:16:43 +03:00
parent f207254238
commit 8492d94054
23 changed files with 605 additions and 612 deletions

View File

@ -1,5 +1,5 @@
{ {
"extends": "airbnb", "extends": "airbnb-base",
"env": { "env": {
"node": true "node": true
}, },

View File

@ -1,4 +1,5 @@
'use strict'; 'use strict';
const Client = require('./lib/Client'); const Client = require('./lib/Client');
module.exports = Client; module.exports = Client;

View File

@ -1,66 +1,55 @@
'use strict'; 'use strict';
const fs = require('fs'); const moment = require('moment');
const moment = require('moment');
const handlebars = require('handlebars'); const handlebars = require('handlebars');
const BN = require("bn.js"); const BN = require('bn.js');
const registerHelpers = () => {
handlebars.registerHelper('today', () => moment().format('DD.MM.YYYY'));
handlebars.registerHelper('now', () => moment().format('HH:mm:ss'));
handlebars.registerHelper('keyExponentBits', k => Buffer.byteLength(new BN(k.key.keyPair.e).toBuffer()) * 8);
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('keyModulus', k => k.n().toUpperCase().match(/.{1,2}/g).join(' '));
handlebars.registerHelper('sha256', (k) => {
const digest = Buffer.from(k.publicDigest(), 'base64').toString('HEX');
return digest.toUpperCase().match(/.{1,2}/g).join(' ');
});
};
module.exports = class BankLetter { module.exports = class BankLetter {
constructor(client, bankName) { constructor({
this.client = client; client,
this.bankName = bankName; bankName,
this.pathToTemplate = './app/ebics/ini.hbs'; template,
}; }) {
this.client = client;
this.bankName = bankName;
this.template = template;
}
_registerHelpers() {
handlebars.registerHelper("today", () => {
return moment().format('DD.MM.YYYY');
});
handlebars.registerHelper("now", () => {
return moment().format('HH:mm:ss');
});
handlebars.registerHelper("keyExponentBits", (k) => {
return Buffer.byteLength(new BN(k.key.keyPair.e).toBuffer()) * 8;
});
handlebars.registerHelper("keyModulusBits", (k) => {
return k.key.getKeySize();
// return Buffer.byteLength(new BN(k.key.keyPair.e).toBuffer()) * 8;
});
handlebars.registerHelper("keyExponent", (k) => {
return k.e();
});
handlebars.registerHelper("keyModulus", (k) => {
return k.n().toUpperCase().match(/.{1,2}/g).join(' ');
});
handlebars.registerHelper("sha256", (k) => {
const digest = Buffer.from(k.publicDigest(), 'base64').toString('HEX');
return digest.toUpperCase().match(/.{1,2}/g).join(' ');
});
};
generate() { generate() {
this._registerHelpers(); registerHelpers();
const str = fs.readFileSync(this.pathToTemplate).toString(); const templ = handlebars.compile(this.template);
const templ = handlebars.compile(str);
const data = { const data = {
bankName : this.bankName, bankName: this.bankName,
userId : this.client.userId, userId: this.client.userId,
partnerId: this.client.partnerId, partnerId: this.client.partnerId,
A006 : this.client.a(), A006: this.client.a(),
X002 : this.client.x(), X002: this.client.x(),
E002 : this.client.e(), E002: this.client.e(),
}; };
return templ(data); return templ(data);
} }
} };

View File

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

View File

@ -2,78 +2,75 @@
const crypto = require('crypto'); const crypto = require('crypto');
const BN = require('bn.js'); const BN = require('bn.js');
const NodeRSA = require("node-rsa"); const NodeRSA = require('node-rsa');
const MGF1 = require('./MGF1'); const mgf1 = require('./MGF1');
module.exports = class Key { module.exports = class Key {
constructor(encodedKey, passphrase = null) { constructor(encodedKey/* , passphrase = null */) {
if (encodedKey instanceof NodeRSA) { this.key = (encodedKey instanceof NodeRSA) ? encodedKey : new NodeRSA(encodedKey);
this.key = encodedKey
} else {
this.key = new NodeRSA(encodedKey);
}
};
publicDigest() {
const str = [this.e().replace(/^(0+)/g, ''), this.n().replace(/^(0+)/g, '')].map((x) => x.toLowerCase()).join(' ');
return crypto.createHash('sha256').update(str).digest('base64').trim();
};
publicEncrypt(buf) {
return crypto.publicEncrypt({ 'key': this.toPem(), padding: crypto.constants.RSA_PKCS1_PADDING }, buf);
} }
n() { publicDigest() {
return this.key.exportKey("components-public").n.toString("hex", 1); const str = [this.e().replace(/^(0+)/g, ''), this.n().replace(/^(0+)/g, '')].map(x => x.toLowerCase()).join(' ');
};
return crypto.createHash('sha256').update(str).digest('base64').trim();
}
publicEncrypt(buf) {
return crypto.publicEncrypt({
key: this.toPem(),
padding: crypto.constants.RSA_PKCS1_PADDING,
}, buf);
}
n() {
return this.key.exportKey('components-public').n.toString('hex', 1);
}
e() { e() {
return new BN(this.key.exportKey("components-public").e).toBuffer().toString('hex'); return new BN(this.key.exportKey('components-public').e).toBuffer().toString('hex');
}; }
toPem() { toPem() {
return this.key.isPrivate() ? this.key.exportKey("pkcs1-private-pem") : this.key.exportKey("pkcs8-public-pem"); return this.key.isPrivate() ? this.key.exportKey('pkcs1-private-pem') : this.key.exportKey('pkcs8-public-pem');
} }
sign(msg, salt = crypto.randomBytes(32)) { sign(msg, salt = crypto.randomBytes(32)) {
const base = new BN(this._emsaPSS(msg, salt)); const base = new BN(Key._emsaPSS(msg, salt));
const power = new BN(this.key.keyPair.d.toBuffer()); const power = new BN(this.key.keyPair.d.toBuffer());
const mod = new BN(this.key.keyPair.n.toBuffer()); const mod = new BN(this.key.keyPair.n.toBuffer());
return (this._modPow(base, power, mod)).toBuffer().toString('base64'); return (Key._modPow(base, power, mod)).toBuffer().toString('base64');
} }
_emsaPSS(msg, salt) { static _emsaPSS(msg, salt) {
const eightNullBytes = Buffer.from("\x00".repeat(8)); const eightNullBytes = Buffer.from('\x00'.repeat(8));
const digestedMsg = crypto.createHash('sha256').update(msg).digest(); const digestedMsg = crypto.createHash('sha256').update(msg).digest();
const mTickHash = crypto.createHash('sha256').update(Buffer.concat([eightNullBytes, digestedMsg, salt]), 'binary').digest(); const mTickHash = crypto.createHash('sha256').update(Buffer.concat([eightNullBytes, digestedMsg, salt]), 'binary').digest();
const ps = Buffer.from("\x00".repeat(190)); const ps = Buffer.from('\x00'.repeat(190));
const db = Buffer.concat([ps, Buffer.from("\x01"), salt]); const db = Buffer.concat([ps, Buffer.from('\x01'), salt]);
const dbMask = MGF1.generate(mTickHash, db.length); const dbMask = mgf1.generate(mTickHash, db.length);
const maskedDb = MGF1.xor(db, dbMask); // so far so good const maskedDb = mgf1.xor(db, dbMask); // so far so good
let maskedDbMsb = (new MGF1)._rjust(new BN(maskedDb.slice(0, 1), 2).toString(2), 8, "0"); let maskedDbMsb = mgf1.rjust(new BN(maskedDb.slice(0, 1), 2).toString(2), 8, '0');
maskedDbMsb = "0" + maskedDbMsb.substr(1);
maskedDb[0] = (new BN(maskedDbMsb, 2).toBuffer())[0]; maskedDbMsb = `0${maskedDbMsb.substr(1)}`;
maskedDb[0] = (new BN(maskedDbMsb, 2).toBuffer())[0]; // eslint-disable-line
return Buffer.concat([maskedDb, mTickHash, Buffer.from('BC', 'hex')]); return Buffer.concat([maskedDb, mTickHash, Buffer.from('BC', 'hex')]);
} }
_modPow(base, power, mod) { static _modPow(base, power, mod) {
let result = new BN(1); let result = new BN(1);
while (power > 0) {
while( power > 0 ) { result = power.and(new BN(1)) === 1 ? (result.mul(base)).mod(mod) : result;
result = power.and(new BN(1)) == 1 ? (result.mul(base)).mod(mod) : result;
base = (base.mul(base)).mod(mod); base = (base.mul(base)).mod(mod);
power = power.shrn(1); power = power.shrn(1);
} }
return result; return result;
} }
}; };

View File

@ -1,67 +1,48 @@
'use strict' 'use strict';
const crypto = require("crypto"); const crypto = require('crypto');
const BN = require('bn.js'); const BN = require('bn.js');
module.exports = class MGF1 { const MFG_LEN = 32;
constructor() { const divceil = (a, b) => ~~(((a + b) - 1) / b); // eslint-disable-line no-bitwise
this._len = 32; const rjust = (string, width, padding) => {
} padding = padding || ' ';
padding = padding.substr(0, 1);
if (string.length < width)
return padding.repeat(width - string.length) + string;
return string;
};
const xor = (a, b) => {
if (a.length !== b.length)
throw new Error('Different length for a and b');
/** for (let i = 0; i < a.length; i++)
* a[i] ^= b[i]; // eslint-disable-line no-bitwise
* @param {Buffer} seed
* @param {Number} masklen
*
* @returns Buffer
*/
static generate(seed, masklen) {
const mgf1 = new MGF1();
if ( masklen > 4294967296 * this._len) { return a;
};
const i2osp = (x, len) => {
if (x >= 256 ** len)
throw new Error('Integer too large');
return Buffer.from(rjust((Buffer.from((new BN(x)).toArray('be', 4)).toString()).replace(/\x00/gi, ''), len, '\x00')); // eslint-disable-line no-control-regex
};
module.exports = {
generate(seed, masklen) {
if (masklen > 4294967296 * MFG_LEN)
throw new Error('Mask too long'); throw new Error('Mask too long');
}
const b = []; const b = [];
for (let i = 0; i < mgf1._divceil(masklen, mgf1._len); i++) { for (let i = 0; i < divceil(masklen, MFG_LEN); i++)
b[i] = crypto.createHash('sha256').update(Buffer.concat([seed, mgf1._i2osp(i, 4)])).digest(); b[i] = crypto.createHash('sha256').update(Buffer.concat([seed, i2osp(i, 4)])).digest();
}
return (Buffer.concat(b)).slice(0, masklen); return (Buffer.concat(b)).slice(0, masklen);
} },
xor,
static xor(a, b) { rjust,
if ( a.length != b.length ) { };
throw new Error('Different length for a and b');
}
for ( let i = 0; i < a.length; i++ ) {
a[i] ^= b[i];
}
return a;
}
_divceil(a, b) {
return ~~((a + b - 1) / b);
}
_i2osp(x, len) {
if ( x >= 256 ** len ) {
throw new Error('Integer too large');
}
return Buffer.from(this._rjust( (Buffer.from((new BN(x)).toArray('be', 4)).toString()).replace(/\x00/gi, ''), len, "\x00" ));
}
_rjust( string, width, padding ) {
padding = padding || " ";
padding = padding.substr( 0, 1 );
if ( string.length < width )
return padding.repeat( width - string.length ) + string;
else
return string;
}
}

View File

@ -1,52 +1,52 @@
'use strict'; 'use strict';
const zlib = require('zlib'); const zlib = require('zlib');
const crypto = require("crypto"); const crypto = require('crypto');
const DOMParser = require("xmldom").DOMParser; const { DOMParser, XMLSerializer } = require('xmldom');
const XMLSerializer = require("xmldom").XMLSerializer; const xpath = require('xpath');
const xpath = require("xpath");
const DEFAULT_IV = Buffer.from(Array(16).fill(0, 0, 15));
module.exports = class Response { module.exports = class Response {
constructor(client, data) { constructor(client, data) {
this.client = client; this.client = client;
this.doc = new DOMParser().parseFromString(data, 'text/xml'); this.doc = new DOMParser().parseFromString(data, 'text/xml');
}; }
isSegmented() { isSegmented() {
const select = xpath.useNamespaces({'xmlns': "urn:org:ebics:H004"}); const select = xpath.useNamespaces({ xmlns: 'urn:org:ebics:H004' });
const node = select("//xmlns:header/xmlns:mutable/xmlns:SegmentNumber", this.doc); const node = select('//xmlns:header/xmlns:mutable/xmlns:SegmentNumber', this.doc);
return node.length ? true: false; return !!node.length;
} }
isLastSegment() { isLastSegment() {
const select = xpath.useNamespaces({'xmlns': "urn:org:ebics:H004"}); const select = xpath.useNamespaces({ xmlns: 'urn:org:ebics:H004' });
const node = select("//xmlns:header/xmlns:mutable/*[@lastSegment='true']", this.doc); const node = select("//xmlns:header/xmlns:mutable/*[@lastSegment='true']", this.doc);
return node.length ? true: false; return !!node.length;
} }
orderData() { orderData() {
const orderData = this.doc.getElementsByTagNameNS("urn:org:ebics:H004", "OrderData")[0].textContent; const orderData = this.doc.getElementsByTagNameNS('urn:org:ebics:H004', 'OrderData')[0].textContent;
const decipher = crypto.createDecipheriv('aes-128-cbc', this.transactionKey(), Buffer.from([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,])).setAutoPadding(false); const decipher = crypto.createDecipheriv('aes-128-cbc', this.transactionKey(), DEFAULT_IV).setAutoPadding(false);
const data = Buffer.from(decipher.update(orderData, 'base64', 'binary') + decipher.final('binary'), 'binary'); const data = Buffer.from(decipher.update(orderData, 'base64', 'binary') + decipher.final('binary'), 'binary');
return zlib.inflateSync(data).toString(); return zlib.inflateSync(data).toString();
} }
transactionKey() { transactionKey() {
const keyNodeText = this.doc.getElementsByTagNameNS("urn:org:ebics:H004", "TransactionKey")[0].textContent; const keyNodeText = this.doc.getElementsByTagNameNS('urn:org:ebics:H004', 'TransactionKey')[0].textContent;
const tkEncrypted = Buffer.from(keyNodeText, 'base64'); const tkEncrypted = Buffer.from(keyNodeText, 'base64');
this.client.e().key.setOptions({encryptionScheme: 'pkcs1'}); this.client.e().key.setOptions({ encryptionScheme: 'pkcs1' });
return this.client.e().key.decrypt(tkEncrypted); return this.client.e().key.decrypt(tkEncrypted);
} }
transactionId() { transactionId() {
const select = xpath.useNamespaces({'xmlns': "urn:org:ebics:H004"}); const select = xpath.useNamespaces({ xmlns: 'urn:org:ebics:H004' });
const node = select("//xmlns:header/xmlns:static/xmlns:TransactionID", this.doc); const node = select('//xmlns:header/xmlns:static/xmlns:TransactionID', this.doc);
return node.length ? node[0].textContent : ''; return node.length ? node[0].textContent : '';
} }

View File

@ -1,11 +1,10 @@
'use strict'; 'use strict';
const crypto = require("crypto"); const crypto = require('crypto');
const DOMParser = require("xmldom").DOMParser; const { DOMParser, XMLSerializer } = require('xmldom');
const XMLSerializer = require("xmldom").XMLSerializer; const select = require('xpath.js');
const select = require("xpath.js"); const C14n = require('xml-crypto/lib/c14n-canonicalization').C14nCanonicalization;
const c14n = require('xml-crypto/lib/c14n-canonicalization').C14nCanonicalization;
module.exports = class Signer { module.exports = class Signer {
@ -67,30 +66,29 @@ module.exports = class Signer {
digest() { digest() {
// get the xml node, where the digested value is supposed to be // get the xml node, where the digested value is supposed to be
const nodeDigestValue = this.doc.getElementsByTagName("ds:DigestValue")[0]; const nodeDigestValue = this.doc.getElementsByTagName('ds:DigestValue')[0];
const nodes = select(this.doc, "//*[@authenticate='true']"); // const nodes = select(this.doc, "//*[@authenticate='true']");
// canonicalize the node that has authenticate='true' attribute // canonicalize the node that has authenticate='true' attribute
// const contentToDigest = select(this.doc, '//*[@authenticate="true"]')
const contentToDigest = select(this.doc, "//*[@authenticate='true']") const contentToDigest = select(this.doc, "//*[@authenticate='true']")
.map(x => { .map(x => new C14n().process(x)).join('');
const aaaa = x.toString();
return new c14n().process(x)
}).join("");
console.log('digest', 'contentToDigest', contentToDigest);
// fix the canonicalization // fix the canonicalization
const fixedContent = contentToDigest.replace(/xmlns="urn:org:ebics:H004"/g, 'xmlns="urn:org:ebics:H004" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"'); const fixedContent = contentToDigest.replace(/xmlns="urn:org:ebics:H004"/g, 'xmlns="urn:org:ebics:H004" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"');
if (nodeDigestValue) { if (nodeDigestValue)
nodeDigestValue.textContent = crypto.createHash('sha256').update(fixedContent).digest('base64').trim(); nodeDigestValue.textContent = crypto.createHash('sha256').update(fixedContent).digest('base64').trim();
} }
};
sign() { sign() {
const nodeSignatureValue = this.doc.getElementsByTagName("ds:SignatureValue")[0]; const nodeSignatureValue = this.doc.getElementsByTagName('ds:SignatureValue')[0];
console.log('sign =>');
if (nodeSignatureValue) { if (nodeSignatureValue) {
const contentToSign = (new c14n().process(select(this.doc, "//ds:SignedInfo")[0])).replace('xmlns:ds="http://www.w3.org/2000/09/xmldsig#"', 'xmlns="urn:org:ebics:H004" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"'); const contentToSign = (new C14n().process(select(this.doc, '//ds:SignedInfo')[0])).replace('xmlns:ds="http://www.w3.org/2000/09/xmldsig#"', 'xmlns="urn:org:ebics:H004" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"');
nodeSignatureValue.textContent = this.client.x().key.sign(contentToSign, 'base64'); nodeSignatureValue.textContent = this.client.x().key.sign(contentToSign, 'base64');
} }
@ -99,5 +97,4 @@ module.exports = class Signer {
toXML() { toXML() {
return new XMLSerializer().serializeToString(this.doc); return new XMLSerializer().serializeToString(this.doc);
} }
}; };

View File

@ -3,10 +3,10 @@
const packageJson = require('../package.json'); const packageJson = require('../package.json');
const name = 'eCollect Node Ebics Client'; const name = 'eCollect Node Ebics Client';
const version = packageJson.version; const { version } = packageJson;
module.exports = { module.exports = {
name, name,
version, version,
productString: `${name} ${version}`, productString: `${name} ${version}`,
}; };

View File

@ -6,10 +6,10 @@ module.exports = class ParseResponse {
constructor(client, data) { constructor(client, data) {
this.client = client; this.client = client;
this.data = data; this.data = data;
}; }
static go (client, data) { static go(client, data) {
const parseRensponse = new ParseResponse(client, data); // const parseRensponse = new ParseResponse(client, data);
const response = new Response(client, data); const response = new Response(client, data);
// TODO: // TODO:

View File

@ -6,10 +6,10 @@ module.exports = class XMLSign {
constructor(client, data) { constructor(client, data) {
this.client = client; this.client = client;
this.data = data; this.data = data;
}; }
static go (client, data) { static go(client, data) {
const xmlSigner = new XMLSign(client, data); // const xmlSigner = new XMLSign(client, data);
const signer = new Signer(client, data); const signer = new Signer(client, data);
signer.digest(); signer.digest();

View File

@ -3,48 +3,58 @@
const GenericOrder = require('./GenericOrder'); const GenericOrder = require('./GenericOrder');
module.exports = class C52 extends GenericOrder { module.exports = class C52 extends GenericOrder {
constructor (client, from, to) { constructor(client, from, to) {
super(client); super(client);
this._from = from; this._from = from;
this._to = to; this._to = to;
this._schema.header = { this._schema.header = {
"@" : { authenticate: true }, '@': {
authenticate: true,
},
static: { static: {
HostID : this.hostId, HostID: this.hostId,
Nonce : this.nonce(), Nonce: this.nonce(),
Timestamp: this.timestamp(), Timestamp: this.timestamp(),
PartnerID: this.partnerId, PartnerID: this.partnerId,
UserID : this.userId, UserID: this.userId,
Product : { Product: {
"@": { Language: "de" }, '@': {
"#": this.productString, Language: 'de',
},
'#': this.productString,
}, },
OrderDetails: { OrderDetails: {
OrderType : "C52", OrderType: 'C52',
OrderAttribute : "DZHNN", OrderAttribute: 'DZHNN',
StandardOrderParams: { StandardOrderParams: {
DateRange: { DateRange: {
Start: this._from, Start: this._from,
End : this._to End: this._to,
} },
}, },
}, },
BankPubKeyDigests: { BankPubKeyDigests: {
Authentication: { Authentication: {
"@": { Version: "X002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, '@': {
"#": this.client.bankX().publicDigest() Version: 'X002',
Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256',
},
'#': this.client.bankX().publicDigest(),
}, },
Encryption: { Encryption: {
"@": { Version: "E002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, '@': {
"#": this.client.bankE().publicDigest() Version: 'E002',
} Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256',
},
'#': this.client.bankE().publicDigest(),
},
}, },
SecurityMedium: "0000" SecurityMedium: '0000',
}, },
mutable: { mutable: {
TransactionPhase: "Initialisation" TransactionPhase: 'Initialisation',
} },
}; };
}; }
}; };

View File

@ -1,10 +1,47 @@
'use strict'; 'use strict';
// const randHex = require('../../lib/utils').randHex; // const randHex = require('../../lib/utils').randHex;
const crypto = require("crypto"); const crypto = require('crypto');
const js2xmlparser = require('js2xmlparser'); const js2xmlparser = require('js2xmlparser');
const consts = require('../consts'); const consts = require('../consts');
const authSignature = () => {
return {
'ds:SignedInfo': {
'ds:CanonicalizationMethod': {
'@': {
Algorithm:
'http://www.w3.org/TR/2001/REC-xml-c14n-20010315',
},
},
'ds:SignatureMethod': {
'@': {
Algorithm:
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
},
},
'ds:Reference': {
'@': { URI: "#xpointer(//*[@authenticate='true'])" },
'ds:Transforms': {
'ds:Transform': {
'@': {
Algorithm:
'http://www.w3.org/TR/2001/REC-xml-c14n-20010315',
},
},
},
'ds:DigestMethod': {
'@': {
Algorithm:
'http://www.w3.org/2001/04/xmlenc#sha256',
},
},
'ds:DigestValue': {},
},
},
'ds:SignatureValue': {},
};
};
module.exports = class GenericOrder { module.exports = class GenericOrder {
constructor(client) { constructor(client) {
this.client = client; this.client = client;
@ -18,67 +55,29 @@ module.exports = class GenericOrder {
this.xmlOptions = { this.xmlOptions = {
declaration: { declaration: {
include: true, include: true,
encoding: "utf-8" encoding: 'utf-8',
}, },
format: { format: {
doubleQuotes: true, doubleQuotes: true,
indent: '', indent: '',
newline: '', newline: '',
pretty: true pretty: true,
} },
}; };
this._schema = { this._schema = {
"@": { '@': {
"xmlns:ds": "http://www.w3.org/2000/09/xmldsig#", 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
xmlns: "urn:org:ebics:H004", xmlns: 'urn:org:ebics:H004',
Version: "H004", Version: 'H004',
Revision: "1" Revision: '1',
}, },
header: {}, header: {},
AuthSignature: this.authSignature(), AuthSignature: authSignature(),
body: {} body: {},
};
}
authSignature() {
return {
"ds:SignedInfo": {
"ds:CanonicalizationMethod": {
"@": {
Algorithm:
"http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
}
},
"ds:SignatureMethod": {
"@": {
Algorithm:
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
}
},
"ds:Reference": {
"@": { URI: "#xpointer(//*[@authenticate='true'])" },
"ds:Transforms": {
"ds:Transform": {
"@": {
Algorithm:
"http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
}
}
},
"ds:DigestMethod": {
"@": {
Algorithm:
"http://www.w3.org/2001/04/xmlenc#sha256"
}
},
"ds:DigestValue": {}
}
},
"ds:SignatureValue": {}
}; };
} }
@ -86,92 +85,94 @@ module.exports = class GenericOrder {
return this._schema; return this._schema;
} }
get productString() { static get productString() {
return consts.productString; return consts.productString;
} }
nonce() { static nonce() {
return crypto.randomBytes(16).toString('hex'); return crypto.randomBytes(16).toString('hex');
} }
timestamp() { // TODO: remove eslint-disable-line
timestamp() { // eslint-disable-line
return new Date().toISOString(); return new Date().toISOString();
} }
root() { root() { // eslint-disable-line class-methods-use-this
return "ebicsRequest"; return 'ebicsRequest';
} }
toReceiptXML() { toReceiptXML() {
const xmlObj = { const xmlObj = {
"@": { '@': {
"xmlns:ds": "http://www.w3.org/2000/09/xmldsig#", 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
xmlns: "urn:org:ebics:H004", xmlns: 'urn:org:ebics:H004',
Version: "H004", Version: 'H004',
Revision: "1" Revision: '1',
}, },
header: { header: {
"@": { authenticate: true }, '@': { authenticate: true },
static: { static: {
HostID: this.hostId, HostID: this.hostId,
TransactionID: this.transactionId TransactionID: this.transactionId,
}, },
mutable: { mutable: {
TransactionPhase: 'Receipt', TransactionPhase: 'Receipt',
} },
}, },
AuthSignature: this.authSignature(), AuthSignature: this.authSignature(),
body: { body: {
TransferReceipt: { TransferReceipt: {
"@": { authenticate: true }, '@': { authenticate: true },
ReceiptCode: 0 ReceiptCode: 0,
} },
} },
}; };
return js2xmlparser.parse(this.root(), xmlObj, this.xmlOptions); return js2xmlparser.parse(this.root(), xmlObj, this.xmlOptions);
} }
toTransferXML(){ toTransferXML() {
const xmlObj = { const xmlObj = {
"@": { '@': {
"xmlns:ds": "http://www.w3.org/2000/09/xmldsig#", 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
xmlns: "urn:org:ebics:H004", xmlns: 'urn:org:ebics:H004',
Version: "H004", Version: 'H004',
Revision: "1" Revision: '1',
}, },
header: { header: {
"@": { authenticate: true }, '@': { authenticate: true },
static: { static: {
HostID: this.hostId, HostID: this.hostId,
TransactionID: this.transactionId TransactionID: this.transactionId,
}, },
mutable: { mutable: {
TransactionPhase: 'Transfer', TransactionPhase: 'Transfer',
SegmentNumber: { SegmentNumber: {
"@": { lastSegment: true }, '@': { lastSegment: true },
"#": 1 '#': 1,
} },
} },
}, },
AuthSignature: this.authSignature(), AuthSignature: this.authSignature(),
body: { body: {
DataTransfer: { DataTransfer: {
OrderData: this.encryptedOrderData() OrderData: this.encryptedOrderData(),
} },
} },
}; };
return js2xmlparser.parse(this.root(), xmlObj, this.xmlOptions); return js2xmlparser.parse(this.root(), xmlObj, this.xmlOptions);
} }
encryptedOrderData() { encryptedOrderData() { // eslint-disable-line class-methods-use-this
return null;
} }
toXML() { toXML() {

View File

@ -1,79 +1,79 @@
'use strict'; 'use strict';
const zlib = require('zlib'); const zlib = require('zlib');
const crypto = require("crypto"); const crypto = require('crypto');
const js2xmlparser = require('js2xmlparser'); const js2xmlparser = require('js2xmlparser');
const GenericOrder = require('./GenericOrder'); const GenericOrder = require('./GenericOrder');
const pad = (d) => {
const dLen = d.length;
const len = 16 * (Math.trunc(dLen / 16) + 1);
return Buffer.concat([d, Buffer.from(Buffer.from([0]).toString().repeat(len - dLen - 1)), Buffer.from([len - dLen])]);
};
module.exports = class GenericUploadOrder extends GenericOrder { module.exports = class GenericUploadOrder extends GenericOrder {
constructor(client, document) { constructor(client, document) {
super(client); super(client);
this._document = document; this._document = document;
this._key = crypto.randomBytes(16); this._key = crypto.randomBytes(16);
this._schema.body = { this._schema.body = {
DataTransfer: { DataTransfer: {
DataEncryptionInfo: { DataEncryptionInfo: {
"@": { authenticate: true }, '@': { authenticate: true },
EncryptionPubKeyDigest: { EncryptionPubKeyDigest: {
"@": { Version: "E002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, '@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
"#": this.client.bankE().publicDigest() '#': this.client.bankE().publicDigest(),
}, },
TransactionKey: this.client.bankE().publicEncrypt(this._key).toString('base64'), TransactionKey: this.client.bankE().publicEncrypt(this._key).toString('base64'),
}, },
SignatureData: { SignatureData: {
"@": { authenticate: true }, '@': { authenticate: true },
"#": this.encryptedOrderSignature() '#': this.encryptedOrderSignature(),
} },
} },
}; };
}; }
orderSignature() { orderSignature() {
const xmlObj = { const xmlObj = {
"@": { '@': {
xmlns: "http://www.ebics.org/S001", xmlns: 'http://www.ebics.org/S001',
"xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
"xsi:schemaLocation": "http://www.ebics.org/S001 http://www.ebics.org/S001/ebics_signature.xsd" 'xsi:schemaLocation': 'http://www.ebics.org/S001 http://www.ebics.org/S001/ebics_signature.xsd',
}, },
OrderSignatureData: { OrderSignatureData: {
SignatureVersion: "A006", SignatureVersion: 'A006',
SignatureValue: this.signatureValue(), SignatureValue: this.signatureValue(),
PartnerID: this.partnerId, PartnerID: this.partnerId,
UserID: this.userId UserID: this.userId,
} },
}; };
return js2xmlparser.parse('UserSignatureData', xmlObj, this.xmlOptions); return js2xmlparser.parse('UserSignatureData', xmlObj, this.xmlOptions);
}; }
signatureValue() { signatureValue() {
const digested = crypto.createHash('sha256').update(this._document.replace(/\n|\r/g, "")).digest(); const digested = crypto.createHash('sha256').update(this._document.replace(/\n|\r/g, '')).digest();
return this.client.a().sign(digested); return this.client.a().sign(digested);
}; }
encryptedOrderData() { encryptedOrderData() {
const dst = zlib.deflateSync(this._document.replace(/\r|\n/g, "")); const dst = zlib.deflateSync(this._document.replace(/\r|\n/g, ''));
const cipher = crypto.createCipheriv('aes-128-cbc', this._key, Buffer.from([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,])).setAutoPadding(false); const cipher = crypto.createCipheriv('aes-128-cbc', this._key, Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])).setAutoPadding(false);
return Buffer.concat([cipher.update(this._pad(dst)), cipher.final()]).toString('base64'); return Buffer.concat([cipher.update(pad(dst)), cipher.final()]).toString('base64');
} }
encryptedOrderSignature() { encryptedOrderSignature() {
const dst = zlib.deflateSync(this.orderSignature()); const dst = zlib.deflateSync(this.orderSignature());
const cipher = crypto.createCipheriv('aes-128-cbc', this._key, Buffer.from([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,])).setAutoPadding(false); const cipher = crypto.createCipheriv('aes-128-cbc', this._key, Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])).setAutoPadding(false);
return Buffer.concat([cipher.update(this._pad(dst)), cipher.final()]).toString('base64'); return Buffer.concat([cipher.update(pad(dst)), cipher.final()]).toString('base64');
};
_pad(d) {
const dLen = d.length;
const len = 16 * ( Math.trunc(dLen / 16) + 1 );
return Buffer.concat([d, Buffer.from(Buffer.from([0]).toString().repeat(len - dLen - 1)), Buffer.from([len-dLen])]);
} }
}; };

View File

@ -3,11 +3,11 @@
const GenericOrder = require('./GenericOrder'); const GenericOrder = require('./GenericOrder');
module.exports = class HAA extends GenericOrder { module.exports = class HAA extends GenericOrder {
constructor (client) { constructor(client) {
super(client); super(client);
this._schema.header = { this._schema.header = {
"@": { authenticate: true }, '@': { authenticate: true },
static: { static: {
HostID: this.hostId, HostID: this.hostId,
Nonce: this.nonce(), Nonce: this.nonce(),
@ -15,29 +15,29 @@ module.exports = class HAA extends GenericOrder {
PartnerID: this.partnerId, PartnerID: this.partnerId,
UserID: this.userId, UserID: this.userId,
Product: { Product: {
"@": { Language: "de" }, '@': { Language: 'de' },
"#": this.productString '#': this.productString,
}, },
OrderDetails: { OrderDetails: {
OrderType: "HAA", OrderType: 'HAA',
OrderAttribute: "DZHNN", OrderAttribute: 'DZHNN',
StandardOrderParams: "" StandardOrderParams: '',
}, },
BankPubKeyDigests: { BankPubKeyDigests: {
Authentication: { Authentication: {
"@": { Version: "X002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, '@': { Version: 'X002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
"#": this.client.bankX().publicDigest() '#': this.client.bankX().publicDigest(),
}, },
Encryption: { Encryption: {
"@": { Version: "E002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, '@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
"#": this.client.bankE().publicDigest() '#': this.client.bankE().publicDigest(),
} },
}, },
SecurityMedium: "0000" SecurityMedium: '0000',
}, },
mutable: { mutable: {
TransactionPhase: "Initialisation" TransactionPhase: 'Initialisation',
} },
}; };
}; }
}; };

View File

@ -3,50 +3,50 @@
const GenericOrder = require('./GenericOrder'); const GenericOrder = require('./GenericOrder');
module.exports = class HAC extends GenericOrder { module.exports = class HAC extends GenericOrder {
constructor (client, from = null, to = null) { constructor(client, from = null, to = null) {
super(client); super(client);
this._from = from; this._from = from;
this._to = to; this._to = to;
this._schema.header = { this._schema.header = {
"@" : { authenticate: true }, '@': { authenticate: true },
static: { static: {
HostID : this.hostId, HostID: this.hostId,
Nonce : this.nonce(), Nonce: this.nonce(),
Timestamp: this.timestamp(), Timestamp: this.timestamp(),
PartnerID: this.partnerId, PartnerID: this.partnerId,
UserID : this.userId, UserID: this.userId,
Product : { Product: {
"@": { Language: "de" }, '@': { Language: 'de' },
"#": this.productString, '#': this.productString,
}, },
OrderDetails: { OrderDetails: {
OrderType : "HAC", OrderType: 'HAC',
OrderAttribute : "DZHNN", OrderAttribute: 'DZHNN',
StandardOrderParams: this._hasDateRange() ? { StandardOrderParams: this._hasDateRange() ? {
DateRange: { DateRange: {
Start: this._from, Start: this._from,
End : this._to End: this._to,
} },
} : "" } : '',
}, },
BankPubKeyDigests: { BankPubKeyDigests: {
Authentication: { Authentication: {
"@": { Version: "X002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, '@': { Version: 'X002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
"#": this.client.bankX().publicDigest() '#': this.client.bankX().publicDigest(),
}, },
Encryption: { Encryption: {
"@": { Version: "E002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, '@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
"#": this.client.bankE().publicDigest() '#': this.client.bankE().publicDigest(),
} },
}, },
SecurityMedium: "0000" SecurityMedium: '0000',
}, },
mutable: { mutable: {
TransactionPhase: "Initialisation" TransactionPhase: 'Initialisation',
} },
}; };
}; }
_hasDateRange() { _hasDateRange() {
return this._from && this._to; return this._from && this._to;

View File

@ -10,76 +10,76 @@ module.exports = class HIA extends GenericOrder {
super(client); super(client);
this._schema = { this._schema = {
"@": { '@': {
"xmlns:ds": "http://www.w3.org/2000/09/xmldsig#", 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
xmlns: "urn:org:ebics:H004", xmlns: 'urn:org:ebics:H004',
Version: "H004", Version: 'H004',
Revision: "1" Revision: '1',
}, },
header: { header: {
"@": { authenticate: true }, '@': { authenticate: true },
static: { static: {
HostID: this.hostId, HostID: this.hostId,
PartnerID: this.partnerId, PartnerID: this.partnerId,
UserID: this.userId, UserID: this.userId,
Product: { Product: {
"@": { Language: "de" }, '@': { Language: 'de' },
"#": this.productString, '#': this.productString,
}, },
OrderDetails: { OrderDetails: {
OrderType: "HIA", OrderType: 'HIA',
OrderAttribute: "DZNNN" OrderAttribute: 'DZNNN',
}, },
SecurityMedium: "0000" SecurityMedium: '0000',
}, },
mutable: {} mutable: {},
}, },
body: { body: {
DataTransfer: { DataTransfer: {
OrderData: Buffer.from(zlib.deflateSync(this.orderData())).toString('base64') OrderData: Buffer.from(zlib.deflateSync(this.orderData())).toString('base64'),
} },
} },
}; };
} }
root() { root() { // eslint-disable-line class-methods-use-this
return "ebicsUnsecuredRequest"; return 'ebicsUnsecuredRequest';
}; }
orderData() { orderData() {
const xmlOrderData = { const xmlOrderData = {
"@": { '@': {
"xmlns:ds": "http://www.w3.org/2000/09/xmldsig#", 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
xmlns: "urn:org:ebics:H004" xmlns: 'urn:org:ebics:H004',
}, },
AuthenticationPubKeyInfo: { AuthenticationPubKeyInfo: {
PubKeyValue: { PubKeyValue: {
"ds:RSAKeyValue": { 'ds:RSAKeyValue': {
"ds:Modulus": Buffer.from(this.client.x().n(), 'HEX').toString('base64'), 'ds:Modulus': Buffer.from(this.client.x().n(), 'HEX').toString('base64'),
"ds:Exponent": "AQAB" 'ds:Exponent': 'AQAB',
}, },
}, },
AuthenticationVersion: "X002" AuthenticationVersion: 'X002',
}, },
EncryptionPubKeyInfo: { EncryptionPubKeyInfo: {
PubKeyValue: { PubKeyValue: {
"ds:RSAKeyValue": { 'ds:RSAKeyValue': {
"ds:Modulus": Buffer.from(this.client.e().n(), 'HEX').toString('base64'), 'ds:Modulus': Buffer.from(this.client.e().n(), 'HEX').toString('base64'),
"ds:Exponent": "AQAB" 'ds:Exponent': 'AQAB',
}, },
}, },
EncryptionVersion: "E002" EncryptionVersion: 'E002',
}, },
PartnerID: this.partnerId, PartnerID: this.partnerId,
UserID: this.userId UserID: this.userId,
}; };
return js2xmlparser.parse("HIARequestOrderData", xmlOrderData, this.xmlOptions); return js2xmlparser.parse('HIARequestOrderData', xmlOrderData, this.xmlOptions);
}; }
toXML() { toXML() {
return js2xmlparser.parse(this.root(), this._schema, this.xmlOptions); return js2xmlparser.parse(this.root(), this._schema, this.xmlOptions);
}; }
}; };

View File

@ -3,11 +3,11 @@
const GenericOrder = require('./GenericOrder'); const GenericOrder = require('./GenericOrder');
module.exports = class HKD extends GenericOrder { module.exports = class HKD extends GenericOrder {
constructor (client) { constructor(client) {
super(client); super(client);
this._schema.header = { this._schema.header = {
"@": { authenticate: true }, '@': { authenticate: true },
static: { static: {
HostID: this.hostId, HostID: this.hostId,
Nonce: this.nonce(), Nonce: this.nonce(),
@ -15,29 +15,29 @@ module.exports = class HKD extends GenericOrder {
PartnerID: this.partnerId, PartnerID: this.partnerId,
UserID: this.userId, UserID: this.userId,
Product: { Product: {
"@": { Language: "de" }, '@': { Language: 'de' },
"#": this.productString, '#': this.productString,
}, },
OrderDetails: { OrderDetails: {
OrderType: "HKD", OrderType: 'HKD',
OrderAttribute: "DZHNN", OrderAttribute: 'DZHNN',
StandardOrderParams: "" StandardOrderParams: '',
}, },
BankPubKeyDigests: { BankPubKeyDigests: {
Authentication: { Authentication: {
"@": { Version: "X002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, '@': { Version: 'X002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
"#": this.client.bankX().publicDigest() '#': this.client.bankX().publicDigest(),
}, },
Encryption: { Encryption: {
"@": { Version: "E002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, '@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
"#": this.client.bankE().publicDigest() '#': this.client.bankE().publicDigest(),
} },
}, },
SecurityMedium: "0000" SecurityMedium: '0000',
}, },
mutable: { mutable: {
TransactionPhase: "Initialisation" TransactionPhase: 'Initialisation',
} },
}; };
}; }
}; };

View File

@ -3,11 +3,11 @@
const GenericOrder = require('./GenericOrder'); const GenericOrder = require('./GenericOrder');
module.exports = class HPB extends GenericOrder { module.exports = class HPB extends GenericOrder {
constructor (client) { constructor(client) {
super(client); super(client);
this._schema.header = { this._schema.header = {
"@": { authenticate: true }, '@': { authenticate: true },
static: { static: {
HostID: this.hostId, HostID: this.hostId,
Nonce: this.nonce(), Nonce: this.nonce(),
@ -15,20 +15,20 @@ module.exports = class HPB extends GenericOrder {
PartnerID: this.partnerId, PartnerID: this.partnerId,
UserID: this.userId, UserID: this.userId,
Product: { Product: {
"@": { Language: "de" }, '@': { Language: 'de' },
"#": this.productString, '#': this.productString,
}, },
OrderDetails: { OrderDetails: {
OrderType: "HPB", OrderType: 'HPB',
OrderAttribute: "DZHNN" OrderAttribute: 'DZHNN',
}, },
SecurityMedium: "0000" SecurityMedium: '0000',
}, },
mutable: {} mutable: {},
}; };
}; }
root() { root() { // eslint-disable-line class-methods-use-this
return "ebicsNoPubKeyDigestsRequest"; return 'ebicsNoPubKeyDigestsRequest';
}; }
}; };

View File

@ -3,11 +3,11 @@
const GenericOrder = require('./GenericOrder'); const GenericOrder = require('./GenericOrder');
module.exports = class HTD extends GenericOrder { module.exports = class HTD extends GenericOrder {
constructor (client) { constructor(client) {
super(client); super(client);
this._schema.header = { this._schema.header = {
"@": { authenticate: true }, '@': { authenticate: true },
static: { static: {
HostID: this.hostId, HostID: this.hostId,
Nonce: this.nonce(), Nonce: this.nonce(),
@ -15,29 +15,29 @@ module.exports = class HTD extends GenericOrder {
PartnerID: this.partnerId, PartnerID: this.partnerId,
UserID: this.userId, UserID: this.userId,
Product: { Product: {
"@": { Language: "de" }, '@': { Language: 'de' },
"#": this.productString, '#': this.productString,
}, },
OrderDetails: { OrderDetails: {
OrderType: "HTD", OrderType: 'HTD',
OrderAttribute: "DZHNN", OrderAttribute: 'DZHNN',
StandardOrderParams: "" StandardOrderParams: '',
}, },
BankPubKeyDigests: { BankPubKeyDigests: {
Authentication: { Authentication: {
"@": { Version: "X002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, '@': { Version: 'X002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
"#": this.client.bankX().publicDigest() '#': this.client.bankX().publicDigest(),
}, },
Encryption: { Encryption: {
"@": { Version: "E002", Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256" }, '@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
"#": this.client.bankE().publicDigest() '#': this.client.bankE().publicDigest(),
} },
}, },
SecurityMedium: "0000" SecurityMedium: '0000',
}, },
mutable: { mutable: {
TransactionPhase: "Initialisation" TransactionPhase: 'Initialisation',
} },
}; };
}; }
}; };

View File

@ -6,70 +6,70 @@ const js2xmlparser = require('js2xmlparser');
const GenericOrder = require('./GenericOrder'); const GenericOrder = require('./GenericOrder');
module.exports = class INI extends GenericOrder { module.exports = class INI extends GenericOrder {
constructor (client) { constructor(client) {
super(client); super(client);
this._schema = { this._schema = {
"@": { '@': {
"xmlns:ds": "http://www.w3.org/2000/09/xmldsig#", 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
xmlns: "urn:org:ebics:H004", xmlns: 'urn:org:ebics:H004',
Version: "H004", Version: 'H004',
Revision: "1" Revision: '1',
}, },
header: { header: {
"@": { authenticate: true }, '@': { authenticate: true },
static: { static: {
HostID: this.hostId, HostID: this.hostId,
PartnerID: this.partnerId, PartnerID: this.partnerId,
UserID: this.userId, UserID: this.userId,
Product: { Product: {
"@": { Language: "de" }, '@': { Language: 'de' },
"#": this.productString, '#': this.productString,
}, },
OrderDetails: { OrderDetails: {
OrderType: "INI", OrderType: 'INI',
OrderAttribute: "DZNNN" OrderAttribute: 'DZNNN',
}, },
SecurityMedium: "0000" SecurityMedium: '0000',
}, },
mutable: {} mutable: {},
}, },
body: { body: {
DataTransfer: { DataTransfer: {
OrderData: Buffer.from(zlib.deflateSync(this.keySignature())).toString('base64') OrderData: Buffer.from(zlib.deflateSync(this.keySignature())).toString('base64'),
} },
} },
}; };
} }
root() { root() { // eslint-disable-line class-methods-use-this
return "ebicsUnsecuredRequest"; return 'ebicsUnsecuredRequest';
}; }
keySignature() { keySignature() {
const xmlOrderData = { const xmlOrderData = {
"@": { '@': {
"xmlns:ds": "http://www.w3.org/2000/09/xmldsig#", 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
xmlns: "http://www.ebics.org/S001" xmlns: 'http://www.ebics.org/S001',
}, },
SignaturePubKeyInfo: { SignaturePubKeyInfo: {
PubKeyValue: { PubKeyValue: {
"ds:RSAKeyValue": { 'ds:RSAKeyValue': {
"ds:Modulus": Buffer.from(this.client.a().n(), 'HEX').toString('base64'), 'ds:Modulus': Buffer.from(this.client.a().n(), 'HEX').toString('base64'),
"ds:Exponent": "AQAB" 'ds:Exponent': 'AQAB',
}, },
TimeStamp: this.timestamp() TimeStamp: this.timestamp(),
}, },
SignatureVersion: "A006" SignatureVersion: 'A006',
}, },
PartnerID: this.partnerId, PartnerID: this.partnerId,
UserID: this.userId UserID: this.userId,
}; };
return js2xmlparser.parse("SignaturePubKeyOrderData", xmlOrderData, this.xmlOptions); return js2xmlparser.parse('SignaturePubKeyOrderData', xmlOrderData, this.xmlOptions);
}; }
toXML() { toXML() {
return js2xmlparser.parse(this.root(), this._schema, this.xmlOptions); return js2xmlparser.parse(this.root(), this._schema, this.xmlOptions);

View File

@ -1,36 +1,39 @@
{ {
"name": "node-ebics-client", "name": "ebics-client",
"version": "0.0.35", "version": "0.0.36",
"description": "Node.js ISO 20022 Compliant EBICS Client", "description": "Node.js ISO 20022 Compliant EBICS Client",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/eCollect/node-ebics-client" "url": "https://github.com/eCollect/node-ebics-client"
}, },
"keywords": [ "keywords": [
"EBICS", "EBICS",
"ISO20022", "ISO20022",
"nodejs", "nodejs",
"api" "api"
], ],
"author": "eCollect Sofia Tech Team", "author": "eCollect Sofia Tech Team",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"bn.js": "^4.11.8", "bn.js": "^4.11.8",
"handlebars": "^4.0.11", "handlebars": "^4.0.11",
"js2xmlparser": "^3.0.0", "js2xmlparser": "^3.0.0",
"moment": "^2.22.1", "moment": "^2.22.1",
"node-rsa": "^0.4.2", "node-rsa": "^0.4.2",
"xml-c14n": "0.0.6", "request": "^2.87.0",
"xmldom": "^0.1.27", "xml-c14n": "0.0.6",
"xpath": "0.0.27" "xml-crypto": "^0.10.1",
}, "xmldom": "^0.1.27",
"devDependencies": { "xpath": "0.0.27",
"eslint": "^4.19.1", "xpath.js": "^1.1.0"
"eslint-config-airbnb-base": "^12.1.0", },
"eslint-plugin-import": "^2.12.0" "devDependencies": {
"eslint": "^4.19.1",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-plugin-import": "^2.12.0"
} }
} }