OPC UA Security Explained: Certificates, GDS, Security Policies and Hardening (2026)

OPC UA Security Explained: Certificates, GDS, Security Policies and Hardening (2026)

OPC UA Security Explained: Certificates, GDS, Security Policies and Hardening (2026)

Most plants that “enabled OPC UA security” have done one thing: they picked Basic256Sha256 in a dropdown and clicked “trust” on a certificate dialog the first time a client connected. Two years later nobody knows which certificates sit in which trust list, the self-signed server certificate expired on a Sunday, and the fix was to switch the endpoint back to None. The protocol was never the weak part. The operational model around OPC UA security was.

This matters more in 2026 than it did when OPC UA was a point-to-point link between a SCADA and a PLC. Servers now sit on edge gateways, in machine builders’ products, and behind cloud connectors, and regulation such as the EU Cyber Resilience Act and NIS2 is turning “we have a certificate” into an auditable obligation. A certificate you cannot rotate is a liability, not a control.

You will leave with a precise mental model of how OPC UA authenticates applications and users, what each Security Policy actually does, how trust lists and the Global Discovery Server (GDS) fit together, how PubSub security differs, and a hardening checklist with a runnable client snippet.

What this covers: the layered security model, application instance certificates, security policies and message modes, user identity tokens, trust lists and GDS push/pull, PubSub key distribution, a mapping to IEC 62443 (my analysis, not a standard), failure modes, and a plant checklist.

Context and Background

OPC UA (IEC 62541) is a multi-part specification. Security touches four parts you should know by number. Part 2 is the security model: objectives, threats, and the reasoning behind the design. Part 4 defines the services, including OpenSecureChannel, CreateSession and ActivateSession, and the user identity token types. Part 6 maps those services to concrete encodings and defines the security-related mechanics of the binary protocol, plus the certificate profile. Part 7 lists the Profiles and Security Policies. Part 12 covers Discovery and Global Services, which includes the GDS and certificate management. Part 14 covers PubSub, including its security key service.

The current published versions on the OPC Foundation reference site are in the 1.05 family; the Part 2 page shows release 1.05.06 published 2025-10-22, replacing 1.04 from 2018. That is the Foundation’s own listing, and the Foundation notes its PDF is authoritative if the web rendering differs.

The design philosophy matters because it differs from most industrial protocols. OPC UA does not bolt TLS under an existing protocol. It defines its own application-layer secure channel, with its own handshake, key derivation, and certificate checks, and then a separate session layer on top for user identity. The result is that the transport can be raw TCP (opc.tcp), HTTPS, or WebSockets, while the security semantics stay the same. Two identities exist in every connection: the application (proven by an application instance certificate) and the user (proven by a user identity token). Conflating the two is the source of half of all misconfigurations.

Contrast that with Sparkplug over MQTT, where security is inherited from the broker and TLS, and the payload carries no application identity. If you are comparing the two for a plant, our piece on Sparkplug B versus plain MQTT topics covers the topic-model side. For the regulatory framing, see EU CRA versus NIS2 versus IEC 62443.

The public record of the spec is worth reading directly. The OPC Foundation hosts the online reference for Part 6 and the other parts, with downloadable Markdown versions. Everything stated about certificate fields and token structures below was checked against those pages. Where I describe behaviour that I could not confirm against the text this run, I say so.

Threats the model is built to stop

Part 2 frames the threats in classic terms: eavesdropping on process data, message tampering, replay, spoofing of a server or a client, denial of service, and privilege escalation by an authenticated but unauthorized user. Each mechanism below maps to one or more of those. Signing gives integrity and, together with sequence numbers and nonces, replay resistance. Encryption gives confidentiality. Certificates give application authentication. User tokens plus the server’s authorization model give user-level access control. Notice that nothing here prevents denial of service by a flood of unauthenticated OpenSecureChannel requests; that is a deployment concern we return to in the gotchas section.

The OPC UA Security Architecture: Three Layers and Two Identities

OPC UA security works in three stacked layers. A secure channel authenticates the two applications by certificate and protects every message with a Security Policy and a message mode. A session on top of that channel authenticates the user with an identity token. Finally, the address space enforces authorization on each node using roles and permissions. Trust is decided by lists of certificates, which a Global Discovery Server can distribute.

