u
This commit is contained in:
@@ -8,11 +8,6 @@ PAIRING_CODE=307018
|
|||||||
LOG_FILE=logs/requests.log
|
LOG_FILE=logs/requests.log
|
||||||
ORDER_LOG_FILE=logs/orders.log
|
ORDER_LOG_FILE=logs/orders.log
|
||||||
|
|
||||||
# TLS certificate metadata returned during pairing
|
|
||||||
CERTIFICATE_FINGERPRINT=BC2114CF407A42724BEEF417960F76DCBF9DE879
|
|
||||||
CERTIFICATE_SERIAL_NUMBER=00BFC8BEACDB981B165210EF111CB9D3
|
|
||||||
SERVER_FINGERPRINT=39-6D-BD-DE-F3-5C-5A-EA-C2-19-CF-EB-A7-A9-58-2F-20-3F-20-F7-3D-E6-CA-8E-AE-FD-28-30-37-A6-45-AE
|
|
||||||
|
|
||||||
# Mandant
|
# Mandant
|
||||||
MANDANT_ID=1
|
MANDANT_ID=1
|
||||||
MANDANT_NAME=eB-Standard
|
MANDANT_NAME=eB-Standard
|
||||||
|
|||||||
@@ -34,6 +34,5 @@ const serial = execSync(`openssl x509 -in "${certPath}" -noout -serial`, {
|
|||||||
|
|
||||||
logger.success(`Wrote ${keyPath}`);
|
logger.success(`Wrote ${keyPath}`);
|
||||||
logger.success(`Wrote ${certPath}`);
|
logger.success(`Wrote ${certPath}`);
|
||||||
logger.info(`CERTIFICATE_FINGERPRINT=${sha1.replace(/:/g, '')}`);
|
logger.info(`Fingerprint: ${sha1.replace(/:/g, '')}`);
|
||||||
logger.info(`CERTIFICATE_SERIAL_NUMBER=${serial}`);
|
logger.info(`Serial: ${serial}`);
|
||||||
logger.info(`SERVER_FINGERPRINT=${sha1.replace(/:/g, '-')}`);
|
|
||||||
|
|||||||
@@ -3,8 +3,13 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
#include <uv.h>
|
#include <uv.h>
|
||||||
|
#include <openssl/pem.h>
|
||||||
|
#include <openssl/x509.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/bn.h>
|
||||||
|
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "log.hpp"
|
#include "log.hpp"
|
||||||
@@ -29,12 +34,60 @@ static Router router;
|
|||||||
static PairingStore pairing_store;
|
static PairingStore pairing_store;
|
||||||
static RequestLog request_log;
|
static RequestLog request_log;
|
||||||
|
|
||||||
static json build_config() {
|
static bool read_cert_metadata(const char* cert_path,
|
||||||
|
std::string& fingerprint,
|
||||||
|
std::string& serial,
|
||||||
|
std::string& server_fingerprint) {
|
||||||
|
FILE* fp = std::fopen(cert_path, "r");
|
||||||
|
if (!fp) return false;
|
||||||
|
X509* cert = PEM_read_X509(fp, nullptr, nullptr, nullptr);
|
||||||
|
std::fclose(fp);
|
||||||
|
if (!cert) return false;
|
||||||
|
|
||||||
|
unsigned char md[EVP_MAX_MD_SIZE];
|
||||||
|
unsigned int md_len = 0;
|
||||||
|
if (X509_digest(cert, EVP_sha1(), md, &md_len) != 1) {
|
||||||
|
X509_free(cert);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* hex = "0123456789ABCDEF";
|
||||||
|
fingerprint.clear();
|
||||||
|
fingerprint.reserve(md_len * 2);
|
||||||
|
server_fingerprint.clear();
|
||||||
|
server_fingerprint.reserve(md_len * 3 - 1);
|
||||||
|
for (unsigned int i = 0; i < md_len; ++i) {
|
||||||
|
fingerprint.push_back(hex[(md[i] >> 4) & 0xF]);
|
||||||
|
fingerprint.push_back(hex[md[i] & 0xF]);
|
||||||
|
if (i) server_fingerprint.push_back('-');
|
||||||
|
server_fingerprint.push_back(hex[(md[i] >> 4) & 0xF]);
|
||||||
|
server_fingerprint.push_back(hex[md[i] & 0xF]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ASN1_INTEGER* asn1_serial = X509_get0_serialNumber(cert);
|
||||||
|
BIGNUM* bn = ASN1_INTEGER_to_BN(asn1_serial, nullptr);
|
||||||
|
char* hex_serial = bn ? BN_bn2hex(bn) : nullptr;
|
||||||
|
bool ok = hex_serial != nullptr;
|
||||||
|
if (ok) {
|
||||||
|
serial = hex_serial;
|
||||||
|
for (char& c : serial) c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
|
||||||
|
OPENSSL_free(hex_serial);
|
||||||
|
}
|
||||||
|
BN_free(bn);
|
||||||
|
X509_free(cert);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static json build_config(const char* cert_path) {
|
||||||
|
std::string fingerprint, serial, server_fingerprint;
|
||||||
|
if (!read_cert_metadata(cert_path, fingerprint, serial, server_fingerprint)) {
|
||||||
|
logc::error("failed to read certificate metadata from %s", cert_path);
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
{"authToken", config::get("AUTH_TOKEN", "df40ad2067954646abb0499548a52241")},
|
{"authToken", config::get("AUTH_TOKEN", "df40ad2067954646abb0499548a52241")},
|
||||||
{"certificateFingerprint", config::get("CERTIFICATE_FINGERPRINT", "BC2114CF407A42724BEEF417960F76DCBF9DE879")},
|
{"certificateFingerprint", fingerprint},
|
||||||
{"certificateSerialNumber", config::get("CERTIFICATE_SERIAL_NUMBER", "00BFC8BEACDB981B165210EF111CB9D3")},
|
{"certificateSerialNumber", serial},
|
||||||
{"serverFingerprint", config::get("SERVER_FINGERPRINT", "39-6D-BD-DE-F3-5C-5A-EA-C2-19-CF-EB-A7-A9-58-2F-20-3F-20-F7-3D-E6-CA-8E-AE-FD-28-30-37-A6-45-AE")},
|
{"serverFingerprint", server_fingerprint},
|
||||||
{"mandantId", config::get("MANDANT_ID", "1")},
|
{"mandantId", config::get("MANDANT_ID", "1")},
|
||||||
{"mandantName", config::get("MANDANT_NAME", "eB-Standard")},
|
{"mandantName", config::get("MANDANT_NAME", "eB-Standard")},
|
||||||
{"mandantDatabase", config::get("MANDANT_DATABASE", "eazybusiness")},
|
{"mandantDatabase", config::get("MANDANT_DATABASE", "eazybusiness")},
|
||||||
@@ -125,7 +178,7 @@ int main(int /*argc*/, char* argv[]) {
|
|||||||
|
|
||||||
loop = uv_default_loop();
|
loop = uv_default_loop();
|
||||||
|
|
||||||
server_config = build_config();
|
server_config = build_config(cert_path.c_str());
|
||||||
|
|
||||||
// Register routes
|
// Register routes
|
||||||
router.add_route("GET", "/v1/client", handle_client);
|
router.add_route("GET", "/v1/client", handle_client);
|
||||||
|
|||||||
14
server.js
14
server.js
@@ -7,6 +7,7 @@ import 'dotenv/config';
|
|||||||
import { connectDb, closeDb } from './src/db.js';
|
import { connectDb, closeDb } from './src/db.js';
|
||||||
import { isDemoMode } from './src/demo/mode.js';
|
import { isDemoMode } from './src/demo/mode.js';
|
||||||
import { loadDemoCatalog } from './src/demo/store.js';
|
import { loadDemoCatalog } from './src/demo/store.js';
|
||||||
|
import { readCertMetadata } from './src/cert-meta.js';
|
||||||
import { createJtlPosServer } from './src/jtl-server.js';
|
import { createJtlPosServer } from './src/jtl-server.js';
|
||||||
import { createPairingStore } from './src/pairing.js';
|
import { createPairingStore } from './src/pairing.js';
|
||||||
import { closeOrderLog } from './src/order-log.js';
|
import { closeOrderLog } from './src/order-log.js';
|
||||||
@@ -59,11 +60,18 @@ function formatBody(buffer) {
|
|||||||
return `[binary ${buffer.length} bytes]`;
|
return `[binary ${buffer.length} bytes]`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const certPem = fs.readFileSync(certPath);
|
||||||
|
const keyPem = fs.readFileSync(keyPath);
|
||||||
|
const certMeta = readCertMetadata(certPem);
|
||||||
|
|
||||||
const pairingStore = createPairingStore();
|
const pairingStore = createPairingStore();
|
||||||
pairingStore.setPairingCode(PAIRING_CODE, 'JTL-POS');
|
pairingStore.setPairingCode(PAIRING_CODE, 'JTL-POS');
|
||||||
pairingStore.registerDevice(AUTH_TOKEN, 'JTL-POS');
|
pairingStore.registerDevice(AUTH_TOKEN, 'JTL-POS');
|
||||||
|
|
||||||
const jtlHandler = createJtlPosServer(pairingStore, { authToken: AUTH_TOKEN });
|
const jtlHandler = createJtlPosServer(pairingStore, {
|
||||||
|
authToken: AUTH_TOKEN,
|
||||||
|
...certMeta,
|
||||||
|
});
|
||||||
|
|
||||||
const loggedJtlHandler = async (req, res) => {
|
const loggedJtlHandler = async (req, res) => {
|
||||||
const started = Date.now();
|
const started = Date.now();
|
||||||
@@ -108,8 +116,8 @@ const loggedJtlHandler = async (req, res) => {
|
|||||||
|
|
||||||
const httpsServer = https.createServer(
|
const httpsServer = https.createServer(
|
||||||
{
|
{
|
||||||
key: fs.readFileSync(keyPath),
|
key: keyPem,
|
||||||
cert: fs.readFileSync(certPath),
|
cert: certPem,
|
||||||
},
|
},
|
||||||
loggedJtlHandler
|
loggedJtlHandler
|
||||||
);
|
);
|
||||||
|
|||||||
12
src/cert-meta.js
Normal file
12
src/cert-meta.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { X509Certificate } from 'node:crypto';
|
||||||
|
|
||||||
|
/** Derive pairing metadata from a PEM-encoded TLS certificate. */
|
||||||
|
export function readCertMetadata(certPem) {
|
||||||
|
const x509 = new X509Certificate(certPem);
|
||||||
|
const sha1 = x509.fingerprint; // colon-separated uppercase hex
|
||||||
|
return {
|
||||||
|
certificateFingerprint: sha1.replace(/:/g, ''),
|
||||||
|
certificateSerialNumber: x509.serialNumber,
|
||||||
|
serverFingerprint: sha1.replace(/:/g, '-'),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -4,18 +4,9 @@ import { normalizePath, readBody, sendJson } from './http.js';
|
|||||||
function buildConfig(config = {}) {
|
function buildConfig(config = {}) {
|
||||||
return {
|
return {
|
||||||
authToken: config.authToken || process.env.AUTH_TOKEN || 'df40ad2067954646abb0499548a52241',
|
authToken: config.authToken || process.env.AUTH_TOKEN || 'df40ad2067954646abb0499548a52241',
|
||||||
certificateFingerprint:
|
certificateFingerprint: config.certificateFingerprint || '',
|
||||||
config.certificateFingerprint ||
|
certificateSerialNumber: config.certificateSerialNumber || '',
|
||||||
process.env.CERTIFICATE_FINGERPRINT ||
|
serverFingerprint: config.serverFingerprint || '',
|
||||||
'BC2114CF407A42724BEEF417960F76DCBF9DE879',
|
|
||||||
certificateSerialNumber:
|
|
||||||
config.certificateSerialNumber ||
|
|
||||||
process.env.CERTIFICATE_SERIAL_NUMBER ||
|
|
||||||
'00BFC8BEACDB981B165210EF111CB9D3',
|
|
||||||
serverFingerprint:
|
|
||||||
config.serverFingerprint ||
|
|
||||||
process.env.SERVER_FINGERPRINT ||
|
|
||||||
'39-6D-BD-DE-F3-5C-5A-EA-C2-19-CF-EB-A7-A9-58-2F-20-3F-20-F7-3D-E6-CA-8E-AE-FD-28-30-37-A6-45-AE',
|
|
||||||
mandantId: config.mandantId || process.env.MANDANT_ID || '1',
|
mandantId: config.mandantId || process.env.MANDANT_ID || '1',
|
||||||
mandantName: config.mandantName || process.env.MANDANT_NAME || 'eB-Standard',
|
mandantName: config.mandantName || process.env.MANDANT_NAME || 'eB-Standard',
|
||||||
mandantDatabase: config.mandantDatabase || process.env.MANDANT_DATABASE || 'eazybusiness',
|
mandantDatabase: config.mandantDatabase || process.env.MANDANT_DATABASE || 'eazybusiness',
|
||||||
|
|||||||
Reference in New Issue
Block a user