mirror of
https://github.com/node-ebics/node-ebics-client.git
synced 2024-11-21 21:52:07 +00:00
various modular fixes
This commit is contained in:
parent
f207254238
commit
8492d94054
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"extends": "airbnb",
|
"extends": "airbnb-base",
|
||||||
"env": {
|
"env": {
|
||||||
"node": true
|
"node": true
|
||||||
},
|
},
|
||||||
|
3
index.js
3
index.js
@ -1,4 +1,5 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const Client = require('./lib/Client');
|
const Client = require('./lib/Client');
|
||||||
module.exports = Client;
|
|
||||||
|
module.exports = Client;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
202
lib/Client.js
202
lib/Client.js
@ -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));
|
||||||
|
85
lib/Key.js
85
lib/Key.js
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
93
lib/MGF1.js
93
lib/MGF1.js
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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 : '';
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -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}`,
|
||||||
};
|
};
|
||||||
|
@ -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:
|
||||||
|
@ -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();
|
||||||
|
@ -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',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
@ -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() {
|
||||||
|
@ -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])]);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
@ -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',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
@ -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';
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
@ -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',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
69
package.json
69
package.json
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user