OPC UA security layers: application certificate, secure channel, session user token and trust lists

Figure 1: OPC UA security layers. The secure channel binds two application certificates; the session binds a user identity; trust lists and the GDS sit beside both.

The diagram reads from the bottom. Each application owns a key pair and an application instance certificate. Each side also holds a trust list (certificates or CAs it accepts) and usually an issuer list (intermediate CAs and revocation data). The client opens a secure channel, the server checks the client certificate against its trust list, and the client checks the server certificate the same way. Only then does the client create and activate a session and present a user token. Authorization happens last, per node, and is a server-side decision.

Layer 1: the secure channel

A client first calls GetEndpoints (discovery is allowed over the None policy, which is why None is often left enabled and why it is risky). Each returned EndpointDescription carries the server certificate, a Security Policy URI, a message security mode, and a list of user token policies. The client chooses one.

The OpenSecureChannel handshake then exchanges nonces. In the asymmetric phase, the request is signed with the sender’s private key and encrypted with the receiver’s public key, using the algorithms of the chosen Security Policy. Both sides then derive symmetric keys from the two nonces using a pseudo-random function (P_SHA256 in the SHA-256 family policies), producing separate signing keys and encryption keys for each direction. After that, ordinary service calls use symmetric signatures and, in SignAndEncrypt mode, symmetric encryption, which is far cheaper than public-key operations per message.

The secure channel carries a token with a lifetime. The client is expected to renew before it expires, and the spec’s guidance is to renew well before the end of the lifetime rather than at the edge. Renewal triggers fresh nonces and fresh symmetric keys, so a long-running connection does not use one key set for months. If a client fails to renew, the server closes the channel. This is a common cause of “connection drops every N minutes” tickets: an embedded client with a slow clock or a missing renewal timer.

Sequence numbers and request IDs inside each chunk defend against replay and reordering within a channel. Large messages are split into chunks, and each chunk is individually signed (and encrypted), so a tampered chunk is detected before reassembly.

Layer 2: the session and the user

After the channel is up, CreateSession returns a server nonce and the server certificate again. The client proves it owns its application certificate by signing the server’s nonce combined with the server certificate, and the server proves the same in the response. ActivateSession then carries the UserIdentityToken. This sequence is why a stolen user password alone does not let an attacker impersonate a client application: the attacker also needs a channel the server trusts, which requires an accepted certificate.

The binding goes both ways. The session is bound to the channel that created it. A session may be moved to another channel only with a valid re-activation that proves the identity again. This is a defence against session hijacking by a party who learns the session identifier, which is an unguessable but not secret value.

Layer 3: authorization

OPC UA 1.04 introduced role-based permissions: roles such as Operator, Engineer and SecurityAdmin are well-known identities mapped to users, groups, applications or endpoints, and each node carries RolePermissions. The server evaluates these on every read, write, call and browse. Part 2 and the permissions model are where “who may write this setpoint” is answered. Most real-world incidents I see on OPC UA servers are not cryptographic failures. They are authorization failures: every authenticated user mapped to an all-powerful role, or an anonymous token enabled on a production endpoint.

Application instance certificates in detail

An application instance certificate identifies one installed instance of an OPC UA application, not a person and not a machine in general. Part 6 section 6.2.2 defines its fields (Table 50 in the version I read), and a few are strict enough to cause real interoperability failures.

Field Requirement per Part 6 (as read this run)
subjectAltName Must contain a uniformResourceIdentifier equal to the applicationUri, exactly one URI. Servers must also include a dNSName (partial or fully qualified) or a static IP address.
applicationUri A valid URL (RFC 3986) or URN (RFC 8141); must match the URI in the SAN.
keyUsage (RSA) digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment; self-signed also adds keyCertSign.
keyUsage (ECC) digitalSignature (self-signed also keyCertSign).
extendedKeyUsage (RSA profiles) serverAuth for servers, clientAuth for clients; servers should also carry clientAuth.
extendedKeyUsage (ECC profiles) serverAuth and clientAuth are optional.
Validity validFrom and validTo; this section sets no duration.

The section I fetched does not specify key sizes or signature algorithms; those come from the Security Policy, discussed next. Two consequences follow.

