nauful / LibUA

Open-source OPC UA client and server library
Apache License 2.0
254 stars 91 forks source link

Connect at CodeSys with Basic256Sha256 - BadInvalidArgument #113

Closed VitorKawao closed 1 year ago

VitorKawao commented 1 year ago

Hello,

I am having some troubles connecting to CodeSys Server with Sign or Sign & Encrypt (Basic256Sha256). If I connect with None and None it works correctly.

When I first execute it shows an error BadSecurityChecksFailed, so I go to the CodeSys Server and trust my certificate. After that, when I try to OpenSecureChannel the servers return an BadInvalidArgument. Using WireShark I can see that there is something that is null, but I do not know what I am doing wrong.

image

If I try to connect CodeSys with another client like UaExpert, it shows a warning. image

But clicking at Ignore it works.

nauful commented 1 year ago

Can you paste a full Wireshark capture, or try a new security mode such as Aes128Sha256RsaOaep?

If both fail, the default client certificate probably failed security checks. You can trust an untrusted certificate on the server but it still may reject due to security checks. If that happened, you can provide your own client certificate.

VitorKawao commented 1 year ago

Hello nauful, Thank you for your reply, I don't know if I understand correctly the full WireShark, but I saved my WireShark and attached it.

The LibUa.pcapng is using Basic256Sha256, it is in my product (it is not with the latest version of LibUa).The LibUaAes128_Sha256_RsaOaep.pcapng is tested with Aes128_Sha256_RsaOaep at the Demo application (the latest version of LibUa).The UaExpert.pcapng is using UaExpert that it works with.

WireShark.zip

VitorKawao commented 1 year ago

I forgot to ask about the second part

If both fail, the default client certificate probably failed security checks. You can trust an untrusted certificate on the server but it still may reject due to security checks. If that happened, you can provide your own client certificate.

Both failed, so I need to try to provide my own certificate, sorry for the question, but how do I do it? I am creating the .pem and .der like almost the demo, exchanging for my data.

nauful commented 1 year ago

This doesn't look like a library issue, so I closed the issue but we can keep looking at your configuration.

Instructions for generating .pem and .der with openssl: https://serverfault.com/questions/1006639/create-der-certificatekey-from-pem Do check that your server can accept the generated certficates. Default security policies usually reject self-signed certificates, so if you don't have a key from a certificate authority, you would have to figure out how to change the security configuration on your server.

VitorKawao commented 1 year ago

Hello Nauful,

To fix the problem at OpenSecureChannel, I needed to insert some information in the cfg file of Codesys. I didn't change the certificate. But after that I have another problem, at CreateSession, it is returning StatusCode.BadUnknownResponse.

image

I notice that it happens with a Siemens plc that I have too. It is seems to be the same problem. I've attached the WireShark using libua + codesys, libua + siemens, uaexpert + codesys and uaexpert + siemens. WireShark.zip

With uaexpert everything works. With CodeSys I tested with SecurityPolicy.Basic256Sha256 and SecurityPolicy.Aes256_Sha256_RsaPss. With Siemens only Basic256Sha256 because it doesn't support Aes policy.

My code:

var appDesc = new ApplicationDescription("urn:DemoApplication", "uri:DemoApplication", new LocalizedText("UA SDK client"), ApplicationType.Client, null, null, null);
//Siemens
//var client = new DemoClient("192.168.1.160", 4840, 1000);
//CodeSys 
var client = new DemoClient("192.168.1.111", 4840, 1000);
var messageSecurityMode = MessageSecurityMode.SignAndEncrypt;
var securityPolicy = SecurityPolicy.Basic256Sha256;
bool useAnonymousUser = true;

ApplicationDescription[] appDescs = null;
EndpointDescription[] endpointDescs = null;

client.Connect();
client.OpenSecureChannel(MessageSecurityMode.None, SecurityPolicy.None, null);
client.FindServers(out appDescs, new[] { "en" });
client.GetEndpoints(out endpointDescs, new[] { "en" });
client.Disconnect();

// Will fail if no matching message security mode and security policy is found
var endpointDesc = endpointDescs.First(e => e.SecurityMode == messageSecurityMode && e.SecurityPolicyUri == Types.SLSecurityPolicyUris[(int)securityPolicy]);byte[] serverCert = endpointDesc.ServerCertificate;
var connectRes = client.Connect();
var openRes = client.OpenSecureChannel(messageSecurityMode, securityPolicy, serverCert);
if(openRes != StatusCode.Good)  
    return;

