Azure-Samples / iot-edge-opc-plc

Sample OPC UA server with nodes that generate random and increasing data, anomalies and much more ...
MIT License
218 stars 94 forks source link

Add own certificates to OPC UA Server #348

Closed HenrikMidtun closed 4 months ago

HenrikMidtun commented 6 months ago

Please provide us with the following information:

This issue is for a: (mark with an x)

- [ ] bug report -> please search issues before submitting
- [ ] feature request
- [x ] documentation issue or request
- [ ] regression (a behavior that used to work and stopped in a new release)

Minimal steps to reproduce

Create a certificate with new key in /tmp/opcua_certs/

openssl req -x509 -nodes -newkey rsa:2048 -keyout /tmp/opcua_certs/key_opcua.pem -out /tmp/opcua_certs/opcua.pem -sha256 -days 365

Run the following command which is a mix of the quickstart plus options for adding private key and certificate

docker run -v /tmp/opcua_certs:/app/pki --rm -it -p 50000:50000 -p 8080:8080 --name opcplc mcr.microsoft.com/iotedge/opc-plc:latest \ --pn=50000 --autoaccept --sph --sn=5 --sr=10 --st=uint --fn=5 --fr=1 --ft=uint --gn=5 --ll=trace \ --pk=/app/pki/key_opcua.pem \ --af=/app/pki/opcua.pem

Any log messages given by the failure

Application store contains 2 certs 01: Subject CN=OpcPlc (thumbprint: 7B51229B40A79AB6CAB0A37DC09ADA465CC5B62B) 02: Subject CN=my-opcua, OU=foo, O=foo, L=foo, S=foo, C=foo (thumbprint: AB20A6603718D81A0285042338F73B96AB733CFE)

Expected/desired behavior

I expect that the passed in certificate is used as the application certificate. However, a new one is being generated and is preferred

OS and Version?

Debian 3.2.3 (bullseye)

Versions

Mention any other details that might be useful

I am unable to update the certificate at a later time because the CN is not matching the generated certificate. Would be nice to extend the readme with a guide on how to set your own certificates. Because not all OPC UA clients have the possibility of accepting the certificate from the server during the initial connection, but may have the option of adding the certificate beforehand as a verification.


Thanks! We'll be in touch soon.

cristipogacean commented 6 months ago

It seems that there is a bug in the logic of loading the application instance certificate when explicitly provided per command line argument the way you intended.

In the meanwhile, as alternative option you can do the following:

Create the public and private key for the OPC PLC server in /tmp/opcua_certs/ in slightly different manner, as follows:

md certs
md private
openssl req -x509 -newkey rsa:2048 -keyout private/cert.pem -outform der -out certs/cert.der -sha256 -days 365 -nodes `
  -subj "/CN=OpcPlc" `
  -addext "subjectAltName=URI:urn:OpcPlc:opcplc, DNS:opcplc" `
  -addext "keyUsage=critical, nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyCertSign" `
  -addext "extendedKeyUsage = critical, serverAuth, clientAuth" `
  -addext "basicConstraints=CA:FALSE"

Run the docker with the following arguments:

docker run -v /tmp/opcua_certs:/app/pki --rm -it -p 50000:50000 -p 8080:8080 --name opcplc mcr.microsoft.com/iotedge/opc-plc:latest
--pn=50000 --autoaccept --sph --sn=5 --sr=10 --st=uint --fn=5 --fr=1 --ft=uint --gn=5 --ll=trace
--ph=opcplc --cdn=opcplc --ap=app/pki/

Besides the bug I mentioned above, your generated cert is probably not really OPC UA compliant. Typically the server require the public key to be in .der format. Besides this, the subject common name is rather hardcoded to OpcPlc therefore your generated cert is not accepted as a valid certificate. The certificate shall also contain the hostname of the system you're running on. In this case is 'opcplc' as per docker name, which I specified here:
-addext "subjectAltName=URI:urn:OpcPlc:opcplc, DNS:opcplc and here: --ph=opcplc --cdn=opcplc

I hope this helps you moving forward.

HenrikMidtun commented 6 months ago

Thanks, Luis!

I didn't know that OPC UA had specific requirements to the server certificate, so great of you to share that. Makes me wonder if there are any requirements with regards to client certificates as well.

chunchiehdev commented 2 months ago

Hi, @cristipogacean

I am a beginner with OPC-PLC. I followed your steps and created the private key and certificate, then placed them in the client and server. Finally, I used Python OPC UA to connect. Server told me that my certificate is not trusted, so I had to manually move it. Is this normal?


<6>2024-06-13T19:36:14.877Z - Sender Certificate: (none)
<6>2024-06-13T19:36:14.881Z - ChannelId 1: Token #0 created. CreatedAt=19:36:14.881. Lifetime=3600000.
<6>2024-06-13T19:36:14.883Z - ChannelId 1: Token #1 activated. CreatedAt=19:36:14.881. Lifetime=3600000.
<6>2024-06-13T19:36:14.955Z - TCPSERVERCHANNEL ProcessCloseSecureChannelRequest success, ChannelId=1, TokenId=1, Socket=0160EBA3
<6>2024-06-13T19:36:14.961Z - ChannelId 1: closed
<3>2024-06-13T19:36:15.403Z - Rejecting OPC application with certificate CN=OpcPlc. If you want to trust this certificate, please copy it from the directory pki/rejected/certs to pki/trusted/certs
<3>2024-06-13T19:36:15.404Z - Certificate validation failed with suppressible errors but was rejected. Reason=BadCertificateUntrusted. [CN=OpcPlc] [1562DF891C9CF451C24D9ADF83B6ED9BA89C752D]
<3>2024-06-13T19:36:15.412Z - TCPSERVERCHANNEL ForceChannelFault Socket=021A7086, ChannelId=0, TokenId=0, Reason=BadSecurityChecksFailed 'Could not verify security on OpenSecureChannel request.'```
cristipogacean commented 2 months ago

@chunchiehdev, yes, this is normal. in OPC UA both client and server have their own application instance certificate. To allow communication the user must explicitly establish the mutual trust between the peers (both sides client and server). This is done by adding the server's public key certificate in the client's trusted certificates store and the client's public key certificate in the server's trusted certificates store.