First, the applicationUri is the identity. When a client connects, it compares the ApplicationUri the server reports in GetEndpoints and CreateSession with the URI in the server certificate’s SAN. A mismatch is a hard failure in compliant stacks, and it is the single most common reason a re-issued or cloned certificate “stops working”. Cloning a VM image with a baked-in certificate means two servers claim the same URI and the same key, which defeats the point of the identity entirely. Generate the key pair on first boot, or provision from a GDS.

Second, the DNS name or IP address matters for servers. The client is expected to verify that the host it dialed appears in the certificate. Plants with DHCP-assigned controller addresses and certificates pinned to an IP produce a steady stream of “hostname mismatch” overrides, and engineers learn to tick “ignore”. Use stable DNS names, or static addresses, as the spec text assumes.

Certificate lifetime is a policy choice you must make because the spec does not make it for you. A ten-year self-signed certificate on a PLC is a ten-year-old key you probably cannot rotate. A one-year certificate without automation is a yearly outage. The honest answer is short lifetimes plus automated renewal, which is the argument for the GDS.

Security Policies and message modes

A Security Policy is a named bundle of algorithms: asymmetric signature, asymmetric encryption, symmetric signature, symmetric encryption, key derivation, and minimum key lengths. The message security mode then decides how much of it you use: None, Sign, or SignAndEncrypt. An endpoint is the pair (policy, mode) plus its user token policies.

The following table summarizes the policies as listed in the open62541 1.5 documentation, which is a reputable implementation reference rather than the normative text. For URIs and normative status, consult Part 7.

Policy Algorithms (per open62541 docs) Status
None no signing or encryption Needed for discovery; avoid for data.
Basic128Rsa15 RSA-1024 class, AES-128 Legacy; the docs say “Deprecated, do not use for new systems.”
Basic256 RSA, AES-256 Replaced by Basic256Sha256.
Basic256Sha256 RSA-2048 class, SHA-256, AES-256 Current RSA baseline in many fielded products.
Aes128_Sha256_RsaOaep RSA-OAEP, AES-128, SHA-256 Modern RSA profile.
Aes256_Sha256_RsaPss RSA-PSS, AES-256, SHA-256 Described as the strongest RSA profile.
ECC_nistP256, ECC_nistP384 NIST curves ECC family.
ECC_brainpoolP256r1, ECC_brainpoolP384r1 Brainpool curves ECC family.
ECC_curve25519, ECC_curve448 EdDSA curves ECC; OpenSSL backend only in that stack.
PubSub-Aes128-CTR, PubSub-Aes256-CTR AES-CTR, symmetric only For PubSub message security.

The thing to take away is the direction of travel. SHA-1 and PKCS#1 v1.5 encryption padding, the basis of Basic128Rsa15 and Basic256, are retired. The OAEP and PSS policies move to padding schemes with better security proofs. The ECC policies, introduced in the 1.05 era, give smaller keys and faster handshakes, which matters on constrained controllers. I could not verify from the Foundation pages this run exactly which normative revision made each ECC policy final, nor how widely fielded devices support them; treat ECC as emerging in the field and check your vendor’s conformance statement before specifying it.

One compatibility trap deserves emphasis. Many older server stacks only offered Basic256Sha256 and some only Basic128Rsa15. Modern clients that default to Aes256_Sha256_RsaPss will fail endpoint selection silently if the server lacks it. Always read the endpoint list, choose deliberately, and publish the chosen pair in your integration documentation.

Sign versus SignAndEncrypt

Sign protects integrity and authenticity but leaves data readable. It is not a security mode for an untrusted network, though it is a legitimate choice inside a segmented cell when you need to inspect traffic with a protocol analyser and the threat model excludes eavesdroppers on that segment. SignAndEncrypt is the default for anything crossing a zone boundary. The CPU cost of symmetric AES on modern hardware is small; the cost of choosing Sign for performance reasons is almost always a premature optimisation, and on older ARM controllers without AES acceleration the penalty is real but bounded. Measure on your device rather than assuming.

Walk-Through: Handshake, User Tokens, Trust Lists

The connection sequence is a fixed choreography. Read it once end to end, because every real incident maps to one arrow in it.

OPC UA security handshake sequence from GetEndpoints to ActivateSession with certificate checks

