u
This commit is contained in:
@@ -8,11 +8,6 @@ PAIRING_CODE=307018
|
||||
LOG_FILE=logs/requests.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_ID=1
|
||||
MANDANT_NAME=eB-Standard
|
||||
|
||||
@@ -34,6 +34,5 @@ const serial = execSync(`openssl x509 -in "${certPath}" -noout -serial`, {
|
||||
|
||||
logger.success(`Wrote ${keyPath}`);
|
||||
logger.success(`Wrote ${certPath}`);
|
||||
logger.info(`CERTIFICATE_FINGERPRINT=${sha1.replace(/:/g, '')}`);
|
||||
logger.info(`CERTIFICATE_SERIAL_NUMBER=${serial}`);
|
||||
logger.info(`SERVER_FINGERPRINT=${sha1.replace(/:/g, '-')}`);
|
||||
logger.info(`Fingerprint: ${sha1.replace(/:/g, '')}`);
|
||||
logger.info(`Serial: ${serial}`);
|
||||
|
||||
@@ -3,8 +3,13 @@
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <cctype>
|
||||
|
||||
#include <uv.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/bn.h>
|
||||
|
||||
#include "config.hpp"
|
||||
#include "log.hpp"
|
||||
@@ -29,12 +34,60 @@ static Router router;
|
||||
static PairingStore pairing_store;
|
||||
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 {
|
||||
{"authToken", config::get("AUTH_TOKEN", "df40ad2067954646abb0499548a52241")},
|
||||
{"certificateFingerprint", config::get("CERTIFICATE_FINGERPRINT", "BC2114CF407A42724BEEF417960F76DCBF9DE879")},
|
||||
{"certificateSerialNumber", config::get("CERTIFICATE_SERIAL_NUMBER", "00BFC8BEACDB981B165210EF111CB9D3")},
|
||||
{"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")},
|
||||
{"certificateFingerprint", fingerprint},
|
||||
{"certificateSerialNumber", serial},
|
||||
{"serverFingerprint", server_fingerprint},
|
||||
{"mandantId", config::get("MANDANT_ID", "1")},
|
||||
{"mandantName", config::get("MANDANT_NAME", "eB-Standard")},
|
||||
{"mandantDatabase", config::get("MANDANT_DATABASE", "eazybusiness")},
|
||||
@@ -125,7 +178,7 @@ int main(int /*argc*/, char* argv[]) {
|
||||
|
||||
loop = uv_default_loop();
|
||||
|
||||
server_config = build_config();
|
||||
server_config = build_config(cert_path.c_str());
|
||||
|
||||
// Register routes
|
||||
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 { isDemoMode } from './src/demo/mode.js';
|
||||
import { loadDemoCatalog } from './src/demo/store.js';
|
||||
import { readCertMetadata } from './src/cert-meta.js';
|
||||
import { createJtlPosServer } from './src/jtl-server.js';
|
||||
import { createPairingStore } from './src/pairing.js';
|
||||
import { closeOrderLog } from './src/order-log.js';
|
||||
@@ -59,11 +60,18 @@ function formatBody(buffer) {
|
||||
return `[binary ${buffer.length} bytes]`;
|
||||
}
|
||||
|
||||
const certPem = fs.readFileSync(certPath);
|
||||
const keyPem = fs.readFileSync(keyPath);
|
||||
const certMeta = readCertMetadata(certPem);
|
||||
|
||||
const pairingStore = createPairingStore();
|
||||
pairingStore.setPairingCode(PAIRING_CODE, '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 started = Date.now();
|
||||
@@ -108,8 +116,8 @@ const loggedJtlHandler = async (req, res) => {
|
||||
|
||||
const httpsServer = https.createServer(
|
||||
{
|
||||
key: fs.readFileSync(keyPath),
|
||||
cert: fs.readFileSync(certPath),
|
||||
key: keyPem,
|
||||
cert: certPem,
|
||||
},
|
||||
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 = {}) {
|
||||
return {
|
||||
authToken: config.authToken || process.env.AUTH_TOKEN || 'df40ad2067954646abb0499548a52241',
|
||||
certificateFingerprint:
|
||||
config.certificateFingerprint ||
|
||||
process.env.CERTIFICATE_FINGERPRINT ||
|
||||
'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',
|
||||
certificateFingerprint: config.certificateFingerprint || '',
|
||||
certificateSerialNumber: config.certificateSerialNumber || '',
|
||||
serverFingerprint: config.serverFingerprint || '',
|
||||
mandantId: config.mandantId || process.env.MANDANT_ID || '1',
|
||||
mandantName: config.mandantName || process.env.MANDANT_NAME || 'eB-Standard',
|
||||
mandantDatabase: config.mandantDatabase || process.env.MANDANT_DATABASE || 'eazybusiness',
|
||||
|
||||
Reference in New Issue
Block a user