var createRes = client.CreateSession(appDesc, "urn:DemoApplication", 120);
if (createRes != StatusCode.Good)
{
     //Enter Here
    return;
}

Thank you for your help, Vítor Guedes

nauful commented 1 year ago

Can you try using the latest version? There is a nonce bug in previous versions which might affect this. Not sure what else could be the problem.

VitorKawao commented 1 year ago

I am already using the latest version.

nauful commented 1 year ago

Sorry, I don't know what else could be wrong. You would have to debug through the Connect method to see what failed. I suspect something in the encryption method is failing to encode or decode but I don't know exactly what.

VitorKawao commented 1 year ago

Apparently the connection is successful, at least the SandHello is good, and at MemoryBufferExtensions and Encoding classes nothing returns false. Maybe it is encoding some wrong information or missing some field, but I can't see what is wrong with it.

At the response of CreateSession it is not returning RequestCode.CreateSessionResponse, it is returning RequestCode.ServiceFault. As it is sending RequestCode.CreateSessionRequest at the message I suposing that I am sending something wrong or missing something

nauful commented 1 year ago

That means that no received message could be matched with CreateSessionRequest, mostly likely due to decryption failure. I'm not sure what would be causing that if there are no other errors.

VitorKawao commented 1 year ago

Hello Nauful, I have a question for you, do you know if libua works with any Siemens plc or CodeSys with sign and encrypt (Basic256Sha256)? Or is there a problem with my configuration?

If you would like to test on these plcs I can provide you access to them.

Thank you

nauful commented 1 year ago

Basic256Sha256 is old. I added support for Aes128Sha256RsaOaep and Aes256Sha256RsaPss to replace the older methods which were causing problems with PLCs in recent issues here.

You might have to debug the client locally, not sure what else I can do.

VitorKawao commented 1 year ago

I tested the CodeSys locally with Aes128Sha256RsaOaep and Aes256Sha256RsaPss, but it has the same error.

I could not teste my Siemens plc because it does not have these security mode.

Thanks for all your help Nauful

nauful commented 1 year ago

I haven't been able to reproduce this with any system I have but another user might have one. I will update here if I make any progress there.

Let me know if you have any software server which I can use to reproduce.

VitorKawao commented 1 year ago

Hello, CodeSys could be downloaded at https://store.codesys.com/en/. I added the installer at google drive too, https://drive.google.com/file/d/15r5xlJw2EzQNCcWLW8GxGQ4ZndAOjsnX/view?usp=share_link

But If you prefer I can give you access to my vpn then you can access my CodeSys or my Siemens plc (it only has old security mode).

Thank you

VitorKawao commented 1 year ago

I attached a document that specify how to use CodeSys as Opc Ua Server

codesysopcua.pdf

nauful commented 1 year ago

Thanks, will take a look when I get time to work on this.

nauful commented 1 year ago

I've installed and set up with these settings: S0 S1

However I don't see any security mode options: S2 S3

Did I miss a step?

silvaeberton commented 1 year ago

Hello Nauful,

To save changes of security, follow the steps:

image

nauful commented 1 year ago

Thanks, I can now connect and send a user/pass. It looks like the trouble is with encrypting the user identification token and the SignatureAlgorithmRsaOaep signature isn't acceptable for this specific server: image

Will investigate in more detail.

nauful commented 1 year ago

Using the latest code and a user (username admin-plc, password 123), I am able to connect:

var appDesc = new ApplicationDescription(
    "urn:DemoApplication", "uri:DemoApplication", new LocalizedText("UA SDK client"),
    ApplicationType.Client, null, null, null);

var client = new DemoClient("127.0.0.1", 4840, 1000);
var messageSecurityMode = MessageSecurityMode.SignAndEncrypt;
var securityPolicy = SecurityPolicy.Aes256_Sha256_RsaPss;
bool useAnonymousUser = false;

ApplicationDescription[] appDescs = null;
EndpointDescription[] endpointDescs = null;

client.Connect();
client.OpenSecureChannel(MessageSecurityMode.None, SecurityPolicy.None, null);
client.FindServers(out appDescs, new[] { "en" });
client.GetEndpoints(out endpointDescs, new[] { "en" });
client.Disconnect();

// Will fail if no matching message security mode and security policy is found
var endpointDesc = endpointDescs.First(e =>
    e.SecurityMode == messageSecurityMode &&
    e.SecurityPolicyUri == Types.SLSecurityPolicyUris[(int)securityPolicy]);