Figure 2: The OPC UA connection sequence. Certificate validation happens at OpenSecureChannel on both sides; user authentication happens only at ActivateSession.

The sequence has two places where a client or server can reject the other: certificate validation during OpenSecureChannel, and token validation during ActivateSession. A typical failure message BadSecurityChecksFailed or BadCertificateUntrusted arrives in the first; BadIdentityTokenRejected or BadUserAccessDenied arrives in the second. Knowing which layer rejected you cuts diagnosis from hours to minutes.

User identity tokens

Part 4 defines the token types, and the server advertises which it accepts via UserTokenPolicy entries on each endpoint.

Token What it proves Where it goes wrong
Anonymous Nothing Left enabled on production endpoints; any trusted application gets in.
UserName Knowledge of a password Password sent in clear if the endpoint policy is None; shared accounts.
X509 Possession of a user key pair Reusing the application certificate as the user certificate; no revocation.
IssuedIdentityToken A token from an external authority Poor audience checks; clock skew; no expiry handling.

The UserName token carries the password encrypted with the server’s public key, using the algorithm named by the token policy’s own security policy. If the endpoint uses None and the token policy does not specify a policy, the password can travel unprotected. Well-behaved servers refuse to offer username tokens on unsecured endpoints; many implementations still let you configure it. That is a one-line audit item.

For IssuedIdentityToken, the 1.05 text I read is specific. It is the route to federated identity: a client obtains a token from an external Authorization Service and presents it to the server. Kerberos is deprecated as of 1.05, which leaves JWT (RFC 7519) and OAuth2 as the main options. A server that supports JWT advertises a UserTokenPolicy whose issuerEndpointUrl is a JSON object with resourceId, authorityUrl and authorityProfileUri as required members, plus tokenEndpoint, authorizationEndpoint, requestTypes and scopes as conditional or optional. The token must be signed by the issuer with asymmetric cryptography, so the server needs the Authorization Service’s certificate. Required claims are sub, aud (which must match the resourceId) and exp. Optional claims include iss, groups, roles, nbf and cnf; the last binds the token to a certificate thumbprint so the secure channel must use that exact certificate.

Expiry handling is the part teams forget. Per the text, the server should revoke the session’s privileges on expiry; if anonymous access is allowed, the session may continue as anonymous, otherwise the server should close it. Clients should obtain a fresh token and call ActivateSession again before the old one lapses. A client that ignores this works perfectly for the access-token lifetime, say an hour, then loses write access mid-shift. Test the expiry path in commissioning.

The cnf claim is the most interesting design choice for plants. It converts a bearer token, which anyone who steals it can replay, into a sender-constrained token tied to the application’s key. If your identity provider supports it, require it.

Trust lists, issuer lists, and what “trusted” means

Each application keeps a certificate store, conventionally divided into: trusted certificates (applications and CAs you accept), issuers (intermediate CAs and their CRLs for chain building), and rejected (certificates seen but not accepted, kept so an administrator can review and promote them). The trust list in the OPC UA sense is the content of trusted plus issuer material and its revocation data, and it is a first-class object in the information model with a defined binary form and methods to read and write it.

There are two trust models and you must choose one on purpose.

Direct trust (pinning). Every peer certificate is placed in the trusted store. Self-signed certificates work. It scales as n times m: for 40 servers and 15 clients, each server holds 15 client certificates and each client 40 server certificates, 1,200 trust entries (600 on each side) to manage by hand, and every renewal touches every peer. This is a worked count, not a benchmark. It is robust because there is no CA to compromise, and it is the pattern in small cells.

CA-based trust. Peers trust a CA, and each application certificate chains to it. Adding a server means issuing one certificate, and nobody else changes. Renewal is local. The cost is that you now run a PKI, with an offline root, an intermediate for devices, CRLs or OCSP-style revocation data distributed in the issuer lists, and a procedure for CA key replacement. The same 55 applications need 55 certificates and 2 CA entries in each trust list.

Whichever you choose, be careful with two behaviours. A stack that automatically accepts unknown certificates (“trust on first use”) has no authentication at all; it has only an audit log. And a stack that checks chains but not revocation will keep accepting a certificate you declared lost. Verify revocation behaviour in your stack by revoking a test certificate and trying to connect. Do not assume.

