Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
'use strict';

const Client = require('./lib/Client');
const Orders = require('./lib/predefinedOrders');
const OrdersH004 = require('./lib/predefinedOrders/H004');
const OrdersH005 = require('./lib/predefinedOrders/H005');
const fsKeysStorage = require('./lib/storages/fsKeysStorage');
const tracesStorage = require('./lib/storages/tracesStorage');
const BankLetter = require('./lib/BankLetter');

module.exports = {
Client,
Orders,
/** @deprecated Use OrdersH004 or OrdersH005 instead */
Orders: OrdersH004,
OrdersH004,
OrdersH005,
BankLetter,
fsKeysStorage,
tracesStorage,
Expand Down
47 changes: 39 additions & 8 deletions lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ module.exports = class Client {

return {
orderData: res.orderData(),
transactionId: res.transactionId(),
orderId: res.orderId(),

technicalCode: returnedTechnicalCode,
Expand Down Expand Up @@ -199,37 +200,67 @@ module.exports = class Client {
this.tracesStorage.connect().ofType('TRANSFER.ORDER.UPLOAD');
res = await this.ebicsRequest(order);

return [transactionId, orderId];
const returnedTechnicalCode = res.technicalCode();
const returnedBusinessCode = res.businessCode();

return {
transactionId,
orderId,

technicalCode: returnedTechnicalCode,
technicalCodeSymbol: res.technicalSymbol(),
technicalCodeShortText: res.technicalShortText(
returnedTechnicalCode,
),
technicalCodeMeaning: res.technicalMeaning(returnedTechnicalCode),

businessCode: returnedBusinessCode,
businessCodeSymbol: res.businessSymbol(returnedBusinessCode),
businessCodeShortText: res.businessShortText(returnedBusinessCode),
businessCodeMeaning: res.businessMeaning(returnedBusinessCode),

// for backwards compatibility with the earlier return value [transactionId, orderId]:
0: transactionId,
1: orderId,
[Symbol.iterator]: function* iterator() {
yield transactionId;
yield orderId;
},
};
}

ebicsRequest(order) {
return new Promise(async (resolve, reject) => {
const { version } = order;
const keys = await this.keys();
const r = signer
const unsignedXml = (await serializer.use(order, this)).toXML();
const signedXml = signer
.version(version)
.sign((await serializer.use(order, this)).toXML(), keys.x());
.sign(unsignedXml, keys.x());

if (this.tracesStorage)
this.tracesStorage
.label(`REQUEST.${order.orderDetails.OrderType}`)
.data(r)
.label(`REQUEST.${order.orderDetails.AdminOrderType || order.orderDetails.OrderType}`)
.data(signedXml)
.persist();

rock({
method: 'POST',
url: this.url,
body: r,
body: signedXml,
headers: { 'content-type': 'text/xml;charset=UTF-8' },
},
(err, res, data) => {
if (err) reject(err);
if (err) {
reject(err);
return;
}

const ebicsResponse = response.version(version)(data.toString('utf-8'), keys);

if (this.tracesStorage)
this.tracesStorage
.label(`RESPONSE.${order.orderDetails.OrderType}`)
.label(`RESPONSE.${order.orderDetails.AdminOrderType || order.orderDetails.OrderType}`)
.connect()
.data(ebicsResponse.toXML())
.persist();
Expand Down
139 changes: 92 additions & 47 deletions lib/keymanagers/Key.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,51 @@ const {
privateKeyToPem,
publicKeyFromPem,
privateKeyFromPem,
setRsaPublicKey,
createCertificate,
certificateToPem,
certificateFromPem,
},
md: {
sha256,
},
jsbn: {
BigInteger,
},
} = require('node-forge');
const { X509Certificate } = require('crypto');

const getKeyType = (str) => {
const matches = str.match(/(PRIVATE|PUBLIC) KEY/);
const matches = str.match(/BEGIN (?:RSA )?(PRIVATE|PUBLIC|CERTIFICATE)/);
if (!matches)
return null;
return matches[1].toLowerCase();
};

const keyFromPem = (pem) => {
const type = getKeyType(pem);
const isPublic = type === 'public';
const key = isPublic ? publicKeyFromPem(pem) : privateKeyFromPem(pem);

return {
isPublic,
key,
};
};

/**
* Creates a public key from modulus and exponent
* @param {Buffer} mod - the modulus
* @param {Buffer} exp - the exponent
*/
const keyFromModAndExp = (mod, exp) => {
const bnMod = new BigInteger(mod.toString('hex'), 16);
const bnExp = new BigInteger(exp.toString('hex'), 16);

return {
key: rsa.setPublicKey(bnMod, bnExp),
isPublic: true,
};
const certificateFromPrivateKey = (privateKey) => {
const certificate = createCertificate();
certificate.publicKey = setRsaPublicKey(privateKey.n, privateKey.e);
certificate.validity.notBefore = new Date('2000-01-01');
certificate.validity.notAfter = new Date('9999-12-31');
certificate.setIssuer([
{
shortName: 'CN', value: 'ebics.example.com',
},
]);
certificate.subject = certificate.issuer;
certificate.setExtensions([
{
name: 'keyUsage',
keyCertSign: true,
digitalSignature: true,
nonRepudiation: true,
keyEncipherment: true,
dataEncipherment: true,
},
]);
certificate.sign(privateKey, sha256.create());

return certificate;
};

module.exports = class Key {
Expand All @@ -54,39 +62,68 @@ module.exports = class Key {
if (!pem && !mod && !exp) {
const keyPair = rsa.generateKeyPair(size);

this.keyIsPublic = false;
this.type = 'private';
this.privateKey = keyPair.privateKey;
this.publicKey = keyPair.publicKey;
this.certificate = certificateFromPrivateKey(keyPair.privateKey);

return;
}

// new key from pem string
if (pem) {
const { key, isPublic } = keyFromPem(pem);

this.keyIsPublic = isPublic;
this.privateKey = isPublic ? null : key;
this.publicKey = isPublic ? key : null;

this.readFromPem(pem);
return;
}

// new key from mod and exp
if (mod && exp) {
const { key, isPublic } = keyFromModAndExp(mod, exp);

this.keyIsPublic = isPublic;
this.privateKey = isPublic ? null : key;
this.publicKey = isPublic ? key : null;

this.readFromModAndExp(mod, exp); // only used for H004
return;
}

// not good
throw new Error(`Can not create key without ${!mod ? 'modulus' : 'exponent'}.`);
}

readFromPem(pem) {
this.type = getKeyType(pem);
switch (this.type) {
case 'public':
this.publicKey = publicKeyFromPem(pem);
this.privateKey = null;
this.certificate = null;
break;
case 'private':
this.privateKey = privateKeyFromPem(pem);
this.publicKey = setRsaPublicKey(this.privateKey.n, this.privateKey.e);
this.certificate = certificateFromPrivateKey(this.privateKey);
break;
case 'certificate':
this.privateKey = null;
this.certificate = certificateFromPem(pem);
this.publicKey = this.certificate.publicKey;
break;
default:
throw new Error(`Unknown key type: ${this.type}`);
}
}

/**
* Creates a public key from modulus and exponent
* @param {Buffer} mod - the modulus
* @param {Buffer} exp - the exponent
*/
readFromModAndExp(mod, exp) {
const bnMod = new BigInteger(mod.toString('hex'), 16);
const bnExp = new BigInteger(exp.toString('hex'), 16);

this.type = 'public';
this.publicKey = rsa.setPublicKey(bnMod, bnExp);
this.privateKey = null;
this.certificate = null;
}

static generate(size = 2048) {
return new Key({ size });
}
Expand All @@ -96,32 +133,31 @@ module.exports = class Key {
}

n(to = 'buff') {
const key = this.keyIsPublic ? this.publicKey : this.privateKey;
const key = this.privateKey || this.publicKey;
const keyN = Buffer.from(key.n.toByteArray());

return to === 'hex' ? keyN.toString('hex', 1) : keyN;
}

e(to = 'buff') {
const key = this.keyIsPublic ? this.publicKey : this.privateKey;
const key = this.privateKey || this.publicKey;
const eKey = Buffer.from(key.e.toByteArray());

return to === 'hex' ? eKey.toString('hex') : eKey;
}

d() {
if (this.keyIsPublic)
if (!this.privateKey)
throw new Error('Can not get d component out of public key.');

return Buffer.from(this.privateKey.d.toByteArray());
}

isPrivate() {
return !this.keyIsPublic;
}
certificateBase64() {
if (!this.certificate)
throw new Error('Certificate is not available.');

isPublic() {
return this.keyIsPublic;
return new X509Certificate(certificateToPem(this.certificate)).raw.toString('base64');
}

// eslint-disable-next-line class-methods-use-this
Expand All @@ -133,6 +169,15 @@ module.exports = class Key {
}

toPem() {
return this.keyIsPublic ? publicKeyToPem(this.publicKey) : privateKeyToPem(this.privateKey);
if (this.privateKey)
return privateKeyToPem(this.privateKey);

if (this.certificate)
return certificateToPem(this.certificate);

if (this.publicKey)
return publicKeyToPem(this.publicKey);

throw new Error('No key found');
}
};
2 changes: 1 addition & 1 deletion lib/keymanagers/defaultKeyEncryptor.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ const { encrypt, decrypt } = require('../crypto/encryptDecrypt');

module.exports = ({ passphrase, iv, algorithm = 'aes-256-cbc' }) => ({
encrypt: data => encrypt(data, algorithm, passphrase, iv),
decrypt: data => decrypt(data, algorithm, passphrase),
decrypt: data => decrypt(data, algorithm, passphrase, iv),
});
4 changes: 3 additions & 1 deletion lib/middleware/response.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
'use strict';

const H004Response = require('../orders/H004/response');
const H005Response = require('../orders/H005/response');

module.exports = {
version(v) {
if (v.toUpperCase() === 'H004') return H004Response;

if (v.toUpperCase() === 'H005') return H005Response;

throw Error('Error from middleware/response.js: Invalid version number');
},
};
2 changes: 2 additions & 0 deletions lib/middleware/serializer.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
'use strict';

const H004Serializer = require('../orders/H004/serializer');
const H005Serializer = require('../orders/H005/serializer');

module.exports = {
use(order, client) {
const { version } = order;

if (version.toUpperCase() === 'H004') return H004Serializer.use(order, client);
if (version.toUpperCase() === 'H005') return H005Serializer.use(order, client);

throw Error('Error middleware/serializer.js: Invalid version number');
},
Expand Down
2 changes: 2 additions & 0 deletions lib/middleware/signer.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
'use strict';

const H004Signer = require('../orders/H004/signer');
const H005Signer = require('../orders/H005/signer');

module.exports = {
version(v) {
if (v.toUpperCase() === 'H004') return H004Signer;
if (v.toUpperCase() === 'H005') return H005Signer;

throw Error('Error from middleware/signer.js: Invalid version number');
},
Expand Down
Loading