From 23916cbb98e952aab752a647ac96020aab709bb6 Mon Sep 17 00:00:00 2001 From: Thomas Vanbesien Date: Wed, 18 Feb 2026 12:03:16 +0100 Subject: Add direct connect, auto-detect PKI, and rework demo flow - connectDirect(policy, mode): connect without endpoint discovery, for servers with no unencrypted endpoint. Sets user identity token policy matching the auth mode. - autoDetectPki(): scan own/certs/*.der and own/private/*.pem|crt, called automatically at startup. - Demo: discovery auto-starts, PKI section hidden behind toggle with auto-detected cert summary, direct connect appears on connect failure. File/folder dialogs for manual PKI override. --- src/BobinkClient.cpp | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/BobinkClient.h | 20 +++++++++++++ 2 files changed, 99 insertions(+) (limited to 'src') diff --git a/src/BobinkClient.cpp b/src/BobinkClient.cpp index a067489..565868c 100644 --- a/src/BobinkClient.cpp +++ b/src/BobinkClient.cpp @@ -6,6 +6,7 @@ #include "BobinkAuth.h" #include +#include #include BobinkClient *BobinkClient::s_instance = nullptr; @@ -33,6 +34,8 @@ BobinkClient::BobinkClient(QObject *parent) { ensurePkiDirs(m_pkiDir); setupClient(); + autoDetectPki(); + applyPki(); connect(&m_discoveryTimer, &QTimer::timeout, this, &BobinkClient::doDiscovery); } @@ -129,6 +132,65 @@ void BobinkClient::connectToServer() m_client->requestEndpoints(url); } +static QString securityPolicyUri(BobinkClient::SecurityPolicy policy) +{ + switch (policy) { + case BobinkClient::Basic256Sha256: + return QStringLiteral( + "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256"); + case BobinkClient::Aes128_Sha256_RsaOaep: + return QStringLiteral( + "http://opcfoundation.org/UA/SecurityPolicy#Aes128_Sha256_RsaOaep"); + case BobinkClient::Aes256_Sha256_RsaPss: + return QStringLiteral( + "http://opcfoundation.org/UA/SecurityPolicy#Aes256_Sha256_RsaPss"); + } + return {}; +} + +void BobinkClient::connectDirect(SecurityPolicy policy, SecurityMode mode) +{ + if (!m_client) { + emit connectionError(QStringLiteral("OPC UA backend not available")); + return; + } + if (m_serverUrl.isEmpty()) { + emit connectionError(QStringLiteral("No server URL set")); + return; + } + if (m_client->state() != QOpcUaClient::Disconnected) { + emit connectionError(QStringLiteral("Already connected or connecting")); + return; + } + + QOpcUaEndpointDescription endpoint; + endpoint.setEndpointUrl(m_serverUrl); + endpoint.setSecurityPolicy(securityPolicyUri(policy)); + endpoint.setSecurityMode( + static_cast(mode)); + + QOpcUaUserTokenPolicy tokenPolicy; + if (m_auth) { + switch (m_auth->mode()) { + case BobinkAuth::Anonymous: + tokenPolicy.setTokenType(QOpcUaUserTokenPolicy::TokenType::Anonymous); + break; + case BobinkAuth::UserPass: + tokenPolicy.setTokenType(QOpcUaUserTokenPolicy::TokenType::Username); + break; + case BobinkAuth::Certificate: + tokenPolicy.setTokenType(QOpcUaUserTokenPolicy::TokenType::Certificate); + break; + } + m_client->setAuthenticationInformation(m_auth->toAuthenticationInformation()); + } else { + tokenPolicy.setTokenType(QOpcUaUserTokenPolicy::TokenType::Anonymous); + } + endpoint.setUserIdentityTokens({tokenPolicy}); + + m_client->connectToEndpoint(endpoint); +} + void BobinkClient::disconnectFromServer() { if (m_client) @@ -223,6 +285,23 @@ void BobinkClient::setKeyFile(const QString &path) emit keyFileChanged(); } +void BobinkClient::autoDetectPki() +{ + if (m_pkiDir.isEmpty()) + return; + + QDir certDir(m_pkiDir + QStringLiteral("/own/certs")); + QStringList certs = certDir.entryList({QStringLiteral("*.der")}, QDir::Files); + if (!certs.isEmpty()) + setCertFile(certDir.filePath(certs.first())); + + QDir keyDir(m_pkiDir + QStringLiteral("/own/private")); + QStringList keys = keyDir.entryList( + {QStringLiteral("*.pem"), QStringLiteral("*.crt")}, QDir::Files); + if (!keys.isEmpty()) + setKeyFile(keyDir.filePath(keys.first())); +} + void BobinkClient::applyPki() { if (!m_client || m_pkiDir.isEmpty()) diff --git a/src/BobinkClient.h b/src/BobinkClient.h index f95ab02..43eda65 100644 --- a/src/BobinkClient.h +++ b/src/BobinkClient.h @@ -88,8 +88,26 @@ public: QString keyFile () const; void setKeyFile (const QString &path); + enum SecurityMode + { + SignAndEncrypt = 3, + Sign = 2, + None = 1, + }; + Q_ENUM (SecurityMode) + + enum SecurityPolicy + { + Basic256Sha256, + Aes128_Sha256_RsaOaep, + Aes256_Sha256_RsaPss, + }; + Q_ENUM (SecurityPolicy) + /** @brief Discover endpoints, pick the most secure, connect. */ Q_INVOKABLE void connectToServer (); + /** @brief Connect directly without endpoint discovery. */ + Q_INVOKABLE void connectDirect (SecurityPolicy policy, SecurityMode mode); Q_INVOKABLE void disconnectFromServer (); /** @brief Accept the pending server certificate. */ @@ -100,6 +118,8 @@ public: Q_INVOKABLE void startDiscovery (); Q_INVOKABLE void stopDiscovery (); + /** @brief Auto-detect cert/key from the PKI directory and apply. */ + Q_INVOKABLE void autoDetectPki (); /** @brief Apply PKI dirs and cert/key. Call before connecting. */ Q_INVOKABLE void applyPki (); -- cgit v1.2.3