Global Discovery Server: Certificate Lifecycle at Scale

A GDS (Global Discovery Server, Part 12) is an OPC UA server that acts as a registry of applications and as a certificate and trust-list distribution point. It lets you issue short-lived certificates and rotate trust lists without walking to each machine. It does this in two models, push and pull, which differ in who initiates the call.

GDS push and pull certificate management flows between an administrator, the GDS and OPC UA applications

Figure 3: GDS pull (application calls the GDS) versus push (an admin tool calls the application’s ServerConfiguration object).

Pull management

In the pull model the application is a client of the GDS. Based on the Unified Automation SDK documentation of the flow, which tracks the Part 12 methods, the sequence is:

  1. The application calls FindApplications and, if it is not registered, RegisterApplication. In that SDK’s example an administrator must approve the registration in the GDS before certificates or trust lists are issued.
  2. It browses the GDS certificate groups and matches them to the groups it is configured for.
  3. Periodically it calls GetCertificateStatus to learn whether its certificate needs renewal. If so, it calls StartSigningRequest with a certificate signing request (or StartNewKeyPairRequest to have the GDS generate the key pair), then polls FinishRequest until the signed certificate is returned.
  4. It reads the trust list’s LastUpdateTime and, if changed, calls GetTrustList and reads the list through the trust list’s file-style methods.
  5. On decommission it calls UnregisterApplication.

Pull suits devices that can reach the GDS and can run a client: edge gateways, PCs, modern PLC runtimes. Its security rests on the application authenticating to the GDS over a secure channel, and on the GDS authorizing that application (via roles such as application administrator or the registered application’s own identity) to touch only its own registration.

Push management

In the push model, a management tool, usually a GDS admin client or an operator workstation, is a client of the application. The application exposes a ServerConfiguration object. The Part 12 method names visible on the reference site include UpdateCertificate, CreateSelfSignedCertificate, DeleteCertificate, ApplyChanges, CancelChanges and ResetToServerDefaults, alongside the pull methods named above. The reference page I fetched lists the names but not their semantics, so what follows about the workflow is the design as I understand it from Part 12 and vendor SDKs, not a quotation:

  • The admin tool reads the application’s trust list and certificate groups.
  • It requests a new certificate (the application can generate a signing request), has the CA sign it, then calls UpdateCertificate.
  • Trust-list changes are written through the trust list object and committed.
  • ApplyChanges triggers the application to start using the new material, which typically closes existing secure channels so that clients reconnect with the new certificate.

Push suits devices that run a server but have no practical way to act as a GDS client, which describes many fieldbus-attached controllers. The trade-off is that the admin tool needs network reach into each device and a role strong enough to rewrite the device’s trust. Treat the admin workstation as Tier 0 for the cell.

Choosing between them

Concern Pull Push
Who initiates Application Admin tool
Needs outbound path to GDS Yes No; needs inbound path to device
Device needs GDS client code Yes No
Renewal automation Natural: device polls status Scripted from the admin side
Firewall posture Device-initiated flows to one GDS Admin-initiated flows to many devices
Typical fit Gateways, PCs, HMIs Embedded servers, legacy controllers

In mixed plants you use both, with one CA behind them. The GDS in that arrangement is the authoritative inventory: it knows each application’s URI, its certificate group, and expiry. Even if you never automate renewals, that inventory pays for itself the first time an auditor asks “which applications trust this CA”.

A worked rotation timeline

Assume 60 servers with 90-day certificates and a 30-day renewal window (illustrative policy numbers, not a recommendation from the spec). Each application checks certificate status daily. At day 60 the status call reports renewal due, the app submits a signing request, an approval rule or an operator signs it, and the new certificate is installed at day 61. Clients using CA trust notice nothing, because the chain is unchanged; clients using direct trust break, which is exactly why direct trust and short lifetimes do not mix. The failure mode to engineer out is the half-rotated device: certificate replaced but process not restarted, so the old certificate is still presented. Use ApplyChanges semantics or an explicit restart, and monitor the presented certificate’s thumbprint from outside.

GDS hardening notes

