Resolving WinCC TIA V14 OPC UA Client Certificate Trust Failures
When a WinCC Professional (TIA Portal V14) OPC UA client refuses to connect to a Node.js node-opcua server running on a Raspberry Pi 3 — even though a third-party client (ProSys, UaExpert) connects successfully from the same engineering workstation — the failure almost always traces back to OPC UA application instance certificate trust, SecurityPolicy negotiation, or endpoint URL/ApplicationUri mismatch. This reference walks through a deterministic diagnostic path and field-proven remediation steps for shop-floor deployments.
1. Problem Statement
Engineering workstation symptoms commonly reported:
- ProSys OPC UA Client (or UaExpert) reads nodes successfully from
opc.tcp://<raspi-ip>:4840. - WinCC Runtime Professional on the engineering PC starts the OPC UA channel, attempts the
CreateSessioncall, and the channel stays in Connecting or transitions to Faulted. - The Windows Rejected certificate folder for the WinCC OPC UA client remains empty — no
.derfile appears for the Raspberry Pi server's certificate. - No certificate is created in the WinCC Runtime's own Own Certificates store, blocking the server-side trust step.
The TIA Portal V14 OPC UA Client channel (WinCC Professional only — Comfort Panels require the OPC UA Client license from V14 SP1 onward) implements the full OPC UA Binary protocol stack, but it stores and validates instance certificates in a WinCC-specific directory tree that diverges from the OPC Foundation reference stack. Engineers who follow the generic "copy the cert to the Rejected folder" instructions from a node-opcua manual end up with an empty reject folder and zero progress.
2. Root Cause Analysis
Four root-cause families cover roughly 95% of WinCC ↔ node-opcua certificate failures:
| # | Root cause | Symptom | Detection |
|---|---|---|---|
| A | WinCC Runtime account lacks write permission to the Rejected/Trusted certificate folders | No .der file appears in Rejected; channel remains Faulted |
Procmon trace shows ACCESS_DENIED on WinCCRTPro\Certificates\opcua\rejected\
|
| B | Server endpoint URL or ApplicationUri does not match the certificate's subjectAltName URI | Server returns Bad_CertificateUriInvalid; channel aborted |
Open server cert with OpenSSL; compare URI: field to configured endpoint |
| C | SecurityPolicy/MessageSecurityMode mismatch between WinCC channel and node-opcua | Channel never opens; no cert exchange; Bad_SecurityPolicyRejected
|
Compare WinCC channel SecurityPolicy (None/Basic128Rsa15/Basic256/Basic256Sha256) with server.options.securityPolicies
|
| D | Server certificate was issued by a private CA that is not in WinCC's Trusted root store, or has expired | WinCC logs Bad_CertificateUntrusted or Bad_CertificateTimeInvalid
|
Inspect cert validity dates and issuer chain |
C:\Program Files\Siemens\Automation\WinCC RT Pro\ by default. A certificate that is "Trusted" in Windows is still untrusted to WinCC OPC UA.3. WinCC OPC UA Certificate Store Architecture
WinCC Runtime Professional V14 stores OPC UA instance certificates in a fixed directory tree. The exact paths are documented in the WinCC Professional system manual (Siemens FAQ 63481236) and the TIA Portal V14 WinCC Professional documentation set.
| Folder | Default path (WinCC RT Pro, default install) | Purpose |
|---|---|---|
| Own Certificates | %ProgramFiles%\Siemens\Automation\WinCC RT Pro\Certificates\opcua\own\ |
WinCC client instance certificate (self-signed or CA-issued) |
| Trusted | %ProgramFiles%\Siemens\Automation\WinCC RT Pro\Certificates\opcua\trusted\ |
Trusted server certificates; once placed here, WinCC accepts the server without prompt |
| Rejected | %ProgramFiles%\Siemens\Automation\WinCC RT Pro\Certificates\opcua\rejected\ |
Temporary holding area; WinCC writes any untrusted server cert here during first connect |
| Issuer | %ProgramFiles%\Siemens\Automation\WinCC RT Pro\Certificates\opcua\issuer\ |
Trusted CA / intermediate certificates used to validate the chain |
Under TIA Portal V14 (no SP) the runtime uses X.509 v1 self-signed certificates generated at first runtime start. From TIA V14 SP1 onward, X.509 v3 with subjectAltName URI is supported, but the folder layout is unchanged.
C:\Program Files is write-protected by default. The WinCC Runtime is started by the SCADAConnect service, which typically runs as LOCAL SYSTEM — that account does have write access. However, when an engineer is debugging by launching WinCCRTPro.exe interactively, the user's ACL may be the bottleneck. The Rejected folder appearing empty is the canonical symptom of this ACL failure.4. node-opcua Default Server Configuration
The default node-opcua server (as of v0.3.x — the version contemporary with TIA V14, late 2016) generates a self-signed certificate the first time the server starts and stores it next to the running script:
// Typical node-opcua minimal server (V14 era)
const opcua = require("node-opcua");
const server = new opcua.OPCUAServer({
port: 4840,
resourcePath: "/UA/Server", // endpoint = opc.tcp://<host>:4840/UA/Server
serverInfo: {
applicationUri: "urn:<host>:NodeOpcUaServer",
productUri: "urn:node-opcua:server",
applicationName: { text: "NodeOpcUaServer" },
// V14 era defaults — note: Basic128Rsa15 deprecated in 2020
serverCertificate: null, // auto-generated on first start
securityPolicies: [
opcua.SecurityPolicy.None,
opcua.SecurityPolicy.Basic128Rsa15,
opcua.SecurityPolicy.Basic256
],
securityModes: [
opcua.MessageSecurityMode.None,
opcua.MessageSecurityMode.Sign,
opcua.MessageSecurityMode.SignAndEncrypt
]
}
});
server.initialize(() => { server.start(() => console.log("Server ready")); });
The auto-generated certificate is saved as server_cert.pem (PEM, base64 X.509) in the working directory. The matching private key is server_key.pem. Both are 2048-bit RSA, 1-year validity, with subjectAltName URI = urn:<hostname>:NodeOpcUaServer.
When ProSys connects successfully and WinCC fails, the cause is not in the server's cert validity — both clients see the same cert. The cause is on the WinCC side: WinCC is either dropping the cert to the Rejected folder (and the engineer is looking in the wrong directory) or refusing the cert silently because of an ApplicationUri mismatch.
5. Step-by-Step Diagnostic Procedure
-
Confirm the endpoint URL is reachable. From the engineering PC, open a command prompt and run
telnet <raspi-ip> 4840. A black screen means TCP is open; connection failed means firewall on the Pi blocks inbound 4840/TCP. -
Verify DNS / hostname resolution. The ApplicationUri in the server cert is bound to a hostname (or IP). The WinCC endpoint must use the same host identifier or the server returns
Bad_CertificateUriInvalid. Use the IP literally if hostname resolution is broken. -
Open the server cert with OpenSSL on the Pi and capture the subjectAltName URI:
openssl x509 -in server_cert.pem -noout -text | grep -A1 "Subject Alternative Name" # Expected line: URI:urn:raspi3:NodeOpcUaServer -
Check the WinCC Runtime service identity. In
services.msclocate SIMATIC WinCC RT Pro (and SCADAConnect if present). Confirm the Log On tab account. The LOCAL SYSTEM account is the safest default for OPC UA file access. -
Grant the service account write access to all four certificate folders:
icacls "C:\Program Files\Siemens\Automation\WinCC RT Pro\Certificates\opcua\rejected" /grant "SYSTEM":(OI)(CI)F /T icacls "C:\Program Files\Siemens\Automation\WinCC RT Pro\Certificates\opcua\trusted" /grant "SYSTEM":(OI)(CI)F /T icacls "C:\Program Files\Siemens\Automation\WinCC RT Pro\Certificates\opcua\own" /grant "SYSTEM":(OI)(CI)F /T icacls "C:\Program Files\Siemens\Automation\WinCC RT Pro\Certificates\opcua\issuer" /grant "SYSTEM":(OI)(CI)F /T - Stop and restart the WinCC Runtime (not just the project) so the OPC UA channel re-initialises.
-
Trigger the OPC UA channel in Runtime. Within ~5 seconds, the Rejected folder should contain a
.derfile whose filename is the SHA-1 thumbprint of the server certificate. If still empty, the channel never even reached theGetEndpointscall — the failure is at the transport or SecurityPolicy layer, not trust.
6. Solution: Manual Certificate Exchange (Field-Proven)
The most reliable remediation on TIA V14 — where the runtime's automatic trust prompt is not exposed in the HMI — is a deterministic two-side manual exchange.
6.1 Server side: export node-opcua cert to DER
# On the Raspberry Pi
openssl x509 -in server_cert.pem -outform der -out server_cert.der
# Copy the DER to the engineering PC (WinSCP, scp, USB stick)
6.2 Client side: import to WinCC Trusted folder
- Stop the WinCC Runtime.
- Copy
server_cert.dertoC:\Program Files\Siemens\Automation\WinCC RT Pro\Certificates\opcua\trusted\. - Append the SHA-1 thumbprint to the Rejected list file (WinCC uses
rejected.txtas a denylist; remove the thumbprint from there to be safe).openssl x509 -in server_cert.der -inform der -fingerprint -sha1 -noout # Example: SHA1 Fingerprint=AB:CD:EF:... # Make sure this line is NOT present in: # C:\Program Files\Siemens\Automation\WinCC RT Pro\Certificates\opcua\rejected\rejected.txt - Start the WinCC Runtime and re-arm the OPC UA channel.
6.3 Client side: export WinCC own cert to server
node-opcua, by default, trusts any client certificate placed under trusted/ in its PKI directory (set via server.options.serverCertificateManager). Export the WinCC client cert from the Own Certificates folder and add it to the Pi's trusted store:
# On the engineering PC (PowerShell as admin)
Get-ChildItem "C:\Program Files\Siemens\Automation\WinCC RT Pro\Certificates\opcua\own\" -Filter *.der
# Copy the .der to the Pi and rename to <thumbprint>.der in the node-opcua pki/trusted folder
ActivateSession with Bad_CertificateUntrusted, and the WinCC channel will show as Faulted with no cert in the Rejected folder because the failure happens after WinCC has already accepted the server cert.7. SecurityPolicy and MessageSecurityMode Alignment
TIA Portal V14 ships with the following OPC UA client-side defaults in the WinCC Professional OPC UA channel:
| WinCC setting (TIA V14) | Allowed values | Default | node-opcua counterpart |
|---|---|---|---|
| SecurityPolicy | None, Basic128Rsa15, Basic256, Basic256Sha256 (V14 SP1+) | None | SecurityPolicy.None |
| MessageSecurityMode | None, Sign, SignAndEncrypt | None | MessageSecurityMode.None |
| Authentication | Anonymous, Username/Password, Certificate | Anonymous |
resourcePolicies / userManager
|
| Endpoint URL | opc.tcp://host:port/resourcePath | — | Must match the URL exposed by GetEndpoints
|
The fastest path to a green channel in a brownfield factory is to set WinCC to SecurityPolicy = None and MessageSecurityMode = None, confirm the link, then progressively tighten:
- None / None → confirms transport and ApplicationUri.
- None / Sign → confirms cert exchange path (no payload encryption needed).
- Basic256Sha256 / SignAndEncrypt → production target; requires both sides to have SHA-256-capable certs and matching trust.
Basic256Sha256 with a 2048-bit RSA cert and SHA-256 signature. TIA V14 SP1 and later are required for Basic256Sha256 on the WinCC side.8. Network, Firewall and Timeout Tuning
OPC UA over TCP uses port 4840 by default. On the Raspberry Pi, ensure the OS firewall permits inbound 4840:
# Raspberry Pi OS (iptables legacy)
sudo iptables -A INPUT -p tcp --dport 4840 -j ACCEPT
# or with ufw
sudo ufw allow 4840/tcp
On the Windows engineering PC, allow outbound 4840 through Windows Defender Firewall (usually permitted by default for any local subnet, but corporate GPOs may block).
Session timeout tuning — WinCC's OPC UA Channel Diagnostics view will surface timeouts as the channel cycling between Connecting and Faulted every N seconds. The TIA V14 WinCC OPC UA channel exposes the following parameters in the channel configuration:
| Parameter | Default | Recommended for slow / wireless links |
|---|---|---|
| Session timeout (ms) | 60000 | 120000 |
| Publish interval (ms) | 500 | 1000 |
| Keep-alive count | 10 | 20 |
| Connection attempt timeout (ms) | 5000 | 15000 |
Common OPC Foundation status codes you will encounter in WinCC's channel log:
| Hex status | Symbolic | Meaning | Remediation |
|---|---|---|---|
| 0x80050000 | Bad_CommunicationError | TCP / TLS handshake failed | Check port, firewall, DNS |
| 0x801F0000 | Bad_SecurityPolicyRejected | No common SecurityPolicy | Lower WinCC policy to None, then escalate |
| 0x80220000 | Bad_CertificateUntrusted | Issuer not in WinCC Trusted list | Import issuer cert to issuer\ or server cert to trusted\
|
| 0x80220001 | Bad_CertificateTimeInvalid | Cert expired or not yet valid | Regenerate server cert with node-opcua v0.5+ and a 5-year validity |
| 0x8022000F | Bad_CertificateUriInvalid | Endpoint URL ≠ cert subjectAltName URI | Align URL with the URI field in the cert |
| 0x80060000 | Bad_Timeout | No response within configured timeout | Increase session timeout, check network latency |
9. Alternate Configurations and Edge Cases
9.1 Running node-opcua as a systemd service
On a Raspberry Pi 3 with Raspbian Jessie/Stretch, when started via systemd the server's working directory matters: server_cert.pem is generated next to the executable's CWD, not the script. Use WorkingDirectory=/opt/opcua in the unit file and ReadWritePaths=/opt/opcua.
9.2 Raspberry Pi 3 clock / time skew
The Pi has no RTC by default. If the system time drifts more than a few minutes from real time, the server cert (issued "now") will appear either not-yet-valid (Bad_CertificateTimeInvalid) or already expired on the WinCC side. Always enable systemd-timesyncd or install ntp before commissioning.
9.3 ApplicationUri with IP address
If WinCC connects by IP (opc.tcp://192.168.1.50:4840) and the server cert has urn:raspi3:NodeOpcUaServer in subjectAltName, set the node-opcua alternateName option to add a URI entry containing the IP, or change WinCC's endpoint URL to use the hostname. node-opcua v0.5.x+ accepts an altNames array:
const server = new opcua.OPCUAServer({
// ...
serverInfo: {
applicationUri: "urn:raspi3:NodeOpcUaServer",
// ...
},
serverCertificateManager: new opcua.CertificateManager({
automaticallyAcceptUnknownCertificate: false,
// ensure the cert's URI field includes the IP variant
})
});
9.4 Cross-subnet deployment
When the engineering PC and the Pi are on different VLANs, multicast DNS (used by some node-opcua discovery) does not cross L3 boundaries. Use the literal IP in the WinCC endpoint URL and disable discovery on the server side to avoid delays during the FindServers call.
9.5 TIA V14 vs V14 SP1 vs V15
The certificate folder layout is identical, but the supported SecurityPolicy set expands at SP1:
| Feature | V14 (no SP) | V14 SP1 | V15 / V15.1 |
|---|---|---|---|
| SecurityPolicy Basic128Rsa15 | Yes | Yes | Yes |
| SecurityPolicy Basic256 | Yes | Yes | Yes |
| SecurityPolicy Basic256Sha256 | No | Yes | Yes |
| X.509 v3 with subjectAltName URI | No | Yes | Yes |
| UA-TCP binary chunking > 64 KB | Limited | Yes | Yes |
10. Verification Checklist
After applying the fix, run through the following to confirm a stable channel:
- WinCC Runtime channel status reads Connected (green), not Faulted (red).
- No new
.derfile appears in the Rejected folder after a Runtime restart. - ProSys OPC UA Client on a third PC can browse and read the same tags that WinCC is reading.
- A node tag in WinCC (e.g.
Channel1\Connection1\Tag1) updates at the configured publish interval (default 500 ms). - Stopping the node-opcua process on the Pi causes the WinCC channel to enter Faulted within one publish interval × keep-alive count, and re-establishes within the session timeout when the server returns.
- The WinCC OPC UA Channel Diagnostics view shows zero
Bad_*service faults over a 10-minute observation window.
11. Troubleshooting Matrix
| Symptom | Most likely cause | First action |
|---|---|---|
| Channel stays Connecting, no Rejected cert | Firewall blocks 4840 or wrong host | telnet <pi> 4840 |
| Rejected cert appears, but channel still Faulted | WinCC cert not in node-opcua trusted | Export own cert, copy to Pi |
| Channel connects on None, fails on Basic256 | Cert signed with SHA-1, not SHA-256 | Regenerate node-opcua cert with v0.5+ |
| Channel connects once, drops every 60 s | Session timeout < server's keep-alive interval | Set session timeout 120000 ms in TIA |
| Bad_CertificateTimeInvalid | Pi clock skew | Enable systemd-timesyncd |
| Bad_CertificateUriInvalid | Endpoint URL ≠ cert URI | Match URL or add altNames on server |
| Channel Faulted after WinCC reboot only | Service account ACL lost | Re-apply icacls grants |
12. Field-Proven Configuration Reference
A minimal working configuration for a TIA V14 SP1 engineering PC talking to a Raspberry Pi 3 node-opcua v0.5.x server on a 192.168.1.0/24 production network:
| Setting | WinCC (TIA V14 SP1) | node-opcua server |
|---|---|---|
| Endpoint | opc.tcp://192.168.1.50:4840/UA/Server |
Same, derived from port + resourcePath
|
| SecurityPolicy | Basic256Sha256 | SecurityPolicy.Basic256Sha256 |
| MessageSecurityMode | SignAndEncrypt | MessageSecurityMode.SignAndEncrypt |
| Authentication | Anonymous | Default resourcePolicies
|
| ApplicationUri (client) | urn:<pc-host>:Siemens.WinCC.Pro |
— |
| ApplicationUri (server) | — | urn:raspi3:NodeOpcUaServer |
| Session timeout | 120000 ms | Default 60000 ms (server clamps to min) |
| Publish interval | 1000 ms | — |
| Certificate store (client) | %ProgramFiles%\Siemens\Automation\WinCC RT Pro\Certificates\opcua\ |
— |
| Certificate store (server) | — |
./pki/trusted/ + ./pki/rejected/ in CWD |
Why does the WinCC Runtime Rejected certificate folder stay empty even though my server cert exists?
The WinCC Runtime is most likely running under a Windows account that lacks write permission on C:\Program Files\Siemens\Automation\WinCC RT Pro\Certificates\opcua\rejected. Verify with Process Monitor or grant SYSTEM full control on all four opcua subfolders and restart the runtime.
My ProSys client connects to node-opcua but WinCC does not — what is different?
ProSys uses the OPC Foundation reference stack and auto-trusts server certificates on first connect in its user-managed trust store. WinCC Runtime Professional uses a vendor-specific directory tree and a strict ApplicationUri check. Align the WinCC endpoint URL with the cert's subjectAltName URI field and import the server cert to the trusted folder manually.
Which OPC UA SecurityPolicy should I use on a Raspberry Pi 3 with TIA V14?
Use Basic256Sha256 for new deployments. It requires TIA V14 SP1 or later on the WinCC side and node-opcua v0.5.x or later on the Pi. For brownfield compatibility, None is acceptable on a closed factory network but is not recommended for any link that leaves the cell.
How do I export the WinCC client certificate to add to node-opcua's trust list?
Stop the runtime, navigate to C:\Program Files\Siemens\Automation\WinCC RT Pro\Certificates\opcua\own\, copy the .der file to the Pi and place it in the node-opcua pki/trusted/ directory with the SHA-1 thumbprint as the filename. Restart both services.
The WinCC channel shows Bad_CertificateTimeInvalid even though the date is correct on both PCs. Why?
The Raspberry Pi 3 has no hardware RTC. If the system time on the Pi drifts from real time, the server certificate may already be expired (or not yet valid) from the WinCC client's perspective. Enable NTP via systemd-timesyncd and force a clock sync before commissioning.