OPCFoundation / UA-.NETStandard

OPC Unified Architecture .NET Standard
Other
1.96k stars 946 forks source link

Connecting failed with error 'BadSecurityChecksFailed' with SDK Version 1.04.353.15 #565

Closed vijinpv closed 4 years ago

vijinpv commented 6 years ago

I am doing an upgrade of OPCUA SDK from version v1.03.340 to v1.04.353.15. After the upgrade of SDK I am facing a connection issue from a client with Authentication. Connection works fine if it is anonymous. The client uses SDK version v1.03.340. The same issue is observed with UAExpert(Version 1.4.4.275) tool as well. The ServiceResultException is raised inside the function RsaUtils.Decrypt() from server, which is called for UserNameIdentityToken decryption during the ActivateSession() method call. The exception is due to a missing private key in the server certificate. public static byte[] Decrypt( ArraySegment dataToDecrypt, X509Certificate2 encryptingCertificate, bool useOaep) { RSA rsa = null; try { rsa = encryptingCertificate.GetRSAPrivateKey(); if (rsa == null) { throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "No private key for certificate."); } Our application uses a directory certificate store which is located at C:\ProgramData\<Organization\ folder. The server generates certificate only once during the start up. If you restart the server, it uses the certificate generated in previous session. The issue is not present if I clear all certificate in the location and start the server. During this time the certificates are generated and used. Issue will be reproducible only if you restart the server with a previous certificate. What I see from my debugging is that the LoadPrivateKey() method in CertificateIdentifier is called only once when you generate a new certificate. For all other sessions it is not called. I have tried a workaround as of now for my testing. I have just tried to call LoadPrivateKey() method and pass the certificate while creating a session. This seems to solve the issue.

Please let me know if it is a known bug. Is there any fix for SDK?. If not what could be the issue with my server. Is not calling LoadPrivateKey() method on the next session expected?

vijinpv commented 6 years ago

callstack

mregen commented 6 years ago

@vijinpv Hi, could you share your app startup code? and config file? There might be some configuration issue which prevents the code from finding the .pfx on a subsequent start.

vijinpv commented 6 years ago

Since our application is a legacy application, we dont use the app.config file instead we do it through code as below.

Startup code: m_application = new ApplicationInstance(new ServerApplicationConfiguration()); m_application.ApplicationType = ApplicationType.Server;

// check the application certificate. m_application.CheckApplicationInstanceCertificate(false, 0);

            // start the server.
            DataAccessServer serverBase = new DataAccessServer();
            m_application.Start(serverBase);

In the above code ServerApplicationConfiguration is derived from Opc.Ua.ApplicationConfiguration. DataAccessServer is derived from Opc.Ua.Server.StandardServer.

Security configuration:

private void GetSecurityConfiguration(ref SecurityConfiguration cSecurityConfiguration) { if (cSecurityConfiguration == null) { throw new ArgumentNullException("cSecurityConfiguration"); }

        CertificateIdentifier cApplicationCertificate = new CertificateIdentifier();
        GetApplicationCertificate(ref cApplicationCertificate);
        cSecurityConfiguration.ApplicationCertificate = cApplicationCertificate;

        CertificateTrustList cTrustedPeerCertificates = new CertificateTrustList();
        GetTrustedPeerCertificate(ref cTrustedPeerCertificates);
        cSecurityConfiguration.TrustedPeerCertificates = cTrustedPeerCertificates;

        cSecurityConfiguration.NonceLength = 32;

        CertificateStoreIdentifier cRejectedCertificateStore = new CertificateStoreIdentifier();
        GetRejectedCertificateStore(ref cRejectedCertificateStore);
        cSecurityConfiguration.RejectedCertificateStore = cRejectedCertificateStore;

        cSecurityConfiguration.TrustedIssuerCertificates = null;
    }

private void GetApplicationCertificate(ref CertificateIdentifier cApplicationCertificate) { if (cApplicationCertificate == null) { throw new ArgumentNullException("cApplicationCertificate"); } cApplicationCertificate.StoreType = CertificateStoreType.Directory; cApplicationCertificate.StorePath = Environment.ExpandEnvironmentVariables("%CommonApplicationData%\\CertificateStores\MachineDefault"); cApplicationCertificate.SubjectName = " OPC UA Server"; }

private void AddSecurityPolicies(ref ServerConfiguration cServerConfiguration) { if (cServerConfiguration == null) { throw new ArgumentNullException("cServerConfiguration"); }

        ServerSecurityPolicy cServerSecurityPolicy1 = new ServerSecurityPolicy();
        cServerSecurityPolicy1.SecurityMode         = MessageSecurityMode.SignAndEncrypt;
        cServerSecurityPolicy1.SecurityPolicyUri    = @"http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15";
        cServerConfiguration.SecurityPolicies.Add(cServerSecurityPolicy1);

        ServerSecurityPolicy cServerSecurityPolicy2 = new ServerSecurityPolicy();
        cServerSecurityPolicy2.SecurityMode         = MessageSecurityMode.Sign;
        cServerSecurityPolicy2.SecurityPolicyUri    = @"http://opcfoundation.org/UA/SecurityPolicy#Basic256";
        cServerConfiguration.SecurityPolicies.Add(cServerSecurityPolicy2);

        ServerSecurityPolicy cServerSecurityPolicy3 = new ServerSecurityPolicy();
        cServerSecurityPolicy3.SecurityMode         = MessageSecurityMode.None;
        cServerSecurityPolicy3.SecurityPolicyUri    = @"http://opcfoundation.org/UA/SecurityPolicy#None";
        cServerConfiguration.SecurityPolicies.Add(cServerSecurityPolicy3);
    }

private void AddUserTokenPolicies(ref ServerConfiguration cServerConfiguration) { if (cServerConfiguration == null) { throw new ArgumentNullException("cServerConfiguration"); }

        cServerConfiguration.UserTokenPolicies.Add(new UserTokenPolicy(UserTokenType.Anonymous));
        cServerConfiguration.UserTokenPolicies.Add(new UserTokenPolicy(UserTokenType.UserName));
        cServerConfiguration.UserTokenPolicies.Add(new UserTokenPolicy(UserTokenType.Certificate));
    }

Note: The same app start up code was used in 1.03.340 SDK as well and is working fine.

Please let me know if you are looking for any specific code part.

jabire commented 10 months ago

How this is get fixed

mregen commented 10 months ago

If you don't want to use the file configuration, please use the app configuration with fluent API and don't fill out the structures directly, as you miss some internal sanity checking when the configuration is built which may lead to strange symptoms.