byte[] serverCert = endpointDesc.ServerCertificate;

var connectRes = client.Connect();
var openRes = client.OpenSecureChannel(messageSecurityMode, securityPolicy, serverCert);
var createRes = client.CreateSession(appDesc, "urn:DemoApplication", 120);

StatusCode activateRes;
if (useAnonymousUser)
{
    // Will fail if this endpoint does not allow Anonymous user tokens
    string policyId = endpointDesc.UserIdentityTokens.First(e => e.TokenType == UserTokenType.Anonymous).PolicyId
    activateRes = client.ActivateSession(new UserIdentityAnonymousToken(policyId), new[] { "en" });
}
else
{
    // Will fail if this endpoint does not allow UserName user tokens
    string policyId = endpointDesc.UserIdentityTokens.First(e => e.TokenType == UserTokenType.UserName).PolicyId;
    activateRes = client.ActivateSession(
        new UserIdentityUsernameToken(policyId, "admin-plc",
            (new UTF8Encoding()).GetBytes("123"), Types.SignatureAlgorithmRsaOaep),
        new[] { "en" });
}

Any incorrect username or password gives that service fault above (and BadUnknownResponse). Please try this code, either by creating a user with the same login in a test environment, or substituting the username and password with your own.

nauful commented 1 year ago

Updated the library, ActivateSession can return BadUserAccessDenied instead of BadUnknownResponse and specific responses for other calls.

VitorKawao commented 1 year ago

Hello Nauful, Thanks a lot. I downloaded the new code and tested it with the demo. It works correctly.

Now I'll update my application, but It will probably work too. If I have some problems, I will contact you again.

Thanks again, Vítor Guedes

nauful commented 1 year ago

Let me know how it goes, and feel free to let me know if you run into other issues.

By the way, if you are using this library for any projects, there is no obligation but I would be very interested in hearing where and how. I would appreciate your permission to use that information in my list of use cases. If you prefer email, my email is my username @gmail.com

Thanks.

nauful commented 1 year ago

Forgot to metion: if needed, the latest sample client can also generate better self-signed certificates now.

VitorKawao commented 1 year ago

Forgot to metion: if needed, the latest sample client can also generate better self-signed certificates now.

Thank you.

I send a email to you, explaning our product

VitorKawao commented 1 year ago

Hello, Sorry for bothering you again about this topic.Can you still connect at codesys? I am having some troubles and it does not connect anymore.Probably there is some configuration issue. I tried to install a new one and it does not work either.

nauful commented 1 year ago

Hello Vitor,

Yes, although I already have a VM as the result of following the steps above. Does that version with those steps still work for you? If not, you can share a new installer with me and I can set up another VM to try.

Also, can you check with the git version of code as it was on Feb 21? If something has broken in the source, I can revert that.

Thanks, Nauful

nauful commented 1 year ago

Hello Vitor,

In client.ActivateSession, please try changing the algorithm field to Types.SignatureAlgorithmRsaOaep256:

    new UserIdentityUsernameToken(policyId, "username",
        (new UTF8Encoding()).GetBytes("password"), Types.SignatureAlgorithmRsaOaep256),
    new[] { "en" });

That should fix one potential issue I found: UserIdentityUsernameToken needs a different algorithm.

Thanks, Nauful

VitorKawao commented 1 year ago

Hello Nauful,

I added the last installer of CodeSys at my drive. https://drive.google.com/file/d/1H8xypiy3xBxqAs0mU4T2oj8nJ6xYrFwQ/view?usp=sharing

I am having problem at

var openRes = client.OpenSecureChannel(messageSecurityMode, securityPolicy, serverCert);

It is returning BadInvalidArgument, so my problem is before the Activate Session.

Thank you, Vítor

nauful commented 1 year ago

Hello Vitor,

I'll install that on a new VM and take a look.

Thanks, Nauful

nauful commented 1 year ago

Hello Vitor,

On this screen I have two errors: image

If I press enter to set the active path: image

Scan network doesn't find anything: image

How do I set up a new application?

Thanks, Nauful

silvaeberton commented 1 year ago

Hello nauful,

To fix this error try to do this please:

Check plc version through icon tray->right click->about image

In the navigation tree, right-click on the device and choose update device image

Change the device to CODESYS Control Win V3 x64 - version 3.5.19.0 (should be the same version of icon tray plc)