Protect the GDS itself, since it can mint identities. Keep the CA private key offline or in an HSM, give the GDS only an intermediate CA key, and restrict who can approve registrations. Log every StartSigningRequest and FinishRequest. Do not let applications request certificates for an applicationUri other than the one they registered, and verify that the signing policy checks the SAN against the registered URI. I have not confirmed whether every commercial GDS enforces that check by default; test yours with a deliberately mismatched request.

PubSub Security and the Security Key Service

Client/server security rests on a per-connection handshake. Publish/subscribe has no connection, so Part 14 uses a different trust mechanism: symmetric keys shared by a group. The publisher encrypts and signs NetworkMessages with a key that belongs to a SecurityGroup, and a Security Key Service (SKS) hands those keys to authorized publishers and subscribers. The PubSub policies listed earlier, PubSub-Aes128-CTR and PubSub-Aes256-CTR in the open62541 documentation, use AES in counter mode and are symmetric only; that stack documents them as the PubSub message-security options.

The key flow is ordinary OPC UA client/server. A publisher or subscriber connects to the SKS, which is itself an OPC UA server (often co-located with the GDS or an edge broker), over a secure channel with full certificate authentication. It calls the SKS key-retrieval method (named GetSecurityKeys in Part 14, to the best of my knowledge from the specification family, though I did not fetch that page this run) for a SecurityGroup and receives the current key, upcoming keys and lifetimes. Keys rotate on a schedule, so a leaked key has a bounded window.

Two design points are worth stating plainly. First, authorization to a SecurityGroup is the real access control for a PubSub topic. Anyone who can obtain the group’s key can both read and forge messages for that group, since symmetric signing does not distinguish publisher from subscriber. If you need to know which publisher sent a message, symmetric group keys alone do not give you that. Second, transport still matters. PubSub over MQTT can run over TLS to the broker as well as using message-level security, and the two stack. Message-level security protects data end to end through the broker; broker TLS protects the link. If the broker is untrusted, only the former is meaningful.

For brownfield plants, the pragmatic path is often client/server security for configuration and commands, and PubSub security for telemetry fan-out, with the SKS keyed by the same GDS-issued certificates. For the equivalent considerations on the MQTT side, see the Sparkplug B comparison.

Workload identity as a parallel pattern

OPC UA’s application identity is, conceptually, workload identity: a cryptographic identity per software instance, rotated automatically, bound to a stable name (the applicationUri). If your platform already issues SPIFFE identities to workloads, the two can meet at the edge: SPIFFE SVIDs for cloud-side services, OPC UA certificates for the plant side, and a gateway that translates. Our SPIFFE and SPIRE architecture guide walks through the cloud half. A note on scope: the OPC UA applicationUri is a URI in the SAN, as SPIFFE IDs are, so a shared issuing CA could technically mint both; whether you should is an organisational question, since it merges the blast radius of OT and IT PKI.

Mapping to IEC 62443 (Analysis)

The following mapping is my own analysis of how OPC UA mechanisms support IEC 62443 requirements. It is not an official crosswalk, and conformance to a standard depends on the whole product and process, not on a protocol feature.

IEC 62443 area OPC UA mechanism that can support it Gap you still own
Identification and authentication, software processes and devices (IEC 62443-4-2 component requirements) Application instance certificates and secure channel Key storage in hardware; unique per-device keys
Human user identification and authentication User tokens, X509 or issued tokens MFA and identity-provider integration
Use control and authorization Roles and node permissions Least-privilege role design
Data integrity and confidentiality (IEC 62443-3-3 SR 3.1 and 4.1 areas) Sign and SignAndEncrypt modes Policy choice, key lifetime
Public key infrastructure use Trust lists, issuer lists, GDS CA operations, revocation process
Audit and event logging Audit events defined by the information model Collection and retention
Resource availability Limits on channels and sessions are implementation-defined Rate limiting, segmentation

The final row is where OPC UA is weakest by design: availability is largely an implementation matter. The zone-and-conduit model of IEC 62443-3-2 then places the OPC UA channel as a conduit with its own security level target, which gives you a clean place to document “this conduit requires SignAndEncrypt with a CA-issued certificate”. For the regulatory side, our article on the CRA, NIS2 and IEC 62443 mapping shows how to turn these into evidence. For machine-builder specifics, see the OPC UA companion specifications for robotics and machinery, since a companion spec defines the information model while the security model discussed here protects it.

