mirror of
https://github.com/node-ebics/node-ebics-client.git
synced 2024-11-25 15:42:07 +00:00
Major changes. Separating responsibilities. Orders builder, serializer.
This commit is contained in:
parent
90f51544b7
commit
ff9a3a16b4
@ -14,9 +14,9 @@ const registerHelpers = () => {
|
|||||||
handlebars.registerHelper('keyModulusBits', k => k.key.getKeySize());
|
handlebars.registerHelper('keyModulusBits', k => k.key.getKeySize());
|
||||||
// return Buffer.byteLength(new BN(k.key.keyPair.e).toBuffer()) * 8;
|
// return Buffer.byteLength(new BN(k.key.keyPair.e).toBuffer()) * 8;
|
||||||
|
|
||||||
handlebars.registerHelper('keyExponent', k => k.e());
|
handlebars.registerHelper('keyExponent', k => k.e('hex'));
|
||||||
|
|
||||||
handlebars.registerHelper('keyModulus', k => k.n().toUpperCase().match(/.{1,2}/g).join(' '));
|
handlebars.registerHelper('keyModulus', k => k.n('hex').toUpperCase().match(/.{1,2}/g).join(' '));
|
||||||
|
|
||||||
handlebars.registerHelper('sha256', (k) => {
|
handlebars.registerHelper('sha256', (k) => {
|
||||||
const digest = Buffer.from(k.publicDigest(), 'base64').toString('HEX');
|
const digest = Buffer.from(k.publicDigest(), 'base64').toString('HEX');
|
||||||
|
240
lib/Client.js
240
lib/Client.js
@ -1,32 +1,21 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const fs = require('fs');
|
// const fs = require('fs');
|
||||||
const path = require('path');
|
// const path = require('path');
|
||||||
|
|
||||||
const crypto = require('crypto');
|
|
||||||
const $request = require('request');
|
const $request = require('request');
|
||||||
|
|
||||||
const BN = require('bn.js');
|
|
||||||
const xpath = require('xpath');
|
|
||||||
const NodeRSA = require('node-rsa');
|
|
||||||
const { DOMParser } = require('xmldom');
|
|
||||||
|
|
||||||
const Key = require('./Key');
|
|
||||||
const XMLSign = require('./middleware/XMLSign');
|
const 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 EBICSHIA = require('./orders/HIA');
|
|
||||||
const EBICSHPB = require('./orders/HPB');
|
|
||||||
const EBICSHKD = require('./orders/HKD');
|
|
||||||
const EBICSHAA = require('./orders/HAA');
|
|
||||||
const EBICSHAC = require('./orders/HAC');
|
|
||||||
const EBICSHTD = require('./orders/HTD');
|
|
||||||
const EBICSC52 = require('./orders/C52');
|
|
||||||
|
|
||||||
const defaultIniTemplatePath = path.join(__dirname, '../templates/ini.hbs');
|
const OrderSerializer = require('./orders/H004/OrderSerializer');
|
||||||
|
|
||||||
|
|
||||||
|
/* const defaultIniTemplatePath = path.join(__dirname, '../templates/ini.hbs');
|
||||||
|
|
||||||
const utils = {
|
const utils = {
|
||||||
|
mapObject: (o = {}, predicate = v => v) => Object.entries(o).reduce((r, [key, value]) => { r[key] = value; return r; }, o),
|
||||||
exponent: {
|
exponent: {
|
||||||
// str = 65537 => AQAB
|
// str = 65537 => AQAB
|
||||||
toBase64(str) {
|
toBase64(str) {
|
||||||
@ -37,67 +26,28 @@ const utils = {
|
|||||||
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({
|
||||||
this.keysContent = keysContent;
|
url,
|
||||||
this.passphrase = passphrase;
|
hostId,
|
||||||
|
userId,
|
||||||
|
partnerId,
|
||||||
|
keyManager = new FsKeyManager({ path: './keys.ebics', passphrase: 'node-ebics' }),
|
||||||
|
}) {
|
||||||
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.keyManager = keyManager;
|
||||||
this.keys = keysContent ? this.extractKeys() : {};
|
} */
|
||||||
|
|
||||||
|
constructor({ url }) {
|
||||||
|
this.url = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
a() {
|
/*
|
||||||
return this.keys.A006;
|
|
||||||
}
|
|
||||||
|
|
||||||
e() {
|
|
||||||
return this.keys.E002;
|
|
||||||
}
|
|
||||||
|
|
||||||
x() {
|
|
||||||
return this.keys.X002;
|
|
||||||
}
|
|
||||||
|
|
||||||
bankX() {
|
|
||||||
return this.keys[`${this.hostId}.X002`];
|
|
||||||
}
|
|
||||||
|
|
||||||
bankE() {
|
|
||||||
return this.keys[`${this.hostId}.E002`];
|
|
||||||
}
|
|
||||||
|
|
||||||
encrypt(data) {
|
|
||||||
const cipher = crypto.createCipher(this.encryptAlgorithm, this.passphrase);
|
|
||||||
const encrypted = cipher.update(data, 'utf8', 'hex') + cipher.final('hex');
|
|
||||||
|
|
||||||
return Buffer.from(encrypted).toString('base64');
|
|
||||||
}
|
|
||||||
|
|
||||||
decrypt(data) {
|
|
||||||
data = (new Buffer(data, 'base64')).toString(); // eslint-disable-line no-buffer-constructor
|
|
||||||
|
|
||||||
const decipher = crypto.createDecipher(this.encryptAlgorithm, this.passphrase);
|
|
||||||
const decrypted = decipher.update(data, 'hex', 'utf8') + decipher.final('utf8');
|
|
||||||
|
|
||||||
return decrypted;
|
|
||||||
}
|
|
||||||
|
|
||||||
static setup(passphrase, url, hostId, userId, partnerId, keysize = 2048) {
|
|
||||||
const client = new Client(null, passphrase, url, hostId, userId, partnerId);
|
|
||||||
|
|
||||||
Object.keys({ A006: '', X002: '', E002: '' }).forEach((key) => {
|
|
||||||
client.keys[key] = new Key(new NodeRSA({ b: keysize }));
|
|
||||||
});
|
|
||||||
// for (const key in Object.keys({ A006: '', X002: '', E002: '' }))
|
|
||||||
// client.keys[key] = new Key(new NodeRSA({ b: keysize }));
|
|
||||||
|
|
||||||
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
|
|
||||||
saveIniLetter(bankName, target, template) {
|
saveIniLetter(bankName, target, template) {
|
||||||
const letter = new BankLetter({
|
const letter = new BankLetter({
|
||||||
@ -112,137 +62,73 @@ module.exports = class Client {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
saveKeys(target) {
|
|
||||||
const data = {};
|
|
||||||
|
|
||||||
Object.keys(this.keys).forEach((key) => {
|
|
||||||
data[key] = this.encrypt(this.keys[key].toPem());
|
|
||||||
});
|
|
||||||
// for (const key in this.keys)
|
|
||||||
// data[key] = this.encrypt(this.keys[key].toPem());
|
|
||||||
|
|
||||||
try {
|
|
||||||
fs.writeFileSync(target, JSON.stringify(data));
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extractKeys() {
|
|
||||||
const keys = {};
|
|
||||||
const jsonData = JSON.parse(this.keysContent);
|
|
||||||
|
|
||||||
Object.keys(jsonData).forEach((key) => {
|
|
||||||
keys[key] = new Key(this.decrypt(jsonData[key]));
|
|
||||||
});
|
|
||||||
// for (const key in jsonData)
|
|
||||||
// keys[key] = new Key(this.decrypt(jsonData[key]));
|
|
||||||
|
|
||||||
|
|
||||||
return keys;
|
async initialization(order) {
|
||||||
|
const res = await this.ebicsRequest(OrderSerializer.serialize(order));
|
||||||
|
const xml = res.orderData();
|
||||||
|
|
||||||
|
return {
|
||||||
|
orderData: xml,
|
||||||
|
orderId: res.orderId(),
|
||||||
|
returnCode: res.returnCode(),
|
||||||
|
reportText: res.reportText(),
|
||||||
|
bankKeys: res.bankKeys(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async download(order) {
|
async download(order) {
|
||||||
const res = await this.ebicsRequest(order.toXML());
|
const res = await this.ebicsRequest(OrderSerializer.serialize(order));
|
||||||
|
|
||||||
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());
|
await this.ebicsRequest(OrderSerializer.serialize(order));
|
||||||
|
|
||||||
const receiptXML = order.toReceiptXML(); // TODO: keep this for debugging purposes
|
// return res.orderData();
|
||||||
const rX = receipt.toXML(); // TODO: keep this for debugging purposes
|
return {
|
||||||
}
|
orderData: res.orderData(),
|
||||||
|
orderId: res.orderId(),
|
||||||
return res.orderData();
|
returnCode: res.returnCode(),
|
||||||
|
reportText: res.reportText(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async upload(order) {
|
async upload(order) {
|
||||||
let res = await this.ebicsRequest(order.toXML());
|
let res = await this.ebicsRequest(OrderSerializer.serialize(order));
|
||||||
order.transactionId = res.transactionId();
|
const transactionId = res.transactionId();
|
||||||
// const orderId = res.orderId();
|
const orderId = res.orderId();
|
||||||
|
|
||||||
res = await this.ebicsRequest(order.toTransferXML());
|
order.transactionId = transactionId;
|
||||||
|
|
||||||
return res.transactionId();
|
res = await this.ebicsRequest(OrderSerializer.serialize(order));
|
||||||
|
|
||||||
|
return [transactionId, orderId];
|
||||||
}
|
}
|
||||||
|
|
||||||
async downloadAndUnzip(order) { // eslint-disable-line
|
async downloadAndUnzip(order) { // eslint-disable-line
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ebicsRequest(order) {
|
ebicsRequest(serializedOrder) {
|
||||||
|
const { keys } = serializedOrder;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
const s = XMLSign.go(keys, serializedOrder.toXML());
|
||||||
$request.post({
|
$request.post({
|
||||||
url: this.url,
|
url: this.url,
|
||||||
body: XMLSign.go(this, order),
|
body: s, // XMLSign.go(this, serializedOrder),
|
||||||
headers: { 'content-type': 'text/xml;charset=UTF-8' },
|
headers: { 'content-type': 'text/xml;charset=UTF-8' },
|
||||||
}, (err, res, data) => (err ? reject(err) : resolve(ParseResponse.go(this, data))));
|
}, (err, res, data) => (err ? reject(err) : resolve(ParseResponse.go(keys, data))));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async INI() {
|
request(order) {
|
||||||
return this.ebicsRequest((new EBICSINI(this)).toXML());
|
if (order.type.toLowerCase() === 'ini') return this.initialization(order);
|
||||||
}
|
if (order.type.toLowerCase() === 'payment') return this.upload(order);
|
||||||
|
if (order.type.toLowerCase() === 'status') return this.download(order);
|
||||||
|
|
||||||
async HIA() {
|
throw Error('Invalid order type');
|
||||||
return this.ebicsRequest((new EBICSHIA(this)).toXML());
|
|
||||||
}
|
|
||||||
|
|
||||||
async HPB() {
|
|
||||||
const data = await this.download(new EBICSHPB(this));
|
|
||||||
|
|
||||||
const doc = new DOMParser().parseFromString(data, 'text/xml');
|
|
||||||
const sel = xpath.useNamespaces({ xmlns: 'urn:org:ebics:H004' });
|
|
||||||
const keyNodes = sel('//xmlns:PubKeyValue', doc);
|
|
||||||
// console.log(keyNodes);
|
|
||||||
|
|
||||||
function xmlLastChild(node) {
|
|
||||||
let y = node.lastChild;
|
|
||||||
|
|
||||||
while (y.nodeType !== 1) y = y.previousSibling;
|
|
||||||
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < keyNodes.length; i++) {
|
|
||||||
const type = xmlLastChild(keyNodes[i].parentNode).textContent;
|
|
||||||
const modulus = xpath.select("//*[local-name(.)='Modulus']", keyNodes[i])[0].textContent;
|
|
||||||
const exponent = xpath.select("//*[local-name(.)='Exponent']", keyNodes[i])[0].textContent;
|
|
||||||
|
|
||||||
const mod = new BN(Buffer.from(modulus, 'base64'), 2).toBuffer();
|
|
||||||
const exp = utils.exponent.fromBase64(exponent);
|
|
||||||
|
|
||||||
const bank = new NodeRSA();
|
|
||||||
|
|
||||||
bank.importKey({ n: mod, e: exp }, 'components-public');
|
|
||||||
|
|
||||||
this.keys[`${this.hostId}.${type}`] = new Key(bank);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [this.bankX(), this.bankE()];
|
|
||||||
}
|
|
||||||
|
|
||||||
HKD() {
|
|
||||||
return this.download(new EBICSHKD(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
HAA() {
|
|
||||||
return this.download(new EBICSHAA(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
HTD() {
|
|
||||||
return this.download(new EBICSHTD(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
HAC(from = null, to = null) {
|
|
||||||
return this.download(new EBICSHAC(this, from, to));
|
|
||||||
}
|
|
||||||
|
|
||||||
C52(from, to) {
|
|
||||||
return this.downloadAndUnzip(new EBICSC52(this, from, to));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
76
lib/Key.js
76
lib/Key.js
@ -1,76 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const crypto = require('crypto');
|
|
||||||
|
|
||||||
const BN = require('bn.js');
|
|
||||||
const NodeRSA = require('node-rsa');
|
|
||||||
|
|
||||||
const mgf1 = require('./MGF1');
|
|
||||||
|
|
||||||
module.exports = class Key {
|
|
||||||
constructor(encodedKey/* , passphrase = null */) {
|
|
||||||
this.key = (encodedKey instanceof NodeRSA) ? encodedKey : 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() {
|
|
||||||
return this.key.exportKey('components-public').n.toString('hex', 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
e() {
|
|
||||||
return new BN(this.key.exportKey('components-public').e).toBuffer().toString('hex');
|
|
||||||
}
|
|
||||||
|
|
||||||
toPem() {
|
|
||||||
return this.key.isPrivate() ? this.key.exportKey('pkcs1-private-pem') : this.key.exportKey('pkcs8-public-pem');
|
|
||||||
}
|
|
||||||
|
|
||||||
sign(msg, salt = crypto.randomBytes(32)) {
|
|
||||||
const base = new BN(Key._emsaPSS(msg, salt));
|
|
||||||
const power = new BN(this.key.keyPair.d.toBuffer());
|
|
||||||
const mod = new BN(this.key.keyPair.n.toBuffer());
|
|
||||||
|
|
||||||
return (Key._modPow(base, power, mod)).toBuffer().toString('base64');
|
|
||||||
}
|
|
||||||
|
|
||||||
static _emsaPSS(msg, salt) {
|
|
||||||
const eightNullBytes = Buffer.from('\x00'.repeat(8));
|
|
||||||
const digestedMsg = crypto.createHash('sha256').update(msg).digest();
|
|
||||||
const mTickHash = crypto.createHash('sha256').update(Buffer.concat([eightNullBytes, digestedMsg, salt]), 'binary').digest();
|
|
||||||
|
|
||||||
const ps = Buffer.from('\x00'.repeat(190));
|
|
||||||
const db = Buffer.concat([ps, Buffer.from('\x01'), salt]);
|
|
||||||
|
|
||||||
const dbMask = mgf1.generate(mTickHash, db.length);
|
|
||||||
const maskedDb = mgf1.xor(db, dbMask); // so far so good
|
|
||||||
|
|
||||||
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]; // eslint-disable-line
|
|
||||||
|
|
||||||
return Buffer.concat([maskedDb, mTickHash, Buffer.from('BC', 'hex')]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static _modPow(base, power, mod) {
|
|
||||||
let result = new BN(1);
|
|
||||||
while (power > 0) {
|
|
||||||
result = power.and(new BN(1)) === 1 ? (result.mul(base)).mod(mod) : result;
|
|
||||||
base = (base.mul(base)).mod(mod);
|
|
||||||
power = power.shrn(1);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
@ -2,14 +2,26 @@
|
|||||||
|
|
||||||
const zlib = require('zlib');
|
const zlib = require('zlib');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
|
const BN = require('bn.js');
|
||||||
|
|
||||||
|
const Crypto = require('./crypto/Crypto');
|
||||||
|
|
||||||
const { DOMParser, XMLSerializer } = require('xmldom');
|
const { DOMParser, XMLSerializer } = require('xmldom');
|
||||||
const xpath = require('xpath');
|
const xpath = require('xpath');
|
||||||
|
|
||||||
const DEFAULT_IV = Buffer.from(Array(16).fill(0, 0, 15));
|
const DEFAULT_IV = Buffer.from(Array(16).fill(0, 0, 15));
|
||||||
|
|
||||||
|
const lastChild = (node) => {
|
||||||
|
let y = node.lastChild;
|
||||||
|
|
||||||
|
while (y.nodeType !== 1) y = y.previousSibling;
|
||||||
|
|
||||||
|
return y;
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = class Response {
|
module.exports = class Response {
|
||||||
constructor(client, data) {
|
constructor(keys, data) {
|
||||||
this.client = client;
|
this.keys = keys;
|
||||||
this.doc = new DOMParser().parseFromString(data, 'text/xml');
|
this.doc = new DOMParser().parseFromString(data, 'text/xml');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +40,11 @@ module.exports = class Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
orderData() {
|
orderData() {
|
||||||
const orderData = this.doc.getElementsByTagNameNS('urn:org:ebics:H004', 'OrderData')[0].textContent;
|
const orderDataNode = this.doc.getElementsByTagNameNS('urn:org:ebics:H004', 'OrderData');
|
||||||
|
|
||||||
|
if (!orderDataNode.length) return {};
|
||||||
|
|
||||||
|
const orderData = orderDataNode[0].textContent;
|
||||||
const decipher = crypto.createDecipheriv('aes-128-cbc', this.transactionKey(), DEFAULT_IV).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');
|
||||||
|
|
||||||
@ -37,11 +53,8 @@ module.exports = class Response {
|
|||||||
|
|
||||||
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');
|
|
||||||
|
|
||||||
this.client.e().key.setOptions({ encryptionScheme: 'pkcs1' });
|
return Crypto.privateDecrypt(this.keys.e(), Buffer.from(keyNodeText, 'base64'));
|
||||||
|
|
||||||
return this.client.e().key.decrypt(tkEncrypted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionId() {
|
transactionId() {
|
||||||
@ -51,6 +64,58 @@ module.exports = class Response {
|
|||||||
return node.length ? node[0].textContent : '';
|
return node.length ? node[0].textContent : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
orderId() {
|
||||||
|
const select = xpath.useNamespaces({ xmlns: 'urn:org:ebics:H004' });
|
||||||
|
const node = select('//xmlns:header/xmlns:mutable/xmlns:OrderID', this.doc);
|
||||||
|
|
||||||
|
return node.length ? node[0].textContent : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
returnCode() {
|
||||||
|
const select = xpath.useNamespaces({ xmlns: 'urn:org:ebics:H004' });
|
||||||
|
const node = select('//xmlns:header/xmlns:mutable/xmlns:ReturnCode', this.doc);
|
||||||
|
|
||||||
|
return node.length ? node[0].textContent : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
reportText() {
|
||||||
|
const select = xpath.useNamespaces({ xmlns: 'urn:org:ebics:H004' });
|
||||||
|
const node = select('//xmlns:header/xmlns:mutable/xmlns:ReportText', this.doc);
|
||||||
|
|
||||||
|
return node.length ? node[0].textContent : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
bankKeys() {
|
||||||
|
const orderData = this.orderData();
|
||||||
|
if (!Object.keys(orderData).length) return {};
|
||||||
|
|
||||||
|
const doc = new DOMParser().parseFromString(orderData, 'text/xml');
|
||||||
|
const select = xpath.useNamespaces({ xmlns: 'urn:org:ebics:H004' });
|
||||||
|
const keyNodes = select('//xmlns:PubKeyValue', doc);
|
||||||
|
const bankKeys = {};
|
||||||
|
|
||||||
|
if (!keyNodes.length) return {};
|
||||||
|
|
||||||
|
for (let i = 0; i < keyNodes.length; i++) {
|
||||||
|
const type = lastChild(keyNodes[i].parentNode).textContent;
|
||||||
|
const modulus = xpath.select("//*[local-name(.)='Modulus']", keyNodes[i])[0].textContent;
|
||||||
|
const exponent = xpath.select("//*[local-name(.)='Exponent']", keyNodes[i])[0].textContent;
|
||||||
|
|
||||||
|
const mod = new BN(Buffer.from(modulus, 'base64'), 2).toBuffer();
|
||||||
|
const exp = new BN(Buffer.from(exponent, 'base64')).toNumber();
|
||||||
|
|
||||||
|
bankKeys[`bank${type}`] = { mod, exp };
|
||||||
|
|
||||||
|
// const bank = new NodeRSA();
|
||||||
|
|
||||||
|
// bank.importKey({ n: mod, e: exp }, 'components-public');
|
||||||
|
|
||||||
|
// this.keys[`${this.hostId}.${type}`] = new Key(bank);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bankKeys;
|
||||||
|
}
|
||||||
|
|
||||||
toXML() {
|
toXML() {
|
||||||
return new XMLSerializer().serializeToString(this.doc);
|
return new XMLSerializer().serializeToString(this.doc);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const crypto = require('crypto');
|
// const crypto = require('crypto');
|
||||||
|
const Crypto = require('./crypto/Crypto');
|
||||||
|
|
||||||
const { DOMParser, XMLSerializer } = require('xmldom');
|
const { DOMParser, XMLSerializer } = require('xmldom');
|
||||||
const xpath = require('xpath');
|
const xpath = require('xpath');
|
||||||
@ -11,87 +12,48 @@ module.exports = class Signer {
|
|||||||
/**
|
/**
|
||||||
* Contructor.
|
* Contructor.
|
||||||
*
|
*
|
||||||
* @param {Client} client
|
* @param {Keys} keys
|
||||||
* @param {String} data
|
* @param {String} data
|
||||||
*/
|
*/
|
||||||
constructor(client, data) {
|
constructor(keys, data) {
|
||||||
/**
|
/**
|
||||||
* The main client
|
* Keys to operate with
|
||||||
*
|
*
|
||||||
* @type {Signer}
|
* @type {Keys}
|
||||||
*/
|
*/
|
||||||
this.client = client;
|
this.keys = keys;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request data - generated xml
|
* Request data - generated xml
|
||||||
*
|
*
|
||||||
* @type {...}
|
* @type {String}
|
||||||
*/
|
*/
|
||||||
this.doc = new DOMParser().parseFromString(data, 'text/xml');
|
this.doc = new DOMParser().parseFromString(data, 'text/xml');
|
||||||
}
|
}
|
||||||
|
|
||||||
_junk() {
|
|
||||||
this.digest();
|
|
||||||
this.sign();
|
|
||||||
// console.log(this.toXML());
|
|
||||||
/* const headerSet = select(this.doc, "//*[@authenticate='true']").map(x => {
|
|
||||||
// x.setAttribute('xmlns:ds', 'http://www.w3.org/2000/09/xmldsig#');
|
|
||||||
return new c14n().process(x);
|
|
||||||
}).join();
|
|
||||||
const can = headerSet.replace('xmlns="urn:org:ebics:H004"', 'xmlns="urn:org:ebics:H004" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"');
|
|
||||||
|
|
||||||
const hash = crypto.createHash('sha256');
|
|
||||||
hash.update(can);
|
|
||||||
const digester = hash.digest('base64').trim();
|
|
||||||
if ( this.doc.getElementsByTagName("ds:DigestValue")[0] )
|
|
||||||
this.doc.getElementsByTagName("ds:DigestValue")[0].textContent = digester; */
|
|
||||||
|
|
||||||
/* const nodeSet = select(this.doc, "//ds:SignedInfo");
|
|
||||||
const canonicalized = nodeSet.map(x => {
|
|
||||||
const g = x.toString();
|
|
||||||
const res = new c14n().process(x);
|
|
||||||
return res;
|
|
||||||
}).join();
|
|
||||||
|
|
||||||
const canonicalizedString = canonicalized.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 SIGN = crypto.createSign('RSA-SHA256');
|
|
||||||
// SIGN.update(canonicalizedString);
|
|
||||||
// const key = SIGN.sign(this.client.x().key.exportKey("pkcs1-private-pem"), 'base64');
|
|
||||||
const f = this.client.x().key.sign(canonicalizedString, 'base64');
|
|
||||||
if ( this.doc.getElementsByTagName("ds:SignatureValue")[0] ) {
|
|
||||||
this.doc.getElementsByTagName("ds:SignatureValue")[0].textContent = f;
|
|
||||||
} */
|
|
||||||
}
|
|
||||||
|
|
||||||
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']");
|
|
||||||
|
|
||||||
// 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 = xpath.select("//*[@authenticate='true']", this.doc)
|
const contentToDigest = xpath.select("//*[@authenticate='true']", this.doc)
|
||||||
.map(x => new C14n().process(x)).join('');
|
.map(x => 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.digestWithHash(fixedContent).toString('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 select = xpath.useNamespaces({ ds: 'http://www.w3.org/2000/09/xmldsig#' });
|
const select = xpath.useNamespaces({ ds: 'http://www.w3.org/2000/09/xmldsig#' });
|
||||||
const contentToSign = (new C14n().process(select('//ds:SignedInfo', this.doc)[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('//ds:SignedInfo', this.doc)[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 = Crypto.privateSign(this.keys.x(), contentToSign); // this.keys.x().key.sign(contentToSign, 'base64');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
93
lib/crypto/Crypto.js
Normal file
93
lib/crypto/Crypto.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
const BN = require('bn.js');
|
||||||
|
|
||||||
|
const mgf1 = require('./MGF1');
|
||||||
|
|
||||||
|
const modPow = (base, power, mod) => {
|
||||||
|
let result = new BN(1);
|
||||||
|
|
||||||
|
while (power > 0) {
|
||||||
|
result = power.and(new BN(1)) == 1 ? (result.mul(base)).mod(mod) : result; // eslint-disable-line
|
||||||
|
base = (base.mul(base)).mod(mod);
|
||||||
|
power = power.shrn(1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const emsaPSS = (msg, salt) => {
|
||||||
|
const eightNullBytes = Buffer.from('\x00'.repeat(8));
|
||||||
|
const digestedMsg = crypto.createHash('sha256').update(msg).digest();
|
||||||
|
const mTickHash = crypto.createHash('sha256').update(Buffer.concat([eightNullBytes, digestedMsg, salt]), 'binary').digest();
|
||||||
|
|
||||||
|
const ps = Buffer.from('\x00'.repeat(190));
|
||||||
|
const db = Buffer.concat([ps, Buffer.from('\x01'), salt]);
|
||||||
|
|
||||||
|
const dbMask = mgf1.generate(mTickHash, db.length);
|
||||||
|
const maskedDb = mgf1.xor(db, dbMask);
|
||||||
|
|
||||||
|
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]; // eslint-disable-line
|
||||||
|
|
||||||
|
return Buffer.concat([maskedDb, mTickHash, Buffer.from('BC', 'hex')]);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = class Crypto {
|
||||||
|
static digestPublicKey(key) {
|
||||||
|
const str = [key.e('hex').replace(/^(0+)/g, ''), key.n('hex').replace(/^(0+)/g, '')].map(x => x.toLowerCase()).join(' ');
|
||||||
|
|
||||||
|
return crypto.createHash('sha256').update(str).digest('base64').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
static publicEncrypt(key, data) {
|
||||||
|
return crypto.publicEncrypt({
|
||||||
|
key: key.toPem(),
|
||||||
|
padding: crypto.constants.RSA_PKCS1_PADDING,
|
||||||
|
}, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static privateDecrypt(key, data) {
|
||||||
|
return crypto.privateDecrypt({
|
||||||
|
key: key.toPem(),
|
||||||
|
padding: crypto.constants.RSA_PKCS1_PADDING,
|
||||||
|
}, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static privateSign(key, data, outputEncoding = 'base64') {
|
||||||
|
const signer = crypto.createSign('SHA256');
|
||||||
|
|
||||||
|
return signer.update(data).sign(key.toPem(), outputEncoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
static sign(key, msg, salt = crypto.randomBytes(32)) {
|
||||||
|
const base = new BN(emsaPSS(msg, salt));
|
||||||
|
const power = new BN(key.d());
|
||||||
|
const mod = new BN(key.n());
|
||||||
|
|
||||||
|
return (modPow(base, power, mod)).toBuffer().toString('base64');
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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])]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static digestWithHash(data, algorith = 'sha256') {
|
||||||
|
return crypto.createHash(algorith).update(data).digest();
|
||||||
|
}
|
||||||
|
|
||||||
|
static nonce(outputEncoding = 'hex') {
|
||||||
|
return crypto.randomBytes(16).toString(outputEncoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
static timestamp() {
|
||||||
|
return new Date().toISOString();
|
||||||
|
}
|
||||||
|
};
|
40
lib/keymanagers/FsKeyStorage.js
Normal file
40
lib/keymanagers/FsKeyStorage.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
/* const extractKeys = (keysObject, encryptAlgorithm, passphrase) => Object.entries(keysObject).reduce((keys, [key, data]) => {
|
||||||
|
keys[key] = decrypt(data, encryptAlgorithm, passphrase);
|
||||||
|
return keys;
|
||||||
|
}, {}); */
|
||||||
|
|
||||||
|
module.exports = class FsKeyStorage {
|
||||||
|
/**
|
||||||
|
* @param {String} path - destingiton file to save the keys
|
||||||
|
*/
|
||||||
|
constructor({ path }) {
|
||||||
|
if (!path)
|
||||||
|
throw new Error('Invalid path provided');
|
||||||
|
|
||||||
|
this._path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
get path() {
|
||||||
|
return this._path;
|
||||||
|
}
|
||||||
|
|
||||||
|
read() {
|
||||||
|
return fs.readFileSync(this._path, { encoding: 'utf8' });
|
||||||
|
// return extractKeys(JSON.parse(fs.readFileSync(this._path, { encoding: 'utf8' })), this.algorithm, this.passphrase);
|
||||||
|
}
|
||||||
|
|
||||||
|
save(data) {
|
||||||
|
fs.writeFileSync(this._path, data, { encoding: 'utf8' });
|
||||||
|
// fs.writeFileSync(this._path, encrypt(JSON.stringify(data), this.algorithm, this.passphrase), { encoding: 'utf8' });
|
||||||
|
}
|
||||||
|
|
||||||
|
hasData() {
|
||||||
|
if (fs.existsSync(this._path))
|
||||||
|
return this.read() !== '';
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
50
lib/keymanagers/Key.js
Normal file
50
lib/keymanagers/Key.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const BN = require('bn.js');
|
||||||
|
const NodeRSA = require('node-rsa');
|
||||||
|
|
||||||
|
module.exports = class Key {
|
||||||
|
constructor(encodedKey/* , passphrase = null */) {
|
||||||
|
this._key = (encodedKey instanceof NodeRSA) ? encodedKey : new NodeRSA(encodedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
static generate(keysize = 2048) {
|
||||||
|
return new NodeRSA({ b: keysize });
|
||||||
|
}
|
||||||
|
|
||||||
|
static importKey({ mod, exp }) {
|
||||||
|
const key = new NodeRSA();
|
||||||
|
|
||||||
|
key.importKey({ n: mod, e: exp }, 'components-public');
|
||||||
|
|
||||||
|
return new Key(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
get key() {
|
||||||
|
return this._key;
|
||||||
|
}
|
||||||
|
|
||||||
|
n(to = 'buff') {
|
||||||
|
const keyN = Buffer.from(this.key.exportKey('components-public').n);
|
||||||
|
|
||||||
|
return to === 'hex'
|
||||||
|
? keyN.toString('hex', 1)
|
||||||
|
: keyN;
|
||||||
|
}
|
||||||
|
|
||||||
|
e(to = 'buff') {
|
||||||
|
const eKey = new BN(this.key.exportKey('components-public').e).toBuffer();
|
||||||
|
|
||||||
|
return to === 'hex'
|
||||||
|
? eKey.toString('hex')
|
||||||
|
: eKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
d() {
|
||||||
|
return this.key.keyPair.d.toBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
toPem() {
|
||||||
|
return this.key.isPrivate() ? this.key.exportKey('pkcs1-private-pem') : this.key.exportKey('pkcs8-public-pem');
|
||||||
|
}
|
||||||
|
};
|
58
lib/keymanagers/Keys.js
Normal file
58
lib/keymanagers/Keys.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Key = require('./Key');
|
||||||
|
|
||||||
|
const keyOrNull = key => (key ? new Key(key) : null);
|
||||||
|
|
||||||
|
module.exports = class Keys {
|
||||||
|
constructor({
|
||||||
|
A006,
|
||||||
|
E002,
|
||||||
|
X002,
|
||||||
|
bankX002,
|
||||||
|
bankE002,
|
||||||
|
}) {
|
||||||
|
this.keys = {
|
||||||
|
A006: keyOrNull(A006),
|
||||||
|
E002: keyOrNull(E002),
|
||||||
|
X002: keyOrNull(X002),
|
||||||
|
bankX002: keyOrNull(bankX002),
|
||||||
|
bankE002: keyOrNull(bankE002),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static generate() {
|
||||||
|
const keys = {};
|
||||||
|
|
||||||
|
Object.keys({ A006: '', X002: '', E002: '' }).forEach((key) => {
|
||||||
|
keys[key] = Key.generate();
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Keys(keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
setBankKeys(bankKeys) {
|
||||||
|
this.keys.bankX002 = Key.importKey(bankKeys.bankX002);
|
||||||
|
this.keys.bankE002 = Key.importKey(bankKeys.bankE002);
|
||||||
|
}
|
||||||
|
|
||||||
|
a() {
|
||||||
|
return this.keys.A006;
|
||||||
|
}
|
||||||
|
|
||||||
|
e() {
|
||||||
|
return this.keys.E002;
|
||||||
|
}
|
||||||
|
|
||||||
|
x() {
|
||||||
|
return this.keys.X002;
|
||||||
|
}
|
||||||
|
|
||||||
|
bankX() {
|
||||||
|
return this.keys.bankX002;
|
||||||
|
}
|
||||||
|
|
||||||
|
bankE() {
|
||||||
|
return this.keys.bankE002;
|
||||||
|
}
|
||||||
|
};
|
94
lib/keymanagers/KeysManager.js
Normal file
94
lib/keymanagers/KeysManager.js
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
const Keys = require('./Keys');
|
||||||
|
|
||||||
|
const encrypt = (data, algorithm, passphrase) => {
|
||||||
|
const cipher = crypto.createCipher(algorithm, passphrase);
|
||||||
|
const encrypted = cipher.update(data, 'utf8', 'hex') + cipher.final('hex');
|
||||||
|
|
||||||
|
return Buffer.from(encrypted).toString('base64');
|
||||||
|
};
|
||||||
|
const decrypt = (data, algorithm, passphrase) => {
|
||||||
|
data = (Buffer.from(data, 'base64')).toString();
|
||||||
|
|
||||||
|
const decipher = crypto.createDecipher(algorithm, passphrase);
|
||||||
|
const decrypted = decipher.update(data, 'hex', 'utf8') + decipher.final('utf8');
|
||||||
|
|
||||||
|
return decrypted;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = class KeysManager {
|
||||||
|
constructor(keysStorage, passphrase, algorithm = 'aes-256-cbc', createIfNone = true) {
|
||||||
|
this._storage = keysStorage;
|
||||||
|
this._passphrase = passphrase;
|
||||||
|
this._algorithm = algorithm;
|
||||||
|
|
||||||
|
if (createIfNone && !this._storage.hasData())
|
||||||
|
this.generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the keys to work with. Then either
|
||||||
|
* saves them to the storage or returnes the keys generated
|
||||||
|
*
|
||||||
|
* @param {Boolean} save
|
||||||
|
* @default true
|
||||||
|
*
|
||||||
|
* @returns void | Keys object
|
||||||
|
*/
|
||||||
|
generate(save = true) {
|
||||||
|
const keys = Keys.generate();
|
||||||
|
|
||||||
|
if (save) this.write(keys);
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the keys to the storage
|
||||||
|
*
|
||||||
|
* @param {Keys} keysObject
|
||||||
|
*
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
write(keysObject) {
|
||||||
|
keysObject = keysObject.keys;
|
||||||
|
|
||||||
|
Object.keys(keysObject).map((key) => {
|
||||||
|
keysObject[key] = keysObject[key] === null ? null : keysObject[key].toPem();
|
||||||
|
|
||||||
|
return key;
|
||||||
|
});
|
||||||
|
|
||||||
|
this._storage.save(encrypt(JSON.stringify(keysObject), this._algorithm, this._passphrase));
|
||||||
|
}
|
||||||
|
|
||||||
|
setBankKeys(bankKeys) {
|
||||||
|
const keys = this.keys();
|
||||||
|
|
||||||
|
keys.setBankKeys(bankKeys);
|
||||||
|
this.write(keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the keys
|
||||||
|
*
|
||||||
|
* @returns Keys object
|
||||||
|
*/
|
||||||
|
keys() {
|
||||||
|
return this._read();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the keys from the storage
|
||||||
|
*
|
||||||
|
* @returns Keys object
|
||||||
|
*/
|
||||||
|
_read() {
|
||||||
|
const keysString = this._storage.read();
|
||||||
|
|
||||||
|
return new Keys(JSON.parse(decrypt(keysString, this._algorithm, this._passphrase)));
|
||||||
|
}
|
||||||
|
};
|
@ -3,14 +3,8 @@
|
|||||||
const Response = require('../Response');
|
const Response = require('../Response');
|
||||||
|
|
||||||
module.exports = class ParseResponse {
|
module.exports = class ParseResponse {
|
||||||
constructor(client, data) {
|
static go(keys, data) {
|
||||||
this.client = client;
|
const response = new Response(keys, data);
|
||||||
this.data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static go(client, data) {
|
|
||||||
// const parseRensponse = new ParseResponse(client, data);
|
|
||||||
const response = new Response(client, data);
|
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// raise error if any
|
// raise error if any
|
||||||
|
@ -3,14 +3,8 @@
|
|||||||
const Signer = require('../Signer');
|
const Signer = require('../Signer');
|
||||||
|
|
||||||
module.exports = class XMLSign {
|
module.exports = class XMLSign {
|
||||||
constructor(client, data) {
|
static go(keys, xml) {
|
||||||
this.client = client;
|
const signer = new Signer(keys, xml);
|
||||||
this.data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static go(client, data) {
|
|
||||||
// const xmlSigner = new XMLSign(client, data);
|
|
||||||
const signer = new Signer(client, data);
|
|
||||||
|
|
||||||
signer.digest();
|
signer.digest();
|
||||||
signer.sign();
|
signer.sign();
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const GenericOrder = require('./GenericOrder');
|
|
||||||
|
|
||||||
module.exports = class C52 extends GenericOrder {
|
|
||||||
constructor(client, from, to) {
|
|
||||||
super(client);
|
|
||||||
this._from = from;
|
|
||||||
this._to = to;
|
|
||||||
|
|
||||||
this._schema.header = {
|
|
||||||
'@': {
|
|
||||||
authenticate: true,
|
|
||||||
},
|
|
||||||
static: {
|
|
||||||
HostID: this.hostId,
|
|
||||||
Nonce: this.nonce(),
|
|
||||||
Timestamp: this.timestamp(),
|
|
||||||
PartnerID: this.partnerId,
|
|
||||||
UserID: this.userId,
|
|
||||||
Product: {
|
|
||||||
'@': {
|
|
||||||
Language: 'de',
|
|
||||||
},
|
|
||||||
'#': this.productString,
|
|
||||||
},
|
|
||||||
OrderDetails: {
|
|
||||||
OrderType: 'C52',
|
|
||||||
OrderAttribute: 'DZHNN',
|
|
||||||
StandardOrderParams: {
|
|
||||||
DateRange: {
|
|
||||||
Start: this._from,
|
|
||||||
End: this._to,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
BankPubKeyDigests: {
|
|
||||||
Authentication: {
|
|
||||||
'@': {
|
|
||||||
Version: 'X002',
|
|
||||||
Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256',
|
|
||||||
},
|
|
||||||
'#': this.client.bankX().publicDigest(),
|
|
||||||
},
|
|
||||||
Encryption: {
|
|
||||||
'@': {
|
|
||||||
Version: 'E002',
|
|
||||||
Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256',
|
|
||||||
},
|
|
||||||
'#': this.client.bankE().publicDigest(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SecurityMedium: '0000',
|
|
||||||
},
|
|
||||||
mutable: {
|
|
||||||
TransactionPhase: 'Initialisation',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,181 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
// const randHex = require('../../lib/utils').randHex;
|
|
||||||
const crypto = require('crypto');
|
|
||||||
const js2xmlparser = require('js2xmlparser');
|
|
||||||
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 {
|
|
||||||
constructor(client) {
|
|
||||||
this.client = client;
|
|
||||||
|
|
||||||
this.hostId = client.hostId;
|
|
||||||
this.userId = client.userId;
|
|
||||||
this.partnerId = client.partnerId;
|
|
||||||
|
|
||||||
this.transactionId = '';
|
|
||||||
|
|
||||||
this.xmlOptions = {
|
|
||||||
declaration: {
|
|
||||||
include: true,
|
|
||||||
encoding: 'utf-8',
|
|
||||||
},
|
|
||||||
format: {
|
|
||||||
doubleQuotes: true,
|
|
||||||
indent: '',
|
|
||||||
newline: '',
|
|
||||||
pretty: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
this._schema = {
|
|
||||||
'@': {
|
|
||||||
'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
|
|
||||||
xmlns: 'urn:org:ebics:H004',
|
|
||||||
Version: 'H004',
|
|
||||||
Revision: '1',
|
|
||||||
},
|
|
||||||
|
|
||||||
header: {},
|
|
||||||
|
|
||||||
AuthSignature: authSignature(),
|
|
||||||
|
|
||||||
body: {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get schema() {
|
|
||||||
return this._schema;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get productString() {
|
|
||||||
return consts.productString;
|
|
||||||
}
|
|
||||||
|
|
||||||
static nonce() {
|
|
||||||
return crypto.randomBytes(16).toString('hex');
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove eslint-disable-line
|
|
||||||
timestamp() { // eslint-disable-line
|
|
||||||
return new Date().toISOString();
|
|
||||||
}
|
|
||||||
|
|
||||||
root() { // eslint-disable-line class-methods-use-this
|
|
||||||
return 'ebicsRequest';
|
|
||||||
}
|
|
||||||
|
|
||||||
toReceiptXML() {
|
|
||||||
const xmlObj = {
|
|
||||||
'@': {
|
|
||||||
'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
|
|
||||||
xmlns: 'urn:org:ebics:H004',
|
|
||||||
Version: 'H004',
|
|
||||||
Revision: '1',
|
|
||||||
},
|
|
||||||
|
|
||||||
header: {
|
|
||||||
'@': { authenticate: true },
|
|
||||||
static: {
|
|
||||||
HostID: this.hostId,
|
|
||||||
TransactionID: this.transactionId,
|
|
||||||
},
|
|
||||||
mutable: {
|
|
||||||
TransactionPhase: 'Receipt',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
AuthSignature: this.authSignature(),
|
|
||||||
|
|
||||||
body: {
|
|
||||||
TransferReceipt: {
|
|
||||||
'@': { authenticate: true },
|
|
||||||
ReceiptCode: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return js2xmlparser.parse(this.root(), xmlObj, this.xmlOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
toTransferXML() {
|
|
||||||
const xmlObj = {
|
|
||||||
'@': {
|
|
||||||
'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
|
|
||||||
xmlns: 'urn:org:ebics:H004',
|
|
||||||
Version: 'H004',
|
|
||||||
Revision: '1',
|
|
||||||
},
|
|
||||||
|
|
||||||
header: {
|
|
||||||
'@': { authenticate: true },
|
|
||||||
static: {
|
|
||||||
HostID: this.hostId,
|
|
||||||
TransactionID: this.transactionId,
|
|
||||||
},
|
|
||||||
mutable: {
|
|
||||||
TransactionPhase: 'Transfer',
|
|
||||||
SegmentNumber: {
|
|
||||||
'@': { lastSegment: true },
|
|
||||||
'#': 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
AuthSignature: this.authSignature(),
|
|
||||||
|
|
||||||
body: {
|
|
||||||
DataTransfer: {
|
|
||||||
OrderData: this.encryptedOrderData(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return js2xmlparser.parse(this.root(), xmlObj, this.xmlOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
encryptedOrderData() { // eslint-disable-line class-methods-use-this
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
toXML() {
|
|
||||||
return js2xmlparser.parse(this.root(), this._schema, this.xmlOptions);
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,79 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const zlib = require('zlib');
|
|
||||||
const crypto = require('crypto');
|
|
||||||
|
|
||||||
const js2xmlparser = require('js2xmlparser');
|
|
||||||
|
|
||||||
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 {
|
|
||||||
constructor(client, document) {
|
|
||||||
super(client);
|
|
||||||
|
|
||||||
this._document = document;
|
|
||||||
this._key = crypto.randomBytes(16);
|
|
||||||
|
|
||||||
this._schema.body = {
|
|
||||||
DataTransfer: {
|
|
||||||
DataEncryptionInfo: {
|
|
||||||
'@': { authenticate: true },
|
|
||||||
EncryptionPubKeyDigest: {
|
|
||||||
'@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
|
|
||||||
'#': this.client.bankE().publicDigest(),
|
|
||||||
},
|
|
||||||
TransactionKey: this.client.bankE().publicEncrypt(this._key).toString('base64'),
|
|
||||||
},
|
|
||||||
SignatureData: {
|
|
||||||
'@': { authenticate: true },
|
|
||||||
'#': this.encryptedOrderSignature(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
orderSignature() {
|
|
||||||
const xmlObj = {
|
|
||||||
'@': {
|
|
||||||
xmlns: 'http://www.ebics.org/S001',
|
|
||||||
'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
|
|
||||||
'xsi:schemaLocation': 'http://www.ebics.org/S001 http://www.ebics.org/S001/ebics_signature.xsd',
|
|
||||||
},
|
|
||||||
OrderSignatureData: {
|
|
||||||
SignatureVersion: 'A006',
|
|
||||||
SignatureValue: this.signatureValue(),
|
|
||||||
PartnerID: this.partnerId,
|
|
||||||
UserID: this.userId,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return js2xmlparser.parse('UserSignatureData', xmlObj, this.xmlOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
signatureValue() {
|
|
||||||
const digested = crypto.createHash('sha256').update(this._document.replace(/\n|\r/g, '')).digest();
|
|
||||||
|
|
||||||
return this.client.a().sign(digested);
|
|
||||||
}
|
|
||||||
|
|
||||||
encryptedOrderData() {
|
|
||||||
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);
|
|
||||||
|
|
||||||
return Buffer.concat([cipher.update(pad(dst)), cipher.final()]).toString('base64');
|
|
||||||
}
|
|
||||||
|
|
||||||
encryptedOrderSignature() {
|
|
||||||
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);
|
|
||||||
|
|
||||||
return Buffer.concat([cipher.update(pad(dst)), cipher.final()]).toString('base64');
|
|
||||||
}
|
|
||||||
};
|
|
81
lib/orders/H004/GenericSerializer.js
Normal file
81
lib/orders/H004/GenericSerializer.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const js2xmlparser = require('js2xmlparser');
|
||||||
|
|
||||||
|
const xmlOptions = {
|
||||||
|
declaration: {
|
||||||
|
include: true,
|
||||||
|
encoding: 'utf-8',
|
||||||
|
},
|
||||||
|
format: {
|
||||||
|
doubleQuotes: true,
|
||||||
|
indent: '',
|
||||||
|
newline: '',
|
||||||
|
pretty: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const authSignature = ({
|
||||||
|
'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 GenericSerializer {
|
||||||
|
constructor(orderBuilder) {
|
||||||
|
this._order = orderBuilder;
|
||||||
|
this._orderDetails = orderBuilder.orderDetails;
|
||||||
|
this._hostId = orderBuilder.hostId;
|
||||||
|
this._partnerId = orderBuilder.partnerId;
|
||||||
|
this._userId = orderBuilder.userId;
|
||||||
|
this._keys = orderBuilder.keys;
|
||||||
|
this._transactionId = orderBuilder.transactionId;
|
||||||
|
this._rootName = orderBuilder.root.nodeName;
|
||||||
|
this._rootAttributes = orderBuilder.root.nodeAttributes;
|
||||||
|
|
||||||
|
this._xmlOptions = xmlOptions;
|
||||||
|
this._xml = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static authSignature() {
|
||||||
|
return authSignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
get keys() {
|
||||||
|
return this._keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
toXML() {
|
||||||
|
return js2xmlparser.parse(this._rootName, this._xml, this._xmlOptions);
|
||||||
|
}
|
||||||
|
};
|
128
lib/orders/H004/InitializationSerializer.js
Normal file
128
lib/orders/H004/InitializationSerializer.js
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const zlib = require('zlib');
|
||||||
|
const js2xmlparser = require('js2xmlparser');
|
||||||
|
|
||||||
|
const consts = require('../../consts');
|
||||||
|
const Crypto = require('../../crypto/Crypto');
|
||||||
|
|
||||||
|
const GenericSerializer = require('./GenericSerializer');
|
||||||
|
|
||||||
|
module.exports = class InitializationSerializer extends GenericSerializer {
|
||||||
|
constructor(order) {
|
||||||
|
super(order);
|
||||||
|
|
||||||
|
this._xml = {
|
||||||
|
'@': this._rootAttributes,
|
||||||
|
header: {
|
||||||
|
'@': { authenticate: true },
|
||||||
|
static: {
|
||||||
|
HostID: this._hostId,
|
||||||
|
Nonce: Crypto.nonce(),
|
||||||
|
Timestamp: Crypto.timestamp(),
|
||||||
|
PartnerID: this._partnerId,
|
||||||
|
UserID: this._userId,
|
||||||
|
Product: {
|
||||||
|
'@': { Language: 'en' },
|
||||||
|
'#': consts.productString,
|
||||||
|
},
|
||||||
|
OrderDetails: this._orderDetails,
|
||||||
|
SecurityMedium: '0000',
|
||||||
|
},
|
||||||
|
mutable: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this._isINI() || this._isHIA()) {
|
||||||
|
delete this._xml.header.static.Nonce;
|
||||||
|
delete this._xml.header.static.Timestamp;
|
||||||
|
|
||||||
|
this._xml.body = {
|
||||||
|
DataTransfer: {
|
||||||
|
OrderData: this.orderData(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this._rootName = 'ebicsNoPubKeyDigestsRequest';
|
||||||
|
this._xml.AuthSignature = GenericSerializer.authSignature();
|
||||||
|
this._xml.body = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
orderData() {
|
||||||
|
if (this._isINI()) return this._iniKeySignature();
|
||||||
|
if (this._isHIA()) return this._hiaOrderData();
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
_iniKeySignature() {
|
||||||
|
const xmlOrderData = {
|
||||||
|
'@': {
|
||||||
|
'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
|
||||||
|
xmlns: 'http://www.ebics.org/S001',
|
||||||
|
},
|
||||||
|
SignaturePubKeyInfo: {
|
||||||
|
PubKeyValue: {
|
||||||
|
'ds:RSAKeyValue': {
|
||||||
|
'ds:Modulus': Buffer.from(this._keys.a().n(), 'HEX').toString('base64'),
|
||||||
|
'ds:Exponent': this._keys.a().e().toString('base64'),
|
||||||
|
},
|
||||||
|
TimeStamp: Crypto.timestamp(),
|
||||||
|
},
|
||||||
|
SignatureVersion: 'A006',
|
||||||
|
},
|
||||||
|
PartnerID: this._partnerId,
|
||||||
|
UserID: this._userId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const signature = js2xmlparser.parse('SignaturePubKeyOrderData', xmlOrderData, this._xmlOptions);
|
||||||
|
|
||||||
|
return Buffer.from(zlib.deflateSync(signature)).toString('base64');
|
||||||
|
}
|
||||||
|
|
||||||
|
_hiaOrderData() {
|
||||||
|
const xmlOrderData = {
|
||||||
|
'@': {
|
||||||
|
'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
|
||||||
|
xmlns: 'urn:org:ebics:H004',
|
||||||
|
},
|
||||||
|
AuthenticationPubKeyInfo: {
|
||||||
|
PubKeyValue: {
|
||||||
|
'ds:RSAKeyValue': {
|
||||||
|
'ds:Modulus': Buffer.from(this._keys.x().n(), 'HEX').toString('base64'),
|
||||||
|
'ds:Exponent': this._keys.x().e().toString('base64'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
AuthenticationVersion: 'X002',
|
||||||
|
},
|
||||||
|
EncryptionPubKeyInfo: {
|
||||||
|
PubKeyValue: {
|
||||||
|
'ds:RSAKeyValue': {
|
||||||
|
'ds:Modulus': Buffer.from(this.keys.e().n(), 'HEX').toString('base64'),
|
||||||
|
'ds:Exponent': this._keys.e().e().toString('base64'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
EncryptionVersion: 'E002',
|
||||||
|
},
|
||||||
|
PartnerID: this._partnerId,
|
||||||
|
UserID: this._userId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const order = js2xmlparser.parse('HIARequestOrderData', xmlOrderData, this._xmlOptions);
|
||||||
|
|
||||||
|
return Buffer.from(zlib.deflateSync(order)).toString('base64');
|
||||||
|
}
|
||||||
|
|
||||||
|
_isINI() {
|
||||||
|
return this._orderDetails.OrderType.toUpperCase() === 'INI';
|
||||||
|
}
|
||||||
|
|
||||||
|
_isHIA() {
|
||||||
|
return this._orderDetails.OrderType.toUpperCase() === 'HIA';
|
||||||
|
}
|
||||||
|
|
||||||
|
_isHPB() {
|
||||||
|
return this._orderDetails.OrderType.toUpperCase() === 'HPB';
|
||||||
|
}
|
||||||
|
};
|
78
lib/orders/H004/OrderBuilder.js
Normal file
78
lib/orders/H004/OrderBuilder.js
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
// const orderTypes = ['ini', 'download', 'upload', 'zip'];
|
||||||
|
|
||||||
|
module.exports = class OrderBuilder {
|
||||||
|
constructor() {
|
||||||
|
this._transactionKey = crypto.randomBytes(16);
|
||||||
|
this._root = {
|
||||||
|
nodeName: 'ebicsRequest',
|
||||||
|
nodeAttributes: {
|
||||||
|
'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
|
||||||
|
xmlns: 'urn:org:ebics:H004',
|
||||||
|
Version: 'H004',
|
||||||
|
Revision: '1',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
this._body = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
details(data) {
|
||||||
|
this._data = data;
|
||||||
|
this._data.transactionId = null;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static payment() {
|
||||||
|
const builder = new OrderBuilder();
|
||||||
|
|
||||||
|
builder._type = 'payment';
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
static status() {
|
||||||
|
const builder = new OrderBuilder();
|
||||||
|
|
||||||
|
builder._type = 'status';
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ini() {
|
||||||
|
const builder = new OrderBuilder();
|
||||||
|
|
||||||
|
builder._type = 'ini';
|
||||||
|
builder._root.nodeName = 'ebicsUnsecuredRequest';
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getters
|
||||||
|
*/
|
||||||
|
get type() { return this._type; }
|
||||||
|
get root() { return this._root; }
|
||||||
|
get body() { return this._body; }
|
||||||
|
get data() { return this._data; }
|
||||||
|
get orderDetails() { return this._data.orderDetails; }
|
||||||
|
get transactionId() { return this._data.transactionId; }
|
||||||
|
get document() { return this._data.document; }
|
||||||
|
get transactionKey() { return this._transactionKey; }
|
||||||
|
get ebicsData() { return this._data.ebicsData; }
|
||||||
|
get hostId() { return this._data.ebicsData.hostId; }
|
||||||
|
get partnerId() { return this._data.ebicsData.partnerId; }
|
||||||
|
get userId() { return this._data.ebicsData.userId; }
|
||||||
|
get keys() { return this._data.ebicsData.keysManager.keys(); }
|
||||||
|
|
||||||
|
set transactionId(tid) {
|
||||||
|
this._data.transactionId = tid === '' ? null : tid;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasTransactionId() {
|
||||||
|
return this.transactionId !== null;
|
||||||
|
}
|
||||||
|
};
|
15
lib/orders/H004/OrderSerializer.js
Normal file
15
lib/orders/H004/OrderSerializer.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const InitializationSerializer = require('./InitializationSerializer');
|
||||||
|
const StatusSerializer = require('./StatusSerializer');
|
||||||
|
const PaymentSerializer = require('./PaymentSerializer');
|
||||||
|
|
||||||
|
module.exports = class OrderSerializer {
|
||||||
|
static serialize(order) {
|
||||||
|
if (order.type === 'ini') return new InitializationSerializer(order);
|
||||||
|
if (order.type === 'payment') return new PaymentSerializer(order);
|
||||||
|
if (order.type === 'status') return new StatusSerializer(order);
|
||||||
|
|
||||||
|
throw Error('Incorect order type. Available types: ini, status, payment, statement');
|
||||||
|
}
|
||||||
|
};
|
131
lib/orders/H004/PaymentSerializer.js
Normal file
131
lib/orders/H004/PaymentSerializer.js
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const zlib = require('zlib');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
const js2xmlparser = require('js2xmlparser');
|
||||||
|
|
||||||
|
const consts = require('../../consts');
|
||||||
|
const Crypto = require('../../crypto/Crypto');
|
||||||
|
|
||||||
|
const GenericSerializer = require('./GenericSerializer');
|
||||||
|
|
||||||
|
module.exports = class PaymentSerializer extends GenericSerializer {
|
||||||
|
constructor(order) {
|
||||||
|
super(order);
|
||||||
|
|
||||||
|
this._transactionKey = order.transactionKey;
|
||||||
|
|
||||||
|
this._xml = {
|
||||||
|
'@': this._rootAttributes,
|
||||||
|
header: {
|
||||||
|
'@': { authenticate: true },
|
||||||
|
static: {
|
||||||
|
HostID: this._hostId,
|
||||||
|
Nonce: Crypto.nonce(),
|
||||||
|
Timestamp: Crypto.timestamp(),
|
||||||
|
PartnerID: this._partnerId,
|
||||||
|
UserID: this._userId,
|
||||||
|
Product: {
|
||||||
|
'@': { Language: 'en' },
|
||||||
|
'#': consts.productString,
|
||||||
|
},
|
||||||
|
OrderDetails: this._orderDetails,
|
||||||
|
BankPubKeyDigests: {
|
||||||
|
Authentication: {
|
||||||
|
'@': { Version: 'X002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
|
||||||
|
'#': Crypto.digestPublicKey(this._keys.bankX()),
|
||||||
|
},
|
||||||
|
Encryption: {
|
||||||
|
'@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
|
||||||
|
'#': Crypto.digestPublicKey(this._keys.bankE()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SecurityMedium: '0000',
|
||||||
|
NumSegments: 1,
|
||||||
|
},
|
||||||
|
mutable: {
|
||||||
|
TransactionPhase: 'Initialisation',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
AuthSignature: GenericSerializer.authSignature(),
|
||||||
|
body: {
|
||||||
|
DataTransfer: {
|
||||||
|
DataEncryptionInfo: {
|
||||||
|
'@': { authenticate: true },
|
||||||
|
EncryptionPubKeyDigest: {
|
||||||
|
'@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
|
||||||
|
'#': Crypto.digestPublicKey(this._keys.bankE()),
|
||||||
|
},
|
||||||
|
TransactionKey: Crypto.publicEncrypt(this._keys.bankE(), this._transactionKey).toString('base64'),
|
||||||
|
},
|
||||||
|
SignatureData: {
|
||||||
|
'@': { authenticate: true },
|
||||||
|
'#': this.encryptedOrderSignature(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (order.hasTransactionId()) {
|
||||||
|
this._xml.header = {
|
||||||
|
'@': { authenticate: true },
|
||||||
|
static: {
|
||||||
|
HostID: this._hostId,
|
||||||
|
TransactionID: this._transactionId,
|
||||||
|
},
|
||||||
|
mutable: {
|
||||||
|
TransactionPhase: 'Transfer',
|
||||||
|
SegmentNumber: {
|
||||||
|
'@': { lastSegment: true },
|
||||||
|
'#': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
this._xml.body = {
|
||||||
|
DataTransfer: {
|
||||||
|
OrderData: this.encryptedOrderData(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
orderSignature() {
|
||||||
|
const xmlObj = {
|
||||||
|
'@': {
|
||||||
|
xmlns: 'http://www.ebics.org/S001',
|
||||||
|
'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
|
||||||
|
'xsi:schemaLocation': 'http://www.ebics.org/S001 http://www.ebics.org/S001/ebics_signature.xsd',
|
||||||
|
},
|
||||||
|
OrderSignatureData: {
|
||||||
|
SignatureVersion: 'A006',
|
||||||
|
SignatureValue: this.signatureValue(),
|
||||||
|
PartnerID: this._partnerId,
|
||||||
|
UserID: this._userId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return js2xmlparser.parse('UserSignatureData', xmlObj, this._xmlOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
signatureValue() {
|
||||||
|
const digested = Crypto.digestWithHash(this._order.document.replace(/\n|\r/g, ''));
|
||||||
|
|
||||||
|
return Crypto.sign(this._keys.a(), digested);
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptedOrderData() {
|
||||||
|
const dst = zlib.deflateSync(this._order.document.replace(/\n|\r/g, ''));
|
||||||
|
const cipher = crypto.createCipheriv('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);
|
||||||
|
|
||||||
|
return Buffer.concat([cipher.update(Crypto.pad(dst)), cipher.final()]).toString('base64');
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptedOrderSignature() {
|
||||||
|
const dst = zlib.deflateSync(this.orderSignature());
|
||||||
|
const cipher = crypto.createCipheriv('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);
|
||||||
|
|
||||||
|
return Buffer.concat([cipher.update(Crypto.pad(dst)), cipher.final()]).toString('base64');
|
||||||
|
}
|
||||||
|
};
|
67
lib/orders/H004/StatusSerializer.js
Normal file
67
lib/orders/H004/StatusSerializer.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const consts = require('../../consts');
|
||||||
|
const Crypto = require('../../crypto/Crypto');
|
||||||
|
|
||||||
|
const GenericSerializer = require('./GenericSerializer');
|
||||||
|
|
||||||
|
module.exports = class StatusSerializer extends GenericSerializer {
|
||||||
|
constructor(order) {
|
||||||
|
super(order);
|
||||||
|
|
||||||
|
this._xml = {
|
||||||
|
'@': this._rootAttributes,
|
||||||
|
header: {
|
||||||
|
'@': { authenticate: true },
|
||||||
|
static: {
|
||||||
|
HostID: this._hostId,
|
||||||
|
Nonce: Crypto.nonce(),
|
||||||
|
Timestamp: Crypto.timestamp(),
|
||||||
|
PartnerID: this._partnerId,
|
||||||
|
UserID: this._userId,
|
||||||
|
Product: {
|
||||||
|
'@': { Language: 'en' },
|
||||||
|
'#': consts.productString,
|
||||||
|
},
|
||||||
|
OrderDetails: this._orderDetails,
|
||||||
|
BankPubKeyDigests: {
|
||||||
|
Authentication: {
|
||||||
|
'@': { Version: 'X002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
|
||||||
|
'#': Crypto.digestPublicKey(this._keys.bankX()),
|
||||||
|
},
|
||||||
|
Encryption: {
|
||||||
|
'@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
|
||||||
|
'#': Crypto.digestPublicKey(this._keys.bankE()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SecurityMedium: '0000',
|
||||||
|
},
|
||||||
|
mutable: {
|
||||||
|
TransactionPhase: 'Initialisation',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
AuthSignature: GenericSerializer.authSignature(),
|
||||||
|
body: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (order.hasTransactionId()) {
|
||||||
|
this._xml.header = {
|
||||||
|
'@': { authenticate: true },
|
||||||
|
static: {
|
||||||
|
HostID: this._hostId,
|
||||||
|
TransactionID: this._transactionId,
|
||||||
|
},
|
||||||
|
mutable: {
|
||||||
|
TransactionPhase: 'Receipt',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
this._xml.body = {
|
||||||
|
TransferReceipt: {
|
||||||
|
'@': { authenticate: true },
|
||||||
|
ReceiptCode: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -1,43 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const GenericOrder = require('./GenericOrder');
|
|
||||||
|
|
||||||
module.exports = class HAA extends GenericOrder {
|
|
||||||
constructor(client) {
|
|
||||||
super(client);
|
|
||||||
|
|
||||||
this._schema.header = {
|
|
||||||
'@': { authenticate: true },
|
|
||||||
static: {
|
|
||||||
HostID: this.hostId,
|
|
||||||
Nonce: this.nonce(),
|
|
||||||
Timestamp: this.timestamp(),
|
|
||||||
PartnerID: this.partnerId,
|
|
||||||
UserID: this.userId,
|
|
||||||
Product: {
|
|
||||||
'@': { Language: 'de' },
|
|
||||||
'#': this.productString,
|
|
||||||
},
|
|
||||||
OrderDetails: {
|
|
||||||
OrderType: 'HAA',
|
|
||||||
OrderAttribute: 'DZHNN',
|
|
||||||
StandardOrderParams: '',
|
|
||||||
},
|
|
||||||
BankPubKeyDigests: {
|
|
||||||
Authentication: {
|
|
||||||
'@': { Version: 'X002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
|
|
||||||
'#': this.client.bankX().publicDigest(),
|
|
||||||
},
|
|
||||||
Encryption: {
|
|
||||||
'@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
|
|
||||||
'#': this.client.bankE().publicDigest(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SecurityMedium: '0000',
|
|
||||||
},
|
|
||||||
mutable: {
|
|
||||||
TransactionPhase: 'Initialisation',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,54 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const GenericOrder = require('./GenericOrder');
|
|
||||||
|
|
||||||
module.exports = class HAC extends GenericOrder {
|
|
||||||
constructor(client, from = null, to = null) {
|
|
||||||
super(client);
|
|
||||||
this._from = from;
|
|
||||||
this._to = to;
|
|
||||||
|
|
||||||
this._schema.header = {
|
|
||||||
'@': { authenticate: true },
|
|
||||||
static: {
|
|
||||||
HostID: this.hostId,
|
|
||||||
Nonce: this.nonce(),
|
|
||||||
Timestamp: this.timestamp(),
|
|
||||||
PartnerID: this.partnerId,
|
|
||||||
UserID: this.userId,
|
|
||||||
Product: {
|
|
||||||
'@': { Language: 'de' },
|
|
||||||
'#': this.productString,
|
|
||||||
},
|
|
||||||
OrderDetails: {
|
|
||||||
OrderType: 'HAC',
|
|
||||||
OrderAttribute: 'DZHNN',
|
|
||||||
StandardOrderParams: this._hasDateRange() ? {
|
|
||||||
DateRange: {
|
|
||||||
Start: this._from,
|
|
||||||
End: this._to,
|
|
||||||
},
|
|
||||||
} : '',
|
|
||||||
},
|
|
||||||
BankPubKeyDigests: {
|
|
||||||
Authentication: {
|
|
||||||
'@': { Version: 'X002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
|
|
||||||
'#': this.client.bankX().publicDigest(),
|
|
||||||
},
|
|
||||||
Encryption: {
|
|
||||||
'@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
|
|
||||||
'#': this.client.bankE().publicDigest(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SecurityMedium: '0000',
|
|
||||||
},
|
|
||||||
mutable: {
|
|
||||||
TransactionPhase: 'Initialisation',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_hasDateRange() {
|
|
||||||
return this._from && this._to;
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,85 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const zlib = require('zlib');
|
|
||||||
const js2xmlparser = require('js2xmlparser');
|
|
||||||
|
|
||||||
const GenericOrder = require('./GenericOrder');
|
|
||||||
|
|
||||||
module.exports = class HIA extends GenericOrder {
|
|
||||||
constructor(client) {
|
|
||||||
super(client);
|
|
||||||
|
|
||||||
this._schema = {
|
|
||||||
'@': {
|
|
||||||
'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
|
|
||||||
xmlns: 'urn:org:ebics:H004',
|
|
||||||
Version: 'H004',
|
|
||||||
Revision: '1',
|
|
||||||
},
|
|
||||||
|
|
||||||
header: {
|
|
||||||
'@': { authenticate: true },
|
|
||||||
static: {
|
|
||||||
HostID: this.hostId,
|
|
||||||
PartnerID: this.partnerId,
|
|
||||||
UserID: this.userId,
|
|
||||||
Product: {
|
|
||||||
'@': { Language: 'de' },
|
|
||||||
'#': this.productString,
|
|
||||||
},
|
|
||||||
OrderDetails: {
|
|
||||||
OrderType: 'HIA',
|
|
||||||
OrderAttribute: 'DZNNN',
|
|
||||||
},
|
|
||||||
SecurityMedium: '0000',
|
|
||||||
},
|
|
||||||
mutable: {},
|
|
||||||
},
|
|
||||||
|
|
||||||
body: {
|
|
||||||
DataTransfer: {
|
|
||||||
OrderData: Buffer.from(zlib.deflateSync(this.orderData())).toString('base64'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
root() { // eslint-disable-line class-methods-use-this
|
|
||||||
return 'ebicsUnsecuredRequest';
|
|
||||||
}
|
|
||||||
|
|
||||||
orderData() {
|
|
||||||
const xmlOrderData = {
|
|
||||||
'@': {
|
|
||||||
'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
|
|
||||||
xmlns: 'urn:org:ebics:H004',
|
|
||||||
},
|
|
||||||
AuthenticationPubKeyInfo: {
|
|
||||||
PubKeyValue: {
|
|
||||||
'ds:RSAKeyValue': {
|
|
||||||
'ds:Modulus': Buffer.from(this.client.x().n(), 'HEX').toString('base64'),
|
|
||||||
'ds:Exponent': 'AQAB',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
AuthenticationVersion: 'X002',
|
|
||||||
},
|
|
||||||
EncryptionPubKeyInfo: {
|
|
||||||
PubKeyValue: {
|
|
||||||
'ds:RSAKeyValue': {
|
|
||||||
'ds:Modulus': Buffer.from(this.client.e().n(), 'HEX').toString('base64'),
|
|
||||||
'ds:Exponent': 'AQAB',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
EncryptionVersion: 'E002',
|
|
||||||
},
|
|
||||||
PartnerID: this.partnerId,
|
|
||||||
UserID: this.userId,
|
|
||||||
};
|
|
||||||
|
|
||||||
return js2xmlparser.parse('HIARequestOrderData', xmlOrderData, this.xmlOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
toXML() {
|
|
||||||
return js2xmlparser.parse(this.root(), this._schema, this.xmlOptions);
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,43 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const GenericOrder = require('./GenericOrder');
|
|
||||||
|
|
||||||
module.exports = class HKD extends GenericOrder {
|
|
||||||
constructor(client) {
|
|
||||||
super(client);
|
|
||||||
|
|
||||||
this._schema.header = {
|
|
||||||
'@': { authenticate: true },
|
|
||||||
static: {
|
|
||||||
HostID: this.hostId,
|
|
||||||
Nonce: this.nonce(),
|
|
||||||
Timestamp: this.timestamp(),
|
|
||||||
PartnerID: this.partnerId,
|
|
||||||
UserID: this.userId,
|
|
||||||
Product: {
|
|
||||||
'@': { Language: 'de' },
|
|
||||||
'#': this.productString,
|
|
||||||
},
|
|
||||||
OrderDetails: {
|
|
||||||
OrderType: 'HKD',
|
|
||||||
OrderAttribute: 'DZHNN',
|
|
||||||
StandardOrderParams: '',
|
|
||||||
},
|
|
||||||
BankPubKeyDigests: {
|
|
||||||
Authentication: {
|
|
||||||
'@': { Version: 'X002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
|
|
||||||
'#': this.client.bankX().publicDigest(),
|
|
||||||
},
|
|
||||||
Encryption: {
|
|
||||||
'@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
|
|
||||||
'#': this.client.bankE().publicDigest(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SecurityMedium: '0000',
|
|
||||||
},
|
|
||||||
mutable: {
|
|
||||||
TransactionPhase: 'Initialisation',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,34 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const GenericOrder = require('./GenericOrder');
|
|
||||||
|
|
||||||
module.exports = class HPB extends GenericOrder {
|
|
||||||
constructor(client) {
|
|
||||||
super(client);
|
|
||||||
|
|
||||||
this._schema.header = {
|
|
||||||
'@': { authenticate: true },
|
|
||||||
static: {
|
|
||||||
HostID: this.hostId,
|
|
||||||
Nonce: this.nonce(),
|
|
||||||
Timestamp: this.timestamp(),
|
|
||||||
PartnerID: this.partnerId,
|
|
||||||
UserID: this.userId,
|
|
||||||
Product: {
|
|
||||||
'@': { Language: 'de' },
|
|
||||||
'#': this.productString,
|
|
||||||
},
|
|
||||||
OrderDetails: {
|
|
||||||
OrderType: 'HPB',
|
|
||||||
OrderAttribute: 'DZHNN',
|
|
||||||
},
|
|
||||||
SecurityMedium: '0000',
|
|
||||||
},
|
|
||||||
mutable: {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
root() { // eslint-disable-line class-methods-use-this
|
|
||||||
return 'ebicsNoPubKeyDigestsRequest';
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,43 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const GenericOrder = require('./GenericOrder');
|
|
||||||
|
|
||||||
module.exports = class HTD extends GenericOrder {
|
|
||||||
constructor(client) {
|
|
||||||
super(client);
|
|
||||||
|
|
||||||
this._schema.header = {
|
|
||||||
'@': { authenticate: true },
|
|
||||||
static: {
|
|
||||||
HostID: this.hostId,
|
|
||||||
Nonce: this.nonce(),
|
|
||||||
Timestamp: this.timestamp(),
|
|
||||||
PartnerID: this.partnerId,
|
|
||||||
UserID: this.userId,
|
|
||||||
Product: {
|
|
||||||
'@': { Language: 'de' },
|
|
||||||
'#': this.productString,
|
|
||||||
},
|
|
||||||
OrderDetails: {
|
|
||||||
OrderType: 'HTD',
|
|
||||||
OrderAttribute: 'DZHNN',
|
|
||||||
StandardOrderParams: '',
|
|
||||||
},
|
|
||||||
BankPubKeyDigests: {
|
|
||||||
Authentication: {
|
|
||||||
'@': { Version: 'X002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
|
|
||||||
'#': this.client.bankX().publicDigest(),
|
|
||||||
},
|
|
||||||
Encryption: {
|
|
||||||
'@': { Version: 'E002', Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256' },
|
|
||||||
'#': this.client.bankE().publicDigest(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SecurityMedium: '0000',
|
|
||||||
},
|
|
||||||
mutable: {
|
|
||||||
TransactionPhase: 'Initialisation',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,77 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const zlib = require('zlib');
|
|
||||||
const js2xmlparser = require('js2xmlparser');
|
|
||||||
|
|
||||||
const GenericOrder = require('./GenericOrder');
|
|
||||||
|
|
||||||
module.exports = class INI extends GenericOrder {
|
|
||||||
constructor(client) {
|
|
||||||
super(client);
|
|
||||||
|
|
||||||
this._schema = {
|
|
||||||
'@': {
|
|
||||||
'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
|
|
||||||
xmlns: 'urn:org:ebics:H004',
|
|
||||||
Version: 'H004',
|
|
||||||
Revision: '1',
|
|
||||||
},
|
|
||||||
|
|
||||||
header: {
|
|
||||||
'@': { authenticate: true },
|
|
||||||
static: {
|
|
||||||
HostID: this.hostId,
|
|
||||||
PartnerID: this.partnerId,
|
|
||||||
UserID: this.userId,
|
|
||||||
Product: {
|
|
||||||
'@': { Language: 'de' },
|
|
||||||
'#': this.productString,
|
|
||||||
},
|
|
||||||
OrderDetails: {
|
|
||||||
OrderType: 'INI',
|
|
||||||
OrderAttribute: 'DZNNN',
|
|
||||||
},
|
|
||||||
SecurityMedium: '0000',
|
|
||||||
},
|
|
||||||
mutable: {},
|
|
||||||
},
|
|
||||||
|
|
||||||
body: {
|
|
||||||
DataTransfer: {
|
|
||||||
OrderData: Buffer.from(zlib.deflateSync(this.keySignature())).toString('base64'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
root() { // eslint-disable-line class-methods-use-this
|
|
||||||
return 'ebicsUnsecuredRequest';
|
|
||||||
}
|
|
||||||
|
|
||||||
keySignature() {
|
|
||||||
const xmlOrderData = {
|
|
||||||
'@': {
|
|
||||||
'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
|
|
||||||
xmlns: 'http://www.ebics.org/S001',
|
|
||||||
},
|
|
||||||
SignaturePubKeyInfo: {
|
|
||||||
PubKeyValue: {
|
|
||||||
'ds:RSAKeyValue': {
|
|
||||||
'ds:Modulus': Buffer.from(this.client.a().n(), 'HEX').toString('base64'),
|
|
||||||
'ds:Exponent': 'AQAB',
|
|
||||||
},
|
|
||||||
TimeStamp: this.timestamp(),
|
|
||||||
},
|
|
||||||
SignatureVersion: 'A006',
|
|
||||||
},
|
|
||||||
PartnerID: this.partnerId,
|
|
||||||
UserID: this.userId,
|
|
||||||
};
|
|
||||||
|
|
||||||
return js2xmlparser.parse('SignaturePubKeyOrderData', xmlOrderData, this.xmlOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
toXML() {
|
|
||||||
return js2xmlparser.parse(this.root(), this._schema, this.xmlOptions);
|
|
||||||
}
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user