nauful commented 1 year ago

Thanks, it works now. I'm able to reproduce, will investigate further.

nauful commented 1 year ago

I'm trying to enable the following config option: image

Which file should receive this update?

VitorKawao commented 1 year ago

It is at CODESYSControl.cfg On my pc is locates at C:\ProgramData\CODESYS\CODESYSControlWinV3x64\268E8ADF\CODESYSControl.cfg

nauful commented 1 year ago

I don't see any other error messages in the trace.

How did you make this work with the previous Codesys version?

nauful commented 1 year ago

I'm fairly sure the error is here: https://github.com/OPCFoundation/UA-LDS/blob/a5980842030791339220a03746fcc73748f0e418/stack/Stack/securechannel/opcua_securelistener.c#L1842

However I can't determine exactly what I'm missing. If you have a UA ANSI-C server with security enabled, I can step through that to debug. The only ANSI-C server I found doesn't include any security modes.

nauful commented 1 year ago

Tested with UA-LDS on Linux with the old ANSI-C stack, but this only supports up to Basic256 (no newer security modes) and uaservercpp with Basic256Sha256, didn't have any errors. I'm stuck on how to fix this.

VitorKawao commented 1 year ago

How did you make this work with the previous Codesys version?

For a very oldCodeSys I needed to set some properties at CODESYSControl.cfg, but at the newest versions including the first one that I sent to you, I did not do anything. But It stopped working there too, I don't know why. At the latest installer that I sent to you I could never run there

I'm fairly sure the error is here: https://github.com/OPCFoundation/UA-LDS/blob/a5980842030791339220a03746fcc73748f0e418/stack/Stack/securechannel/opcua_securelistener.c#L1842

This is the official api of opc ua? Does your api use it? Maybe it is the bug there?

If you have a UA ANSI-C server with security enabled, I can step through that to debug. The only ANSI-C server I found doesn't include any security modes.

Sorry but I don't have any UA ANSI-C server with security enabled.

I'll try to convince my client to use without sign and encrypt for now. I hope he accepts it.

I understand that it is hard to fix it without a server to debug. Thanks you for all your help, Vitor Guedes

nauful commented 1 year ago

Hello Vitor,

That's one version of the ANSI C stack used for OPC UA which I believe Codesys uses. I have tested the following versions: ANSI C Stack 1.02 Release [334.5] UA-AnsiC-Legacy-master UA-AnsiC-Legacy-1.04.342 UA-.NETStandard-master UA-.NET-Legacy-master UA-LDS-master

All of these worked fine. The AnsiC servers contain the same error messages I couldn't trigger this error with my client. Tried to debug Codesys' service exe with IDA but it looks like they protect the exe so I can't find error strings to set a breakpoint. I couldn't find anything else to try.

This is where I stopped: image

Thanks, Nauful

nauful commented 1 year ago

Good news! I have found a server where I can reproduce this issue and will investigate. More updates soon.

VitorKawao commented 1 year ago

Great new!!!

Thank you

nauful commented 1 year ago

I have pushed an update. It looks like it works for Codesys, could you please test that?

I will continue to investigate.

VitorKawao commented 1 year ago

I updated the demo and checked. It fixed the error at var openRes = client.OpenSecureChannel(messageSecurityMode, securityPolicy, serverCert);

But now I am having problem to ActivateSession, it returns an error: BadIdentityTokenInvalid

image

If I try to connect with anonymous it works perfectly.

At your test is it working with user/password?

nauful commented 1 year ago

Hello Vitor,

Please change SignatureAlgorithmRsaOaep256 to SignatureAlgorithmRsaOaep. That makes ActivateSession pass for me.

Thanks, Nauful

VitorKawao commented 1 year ago

Thank you Nauful!!

It worked!!!

nauful commented 1 year ago

Hello Vitor,

Glad to hear. Let me know if anything else comes up.

I might do some clean-up in the next few days but at least Codesys is working fine now.

Thanks, Nauful

VitorKawao commented 1 year ago

Hello Nauful, A question, how do I know which Signature Algorithm I need to use?

I have an old Siemens plc, it does not have the newest security policy so I need to connect with Basic256Sha256. Which Signature Algorithm should I use? I tried to all and I get the error BadIdentityTokenInvalid

nauful commented 1 year ago

Hello Vitor,

Can you browse the endpoints and give me a Wireshark capture of that activity? It's somewhere in EndpointDescription, the dump will help me find that.

Thanks, Nauful