OPC UA Certificate chain validation incomplete - OPC Foundation SDK - c#

I've been struggling with this code for hours.
I need to connect to an OPC UA server (IP 192.168.7.118), it's a Siemens S7-1200 PLC.
I'm using the official OPC Foundation NuGet package and the following code:
static void Main(string[] args)
{
// Generate a client application
ApplicationInstance application = new ApplicationInstance();
application.ApplicationType = ApplicationType.Client;
// Load the configuration file
application.LoadApplicationConfiguration(#"./ConsoleReferenceClient.Config.xml", false).Wait();
ApplicationConfiguration m_configuration = application.ApplicationConfiguration;
m_configuration.SecurityConfiguration.AutoAcceptUntrustedCertificates = true;
// Connect to a server
// Get the endpoint by connecting to server's discovery endpoint.
// Try to find the first endopint without security.
EndpointDescription endpointDescription = CoreClientUtils.SelectEndpoint("opc.tcp://192.168.7.118", false);
EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(m_configuration);
ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration) { };
// Create the session
Session session = Session.Create(m_configuration, endpoint, false, false, m_configuration.ApplicationName, (uint)m_configuration.ClientConfiguration.DefaultSessionTimeout, new UserIdentity(), null).Result;
}
I'm getting error Certificate chain validation incomplete on session.Create().
The config.xml file specifies:
<SecurityConfiguration>
<!-- Where the application instance certificate is stored (MachineDefault) -->
<ApplicationCertificate>
<StoreType>Directory</StoreType>
<StorePath>%CommonApplicationData%\OPC Foundation\pki\own</StorePath>
<SubjectName>CN=Quickstart Console Reference Client, C=US, S=Arizona, O=OPC Foundation, DC=localhost</SubjectName>
</ApplicationCertificate>
<!-- Where the issuer certificate are stored (certificate authorities) -->
<TrustedIssuerCertificates>
<StoreType>Directory</StoreType>
<StorePath>%appdata%\opc foundation\pki\issuer</StorePath>
</TrustedIssuerCertificates>
<!-- Where the trust list is stored -->
<TrustedPeerCertificates>
<StoreType>Directory</StoreType>
<StorePath>%appdata%\opc foundation\pki\trusted</StorePath>
</TrustedPeerCertificates>
<!-- The directory used to store invalid certficates for later review by the administrator. -->
<RejectedCertificateStore>
<StoreType>Directory</StoreType>
<StorePath>%appdata%\opc foundation\pki\rejected</StorePath>
</RejectedCertificateStore>
<!-- WARNING: The following setting (to automatically accept untrusted certificates) should be used
for easy debugging purposes ONLY and turned off for production deployments! -->
<AutoAcceptUntrustedCertificates>false</AutoAcceptUntrustedCertificates>
</SecurityConfiguration>
From Siemens Tia Portal, I exported the device certificate under trusted directory, and the issuer certificate under issuer directory. The issuer is the Tia Portal project itself.
I also tried to install both certificates with default Windows Certificate Management, but nothing worked.
I've worked with other OPC UA library in the past, but this is the first time I'm using the official OPC Foundation SDK and honestly I found the documentation a bit challenging to fully understand.
Any help would be much appreciated.

I worked around the problem with the following code:
// Get server certificate
var rawCertificate = endpointDescription.ServerCertificate;
CertificateIdentifier serverCertificate = new(rawCertificate);
// Add server certificate to trusted peers and trusted issuers
configuration.SecurityConfiguration.TrustedPeerCertificates.TrustedCertificates.Add(serverCertificate);
configuration.SecurityConfiguration.TrustedIssuerCertificates.TrustedCertificates.Add(serverCertificate);
This manages to manually add (code-behind instead of configuration file) the server certificate to trusted certificate store.
I don't know why, but I could not get it working loading configuration from file.

Related

How to get Azure Cloud Service details from App service via Code, .NET

Running the following code in Azure App service, which try to get details of a cloud service in the same Azure subscription, and get the exception about Certificate is is not associated with the subscription.
Although, under the subscription blade, under "Management certificates"
the certificate does exists and authorized.
X509Store certStore = new X509Store(StoreName.My, storeLocation);
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, prodThumbprint, false);
if (certCollection.Count > 0)
CredentialsProd = new CertificateCloudCredentials(DBConstants.AZURE_SUBSCRIPTION_ID_PROD, certCollection[0]);
using (var client = new ComputeManagementClient(Credentials))
{
var t = client.HostedServices.GetDetailedAsync(serviceName, CancellationToken);
}
The exception:
ForbiddenError: The server failed to authenticate the request. Verify that the certificate is valid and is associated with this subscription.
The certificate in the subscription:
The certificate in the App service
To use a certificate that is uploaded to or imported into App Service, first make it accessible to your application code. You do this with the WEBSITE_LOAD_CERTIFICATES app setting. For more detail information, please refer to Use an SSL certificate in your application code in Azure App Service.
Add an app setting called WEBSITE_LOAD_CERTIFICATES and set its value to the thumbprint of the certificate. To make multiple certificates accessible, use comma-separated thumbprint values. To make all certificates accessible, set the value to *.

IBM Webshpere MQ client connecting remote queue using SSL

I am trying to connect to the remote queues using secured SSL connection. I have all the details provided by third party regarding SSL connection and Queue Manager details. I have V8 version of MQ client installed on my windows machine.
SSL folder that thirdparty has shared contains jks,kdb,rdb and sth files.
I am using below code to initialize the properties in .net console application
const string SslKeyRepository = #"ssl folder location with key name included";
const string CipherSpec = "TLS_RSA_WITH_3DES_EDE_CBC_SHA";
const string CipherSuite = "SSL_RSA_WITH_3DES_EDE_CBC_SHA";
const string SslPeerName = "Peername";
const string ConnectionType = MQC.TRANSPORT_MQSERIES_CLIENT;
static Hashtable init(String connectionType)
{
Hashtable connectionProperties = new Hashtable
{
{MQC.TRANSPORT_PROPERTY, connectionType},
{MQC.PORT_PROPERTY, 1496},
{MQC.SSL_CERT_STORE_PROPERTY, SslKeyRepository},
{MQC.SSL_CIPHER_SPEC_PROPERTY, CipherSpec},
{MQC.SSL_PEER_NAME_PROPERTY, $"CN=\"{SslPeerName}\""}
};
// Add the connection type
// SSL
// Set up the rest of the connection properties, based on the
// connection type requested
switch (connectionType)
{
case MQC.TRANSPORT_MQSERIES_BINDINGS:
break;
case MQC.TRANSPORT_MQSERIES_CLIENT:
case MQC.TRANSPORT_MQSERIES_XACLIENT:
case MQC.TRANSPORT_MQSERIES_MANAGED:
connectionProperties.Add(MQC.HOST_NAME_PROPERTY, HostName);
connectionProperties.Add(MQC.CHANNEL_PROPERTY, Channel);
break;
}
return connectionProperties;
}
I have tried few things but I end up getting exception "MQRC_SSL_INITIALIZATION_ERROR"
I would appreciate if you can help me with this.
There are many reasons for MQRC_SSL_INITIALIZATION_ERROR. Some of them are mechanical issues such as whether the keystore files can be accessed. Some are procedural such as whether the handshake fails. The best way to diagnose is methodically checking the configuration and then performing differential testing.
For the first part of this, perform the following checks. If you have already done so, don't cut corners. Do it again.
Verify that the certificates are accessible by issuing runmqakm -cert -list against the KDB to verify that it is structurally intact and the stash file is present with the proper password.
Verify that the kdb file is not in a world-readable directory is that the files are not marked world-readable.
Verify that the service account that runs the app is the owner of the KDB files and containing folder and has write access. (Not sure why but GSKit insists that the KDB must be writeable at run time.)
Issue runmqakm -cert -details to verify that the certificate(s) corresponding to the queue manager is/are present and the details. If the QMgr uses a self-signed cert there will be only one. If the QMgr uses a CA-signed cert there should be an intermediate and a root signer.
Find out from the 3rd party whether they have specified SSLCAUTH(OPTIONAL) or SSLCAUTH(REQUIRED). If OPTIONAL then the KDB should have no personal certs, only signers. If REQUIRED then the KDB must have a personal cert and the label must be ibmwebspheremq[serviceaccount] in lower case.
For the differential testing, try some of the following tests:
Test the app by connecting to a local QMgr using TLS (Note: MQ hasn't used SSL for years. It's TLS now. The old field names still retain SSL labels, though.) until you know that it is correctly configured. Go grab a copy of MQ Advanced for Developers and you can do integration testing on the desktop with your own QMgr, fully licensed for free.
Test using one of the sample programs. Use amqsputc or amqsgetc, depending on whether the real app is supposed to have PUT or GET on the queue. These use the same KDB, samme certs, etc. the main difference being they are known-good code.
Ask your business partner to let you test without SSL to make sure the "mechanical" parts of the configuration are correct. This includes things like the firewall routing, host, port and channel name, QMgr name, etc. If you can't connect with plaintext channels, you definitely won't succeed with TLS channels.
Once that works, test with SSL enabled and SSLCAUTH(OPTIONAL) set at the QMgr. This demonstrates that the client can validate the QMgr's cert.
Once that works, and if the objective is to use mutual authentication, test with SSLCAUTH(REQUIRED) set at the QMgr and a personal cert in the local KDB. This demonstrates that the QMgr can validate the client's cert.
Then, and only then, turn on SSLPEER locally to filter on the QMgr cert's DN.
If these don't help, please update the question with detailed results of your testing. The most common issues include cert labels and KDB permissions. If the business partner gave you the JKS and KDB, these should generally not come with a personal cert, only trusted certs.

SignalR Working on One Computer But Not Others

We have a Windows service hosting SignalR. The same code is running on different machines, with different results.
If I go to this link on my laptop, it works:
https://localhost/signalr/negotiate
Response:
{
"Url":"/signalr",
"ConnectionToken":"AQAAANCMnd8BFdERjHoAwE/...==",
"ConnectionId":"0a09e290-c8af-48d6-b791-f05e3b8930b0",
"KeepAliveTimeout":20.0,
"DisconnectTimeout":30.0,
"ConnectionTimeout":110.0,
"TryWebSockets":false,
"ProtocolVersion":"1.2",
"TransportConnectTimeout":5.0,
"LongPollDelay":0.0
}
If I go to that same link on my desktop, I get this:
I checked the IE settings and the TLS settings are the same between my laptop and my desktop. I've also checked many other IE settings.
Edit: On good PC, get cert warning in browser. Doesn't happen on non-working PC.
Here is the code to get SignalR running:
protected override void OnStart()
{
LogUtility.LogInformation("SignalRHubHostController::OnStart()");
string url = this.Application.Configuration.SignalRHubHostController.HostUrl;
UriBuilder uri = new UriBuilder(url);
string schemeOverride = this.Application.Configuration.SignalRHubHostController.UriSchemeOverride;
if (!String.IsNullOrWhiteSpace(schemeOverride))
uri.Scheme = schemeOverride;
LogUtility.LogInformation(String.Format(CultureInfo.InvariantCulture, "Using [{0}] as the SignalR hub URL.", uri.Uri.ToString()));
this._disposableWebServer = WebApp.Start<Startup>(uri.Uri.ToString());
}
internal class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
}
I got this to work. Apparently it was an issue with the existing certificate set to use port 443. I don't know why that was an issue, but the following steps caused things to start working for me.
Note: Rick Strahl's blog helped: Hosting SignalR under SSL/https
Created certificate in IIS specifically for this purpose:
Note: I did not have to use MMC to copy the cert from the personal folder to the trusted root folder. It was already in the trusted root folder.
Delete the existing cert dedicated to port 443:
netsh http delete sslcert 0.0.0.0:443
Note: I originally tried to add the cert and got an error because a cert was already specified for port 443. That's why this delete is here.
Add my new cert for port 443:
netsh http add sslcert ipport=0.0.0.0:443
appid={12345678-db90-4b66-8b01-88f7af2e36bf}
certhash=hashGoesHere
Note: Rick Strahl's blog said to always use that appid. I don't know what an appid means in the context of a cert. So, yeah, whatever...
Run my app and have it connect successfully:
Note: I'll need to use a real cert and trust it so I stop getting that warning. But that's not the point of this answer. I just wanted to show how I got this working with SignalR.
So while this got things working, I don't know why the original cert dedicated to port 443 wasn't working. Perhaps it wasn't in the root store. I couldn't figure out how to find that cert based on its hash on the PC.
Edit
For completeness, I wanted to share how you can get the cert hash. You can manually type it from looking at the certs in IIS, or you can copy it by finding it in MMC. It's the Thumbprint property of the cert. You just have to remove the spaces.

Using secured websocket in C#

I'm using Fleck to implement websocket functionality in my MVC application. On the local setup I have https scheme with self signed certificate, whereas on production setup, I have paid certificate.
This is the syntax given in docs.
var server = new WebSocketServer("wss://0.0.0.0:8431");
server.Certificate = new X509Certificate2("MyCert.pfx");
server.Start(socket =>
{
//...use as normal
});
Here instead of MyCert.pfx, what should be given in case of self signed certificate / [aid certificate? Should it be a path? After providing only name, it gives error System.Security.Cryptography.CryptographicException: The system cannot find the file specified..
Yes, The parameter should be a file path, you can placed pfx file in AppData directory, And use Server.MapPath("~/App_Data/MyCert.pfx")

How to load another's certificate to my local certificate store?

I have a certificate (.pem file) that is distributed by another service vendor. I downloaded the certificate from the vendor and saved it to my local drive. In my WCF client, I am trying to load this certificate from the local drive and it is giving me an error “The private key is not present in the X.509 certificate” when communicating with the service. I was told that I need to load this certificate to my local certificate store to resolve this error. Can anyone provide some directions? Thanks!
I have the below function to load certificate from the path specified in the file parameter.
public static X509Certificate LoadCertificate(string file)
{
try
{
return X509Certificate.CreateFromCertFile(file);
}
catch (System.Security.Cryptography.CryptographicException)
{
string filestr = File.ReadAllText(file);
StringBuilder sb = new StringBuilder(filestr.Remove(0, filestr.IndexOf("-----BEGIN CERTIFICATE-----")));
sb.Replace("-----BEGIN CERTIFICATE-----", "");
sb.Replace("-----END CERTIFICATE-----", "");
//Decode
try
{ //see if the file is a valid Base64 encoded cert
byte[] certBytes = Convert.FromBase64String(sb.ToString());
return new X509Certificate(certBytes);
}
catch (System.FormatException)
{
throw;
}
}
}
In my WCF client, it is loading the certificate that was created from LoadCertificate() function.
public X509Certificate Certificate { get; set; }
ClientCredentials loginCredentials = new ClientCredentials();
loginCredentials.UserName.UserName = this.UserId;
loginCredentials.UserName.Password = this.Password;
loginCredentials.ClientCertificate.Certificate = new X509Certificate2(this.Certificate);
Your code says you are trying to use the certificate to authenticate the client to the server, in addition to providing a username and a password. That's pretty bizarre but I guess possible. You will need the private keys associated with that certificate for that purpose, as the client will need them to encrypt the communication so the server can use the certificate to decrypt and verify that the client is legit. A .pem file can contain both public and private keys but maybe the one that was sent to you does not?
My guess is that really you only wanted the client to connect to a server that is using this certificate to identity itself and encrypt the communication. If so, all the client needs to do is import the certificate locally so it can compare against this local version when the server sends it when the client first connects to it.
Do to that, Microsoft made double clicking on a .pem file in a file browser start the certificate import wizard. But in case that does not work for you, here is the hard way:
Start - run - mmc
File - Add/Remove snap-in
Select "certificates" - click Add - choose Computer Account - Local computer
Close snap-in window with OK
Now browse to Certificates (Local computer) - Personal - Certificates
Right click - All Tasks - Import

Categories

Resources