Hands-On: A Secure Client with Explicit Trust

Theory is cheap, so here is a minimal path with the open-source asyncua Python library. The snippet is a sketch: the set_security_string call format is from the asyncua documentation as I remember it, and I did not execute it this run, so check it against your installed version.

First, generate a client certificate whose SAN carries the same URI the client will announce, with the key usages Part 6 describes for RSA profiles.

openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
  -keyout client_key.pem -out client_cert.pem \
  -subj "/O=Plant/CN=cell1-historian" \
  -addext "subjectAltName=URI:urn:plant:cell1:historian,DNS:hist01.plant.example" \
  -addext "keyUsage=digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyCertSign" \
  -addext "extendedKeyUsage=clientAuth"
openssl x509 -in client_cert.pem -outform der -out client_cert.der

Then connect with a pinned server certificate. The server certificate file is one you obtained out of band and verified against its thumbprint, which is what direct trust means in practice.

import asyncio
from asyncua import Client

URL = "opc.tcp://plc01.plant.example:4840"
SEC = ("Basic256Sha256,SignAndEncrypt,"
       "client_cert.der,client_key.pem,server_cert.der")

async def main():
    client = Client(URL)
    client.application_uri = "urn:plant:cell1:historian"  # must equal the SAN URI
    await client.set_security_string(SEC)
    client.set_user("historian_ro")
    client.set_password(open("/run/secrets/opcua_pw").read().strip())
    async with client:
        node = client.get_node("ns=2;s=Line1.Speed")
        print(await node.read_value())

asyncio.run(main())

What this demonstrates is a short list of habits. The application URI matches the SAN. The server certificate is supplied explicitly rather than accepted on first use. The mode is SignAndEncrypt, so the username password is protected by the secure channel and the token policy. The user is a read-only identity, not an administrator. The password comes from a secret mount, not source control.

On the server side with open62541, the default-with-security-policies configuration call takes the server certificate, private key, and arrays for the trust list, issuer list and revocation list, so you can start with an empty trust list and add only known peers. The function name and exact signature have changed between releases; read the header in the version you build. An empty trust list is a good first test: a correctly configured server rejects every client until you add one.

Trade-offs, Gotchas, and What Goes Wrong

OPC UA security failure modes mapped to the layer where each is caught

Figure 4: Common OPC UA security failures and where in the stack each should be detected.

The None endpoint left on. Servers keep None for discovery and then also expose data on it. An attacker on the network needs no certificate. Fix: disable data access on the None endpoint or remove it entirely and use a separate discovery server.

Auto-accept of untrusted certificates. Several default configurations and quick-start examples accept any certificate and move it to a “rejected” store, or worse, trust it. In production this makes encryption protect against a passive eavesdropper only; an active attacker simply presents their own certificate. Fix: reject by default and promote through a controlled process.

Anonymous plus a broad default role. Authentication occurs, but everyone lands in an administrator role. Fix: map the anonymous token to a minimal read-only role or disable it.

Expiry outages. Ten-year certificates are not an answer; they only move the outage past the next staff turnover. Monitor validTo and the age of the oldest certificate in each trust list.

Clock drift. Certificate validity and token exp, nbf checks depend on correct time. A controller with a dead battery that boots in 2000 will reject every certificate as not yet valid. Distribute time from a trusted source and alarm on skew.

Cloned images. Two devices with the same key and applicationUri break identity, and a compromise of one compromises both.

Hostname and URI mismatches that engineers learn to override. A dialog with an “accept anyway” button trains operators to click it. Make mismatches an incident.

Availability under unauthenticated load. Asymmetric operations at OpenSecureChannel are expensive, and a small controller can be pushed to its limits by repeated handshake attempts. The protocol has limits on channels and sessions, but defaults vary by stack and are implementation-defined. Use firewall allow-lists so only known client addresses reach port 4840, and rate limit.

Old policies for old clients. A single legacy client drags the whole server down to Basic128Rsa15. Segregate legacy clients behind a gateway that terminates the old policy and speaks modern ones inward, rather than enabling the weak policy server-wide.

Overreach of GDS push roles. The identity that can call UpdateCertificate can replace a server’s trust. Few teams model it as an administrator-equivalent credential, but it is.

None of this is exotic. Each is the product of convenience at commissioning time meeting no ongoing ownership, which is why the recommendations below are mostly about process.

Practical Recommendations

Start from the threat model of the conduit, not from the feature list. Inside a locked cell with no routable path, Sign with direct trust may be defensible. Across any zone boundary, use SignAndEncrypt with a CA-issued certificate, a modern RSA policy (or an ECC policy once your devices and conformance statements support it), and explicit user tokens.

Pick one PKI and one owner. A named team operates the CA, the GDS, and the revocation process, and publishes how long certificates live. Prefer short lifetimes with automation to long lifetimes with hope. If a device cannot rotate, isolate it behind a gateway and shorten its exposure rather than extend its certificate.

Treat the trust list as configuration under change control. Export it, diff it, and alert on additions. Treat the rejected store as a work queue, not a dustbin.

Hardening checklist:

  • [ ] Inventory every OPC UA server and client with applicationUri, certificate expiry, issuer and trust-list size.
  • [ ] Remove Basic128Rsa15 and Basic256; keep at most one or two policies per endpoint.
  • [ ] Disable data access on the None endpoint, or remove it.
  • [ ] Set message mode SignAndEncrypt for anything crossing a zone.
  • [ ] Disable auto-accept; reject unknown certificates by default.
  • [ ] Verify SAN URI equals applicationUri and a stable DNS name or static IP is present.
  • [ ] Generate keys per device on first boot or via GDS; never bake keys into images.
  • [ ] Disable Anonymous or map it to a minimal role; no shared admin accounts.
  • [ ] Define roles and node permissions; test with a read-only user.
  • [ ] Test revocation: revoke a test certificate and confirm connection refusal.
  • [ ] Test token expiry handling for issued/JWT tokens.
  • [ ] Synchronise time on all devices; alarm on drift.
  • [ ] Firewall port 4840 (and any HTTPS or WebSocket ports) to known peers.
  • [ ] Restrict and log GDS registration, signing and push roles.
  • [ ] Put PubSub SecurityGroup membership under the same review as user roles.
  • [ ] Record the above as evidence against your IEC 62443 or CRA technical file.

Frequently Asked Questions

Is OPC UA secure by default?

No. The protocol offers strong mechanisms, but a server can be deployed with the None policy, anonymous tokens and auto-accepted certificates, and many quick-start configurations are. Security is a configuration and operations outcome. Verify the endpoint list, token policies and trust behaviour on every server rather than relying on the product being described as OPC UA secure.

What is an OPC UA application instance certificate?

It is an X.509 certificate identifying one installed instance of an OPC UA application. Part 6 requires a subject alternative name URI equal to the applicationUri, and servers also need a DNS name or static IP. It is used to authenticate the application during OpenSecureChannel, separate from any user identity presented later at ActivateSession.

Which security policy should I use?

Avoid Basic128Rsa15 and Basic256, which are deprecated or superseded. Among RSA policies, Aes256_Sha256_RsaPss is described by the open62541 docs as the strongest and Basic256Sha256 as the common baseline. Choose what both ends support, with SignAndEncrypt, and check vendor conformance statements before relying on ECC policies.

What does the GDS actually do?

The Global Discovery Server, defined in Part 12, registers applications, signs certificate requests and distributes trust lists. In the pull model applications call it; in the push model an administrative client updates each application’s ServerConfiguration. It automates rotation but is also a high-value target, since it can mint trusted identities.

Can I use OAuth or JWT with OPC UA?

Yes, through the IssuedIdentityToken defined in Part 4 and detailed in Part 6 section 6.5 of the 1.05 specification. The server advertises an issuer endpoint, validates the JWT signature, and checks aud and exp. Kerberos is deprecated as of 1.05. The optional cnf claim binds the token to a certificate.

How does PubSub security differ from client/server?

PubSub has no session handshake. Publishers and subscribers share symmetric group keys obtained from a Security Key Service over an ordinary secure OPC UA connection. Anyone holding the key can read and forge messages for that group, so access to the SecurityGroup is the real control, and key rotation limits exposure.

Further Reading

By Riju — about

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *