IBM Webshpere MQ client connecting remote queue using SSL - c#

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.

Related

WCF Message Encryption with in memory x509Certificate2 Certificate

I would like to be able to use a certificate that I have in memory and not from the "Current User" or "Current Machine" certificate store.
Our desire is to have multiple WCF clients (with the same service) that we connect to and store their public key in a database. Then, by some convention, pick which certificate to encrypt the message with. I do not want to have this stored in the Web.config nor have it stored in the local machines keystore. We are needing this to be agnostic of where it is deployed (Azure vs on-prem vs whatever).
I know that this would work:
client.ClientCredentials.ServiceCertificate.SetDefaultCertificate(StoreLocation.CurrentUser, StoreName.TrustedPeople, X509FindType.FindByThumbprint, cert.Thumbprint);
But I want to do:
client.ClientCredentials.ServiceCertificate.SetDefaultCertificate(x509Certificate);
Is this possible for an app to have it's own keystore, like in Java?
X509CertificateRecipientClientCredential defines a DefaultCertificate property which is settable.
According to http://referencesource.microsoft.com/#System.ServiceModel/System/ServiceModel/Security/X509CertificateRecipientClientCredential.cs,eaccbfb7a59b7226,references the property sets the same backing field as SetDefaultCertificate.

Using C#, how to programmatically determine if a certificate in a Windows certificate store has been disabled

I'm currently working on refining communications between mutually authenticated client/server applications using HTTPS. I am currently building validation logic into a C# client to help identify configuration issues when a TLS connection fails. In verifying the connection, I validate that the root CA certificate presented by the server is installed on the client, in the appropriate store, and is valid. I'm using X509Store to pull the X509Certificate2, and validating it using X509Chain.
My issue is that the certificate will report as valid even if the certificate has been disabled via MMC. So the TLS connection will fail, despite the chain reporting as valid.
It's an unlikely case, but one I'd like to handle by reporting something like "Could not connect because root CA is disabled."
Could anyone point me in the direction of a .NET or Win32 call that could be made to determine the value of "Certificate Purposes" for a certificate? Or to read the "Certificate Status" for a cert?
I read through MSDN's listing of what's in the System.Security.Cryptography namespace, and started looking into CryptoAPI and CNG, but didn't find anything so far.
Thanks!
That dialog does not "disable" a certificate it disables it "for all purposes". What this means is it counts as having an empty Enhanced Key Usage extension for purposes of EKU validation.
Normally a root certificate (or an intermediate CA certificate) will not have an EKU extension, so if you do a chain build with any ApplicationPolicy value it will be a match. Once you set it to Disable for all purposes you'll get a chain error X509ChainStatusFlags.NotValidForUsage.
If you want to build something for validating TLS you'd check either the Server Authentication or Client Authentication EKUs (depending on what you're checking):
// Server EKU or Client EKU
Oid eku = forServer ? new Oid("1.3.6.1.5.5.7.3.1") : new Oid("1.3.6.1.5.5.7.3.2");
// Test if it's valid for that purpose
chain.ChainPolicy.ApplicationPolicy.Add(eku);
If you want to "Disable" a root CA altogether, add a copy of the certificate to the Disallowed store (called "Untrusted Certificates" in the UI). That will result in a chain build producing X509ChainStatusFlags.ExplicitDistrust.

The LDAP Server is Unavailable using PrincipalContext and ADLDS

We are making use of ADLDS for our user management and authentication. We can successfully query the instance without problems. However, trying to perform an operation such as SetPassword will fail or even trying to create a new user if a password is not set, it fails. I can successfully update a user as long as its not password I'm trying to update. I've been reading a lot of different articles relating to this but not finding a resolution. Posting to see if I can get some fresh perspective on this issue, thanks for any input.
EXAMPLE
ContextType ctxType = ContextType.ApplicationDirectory;
string server = "myadldsserver.com";
string usersCN = "CN=Users,..."; // container where users reside
ContextOptions ctxOpts = ContextOptions.SimpleBind;
string uname = "myuser";
string pswrd = "mypass";
using(var ctx = new PrincipalContext(ctxType, server, usersCN, ctxOpts, uname, pswrd)
using(var newUser = new UserPrincipal(ctx)) {
newUser.Name = "newusername";
newUser.Enabled = true;
newUser.UserPrincipalName = "newusername";
newUser.Save();
newUser.SetPassword("newuserpassword");
}
ERROR 1
The first problem I encounter if I try to create a new UserPrincipal and call Save without having set the password like in Example above I get the exception A constraint violation occurred. with an InnerException extend message of 0000052D: AtrErr: DSID-033807D7, #1:0: 0000052D: DSID-033807D7, problem 1005 (CONSTRAINT_ATT_TYPE), data 2246, Att 9005a (unicodePwd)
Because of this error I tried moving the SetPassword before calling Save along with other approaches I found online such as getting the DirectoryEntry from the UserPrincipal and trying to call SetPassword but got a different error.
ERROR 2
Calling SetPassword before calling UserPrincipal.Save, when save is called, results in the error The directory property cannot be found in the cache.
Note that the same error will occur if I trying calling ResetPassword or getting a DirectoryEntry and calling Invoke("SetPassword"... as well
ERROR 3
From my research most seem to indicate this could have to do with needing to access AD LDS using a Secure connection. So, I changed my server to include the port of 636 string server = "myadldsserver.com:636" and I changed the ContextOptions to be ContextOptions.SimpleBind | ContextOptions.SecureSocketLayer.
Making these changes when the PrincipalContext is being constructed I get the following exception The server could not be contacted. with an inner exception of The LDAP server is unavailable., HResult is -2146233087
JAVA and LDP
To add some background to this, we do have similar code written in an older Java application. We are trying to port some of this logic over to .NET side in C#. The code in Java makes use of a Java keystore that contains the certificate that was generated on the AD LDS server. The Java application of course has no issues using the SSL port. We know the server seems to be configured correctly, it's just an issue of how to access it from .NET side.
Is there an equivalent on the .NET side such as the keystore in Java? We know that an SSL connection can be made to server. We have verified this using LDP as well.
GOALS
Be able to create a new user and set their password during creation
Be able to ResetPassword or ChangePassword for a user
Connect to our AD LDS instance from .NET securely
Have you tried using Microsoft Management Console to import the certificate?
Two ways to install the certificate
Either
Open a cmd.exe console and type "MMC"
File > Add/Remove Snap-In...
Select Certificates, click Add
Choose Computer Account and Local Computer when prompted, then OK...
Certificates should now be showing under Console Root
Certificates > Trusted Root Certification Authorities > Certificates > (right-click) > All Tasks > Import Certificate...
Find the certificate you want to import, click Next and choose defaults (Trusted Root Certification Authorities should already be
selected)
Click Next, Finish
(or)
Simply double-click on the .cer file for the certificate in Windows
Explorer, click Install Certificate... > Next > select the option to
"Place all certificates in following store" > Browse... > Select
Trusted Root Certification Authorities. Continue with next until done.
At this point your certificate is installed, and you should be able to communicate securely with your ADLDS server.

Why active directory is accessible on port 389 even after SSL is enabled?

I basically have three questions here: I need to use .Net 3.5.
I have enabled SSL on my active directory. I have exported the certificate and imported on a different machine. Now when I try to access the active directory using port 389, it allows me to connect . Is this an expected behavior?
Many places I found to use "LDAPS" in my directory path when using SSL. But when I use this I get Unknown COM Exception. Here on MSDN I found there is nothing such "LDAPS"
https://social.msdn.microsoft.com/Forums/vstudio/en-US/723c3908-5806-4515-a5b2-b565e0131a2b/active-directory-connection-ldap-over-ssl
Do I really need to provide the domain name before username (domain\user)? I am able to connect without specifying the domain name this way. All I need to provide the FQDN or the name to which the SSL certificate is issued.
I am using DirectoryEntry class for my implementation.
string path = "LDAP://hostname:port/SearchBase";
DirectoryEntry _directoryEntryObj = new DirectoryEntry(path, userName, password);
if(IsSSL)
_directoryEntryObj.AuthenticationType = AuthenticationTypes.SecureSocketsLayer;
object obj = _directoryEntryObj.NativeObject;
If you don't deactivate plain LDAP, it is an expected behavior, since LDAPS uses a different port, see: https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol
In general I don't believe you will get far with an AD that only runs on LDAPS (port 636), with plain LDAP (389) blocked. I don't think that most appliations implement LDAPS, but I could be wrong.
On top, the certificate has to valid. If you just export to another machine (with another hostname), the certificate won't be trusted. What kind of certificate do you use?
this could be linked with my concern regarding the validity of your certificate
When I played around with ldap/ad in .Net I found that you can also just use the current user. If you don't want to use that user, you'll have to go with domain\user. I also think that you consider that best practice, since you can always determine if the specified user is a local account or a domain account.
--
Hope I could help,
regards

Implement a C# Client that uses WebServices over SSL?

So I've got a ServiceReference added to a C# Console Application which calls a Web Service that is exposed from Oracle.
I've got everything setup and it works like peaches when it's not using SSL (http). I'm trying to set it up using SSL now, and I'm running into issues with adding it to the Service References (or even Web References). For example, the URL (https) that the service is being exposed on, isn't returning the appropriate web methods when I try to add it into Visual Studio.
The underlying connection was closed: An unexpected error occurred on a send.
Received an unexpected EOF or 0 bytes from the transport stream.
Metadata contains a reference that cannot be resolved: 'https://srs204.mywebsite.ca:7776/SomeDirectory/MyWebService?WSDL'
Another quandary I've got is in regards to certificate management and deployment. I've got about 1000 external client sites that will need to use this little utility and they'll need the certificate installed in the appropriate cert store in order to connect to the Web Service. Not sure on the best approach to handling this. Do they need to be in the root store?
I've spent quite a few hours on the web looking over various options but can't get a good clean answer anywhere.
To summarize, I've got a couple of questions here:
1) Anybody have some good links on setting up Web Services in Visual Studio that use SSL?
2) How should I register the certificate? Which store should it exist in? Can I just use something like CertMgr to register it?
There's gotta be a good book/tutorial/whatever that will show me common good practices on setting something like this up. I just can't seem to find it!
Well, I've figured this out. It took me far longer than I care to talk about, but I wanted to share my solution since it's a HUGE pet peeve of mine to see the standard. "Oh I fixed it! Thanks!" posts that leave everyone hanging on what actually happened.
So.
The root problem was that by default Visual Studio 2008 uses TLS for the SSL handshake and the Oracle/Java based Webservice that I was trying to connect to was using SSL3.
When you use the "Add Service Reference..." in Visual Studio 2008, you have no way to specify that the security protocol for the service point manager should be SSL3.
Unless.
You take a static WSDL document and use wsdl.exe to generate a proxy class.
wsdl /l:CS /protocol:SOAP /namespace:MyNamespace MyWebService.wsdl
Then you can use the C Sharp Compiler to turn that proxy class into a library (.dll) and add it to your .Net projects "References".
csc /t:library /r:System.Web.Services.dll /r:System.Xml.dll MyWebService.cs
At this point you also need to make sure that you've included System.Web.Services in your "References" as well.
Now you should be able to call your web service without an issue in the code. To make it work you're going to need one magic line of code added before you instantiate the service.
// We're using SSL here and not TLS. Without this line, nothing workie.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
Okay, so I was feeling pretty impressed with myself as testing was great on my dev box. Then I deployed to another client box and it wouldn't connect again due to a permissions/authority issue. This smelled like certificates to me (whatever they smell like). To resolve this, I used certmgr.exe to register the certificate for the site to the Trusted Root on the Local Machine.
certmgr -add -c "c:\someDir\yourCert.cer" -s -r localMachine root
This allows me to distribute the certificate to our client sites and install it automatically for the users. I'm still not sure on how "security friendly" the different versions of windows will be in regards to automated certificate registrations like this one, but it's worked great so far.
Hope this answer helps some folks. Thanks to blowdart too for all of your help on this one and providing some insight.
It sounds like the web service is using a self signed certificate. Frankly this isn't the best approach.
Assuming you're a large organisation and it's internal you can setup your own trusted certificate authority, this is especially easy with Active Directory. From that CA the server hosting the Oracle service could request a certificate and you can use AD policy to trust your internal CA's root certificate by placing it in the trusted root of the machine store. This would remove the need to manually trust or accept the certificate on the web service.
If the client machines are external then you're going to have to get the folks exposing the service to either purchase a "real" certificate from one of the well known CAs like Verisign, Thawte, GeoTrust etc. or as part of your install bundle the public certificate and install it into Trusted Root certificate authorities at the machine level on every machine. This has problems, for example no way to revoke the certificate, but will remove the prompt.
Thanks for this great tip, took a quick look around at your stuff and you have a lot of good ideas going on. Here's my little bit to add -- I'm figuring out webMethods and (surprise!) it has the same problems as the Oracle app server you connected to (SSL3 instead of TLS). Your approach worked great, here's my addendum.
Given static class "Factory," provide these two handy-dandy items:
/// <summary>
/// Used when dispatching code from the Factory (for example, SSL3 calls)
/// </summary>
/// <param name="flag">Make this guy have values for debugging support</param>
public delegate void CodeDispatcher(ref string flag);
/// <summary>
/// Run code in SSL3 -- this is not thread safe. All connections executed while this
/// context is active are set with this flag. Need to research how to avoid this...
/// </summary>
/// <param name="flag">Debugging context on exception</param>
/// <param name="dispatcher">Dispatching code</param>
public static void DispatchInSsl3(ref string flag, CodeDispatcher dispatcher)
{
var resetServicePoint = false;
var origSecurityProtocol = System.Net.ServicePointManager.SecurityProtocol;
try
{
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Ssl3;
resetServicePoint = true;
dispatcher(ref flag);
}
finally
{
if (resetServicePoint)
{
try { System.Net.ServicePointManager.SecurityProtocol = origSecurityProtocol; }
catch { }
}
}
}
And then to consume this stuff (as you have no doubt already guessed, but put a drum roll in here anyway):
var readings = new ArchG2.Portal.wmArchG201_Svc_fireWmdReading.wmdReading[] {
new ArchG2.Portal.wmArchG201_Svc_fireWmdReading.wmdReading() {
attrID = 1, created = DateTime.Now.AddDays(-1), reading = 17.34, userID = 2
},
new ArchG2.Portal.wmArchG201_Svc_fireWmdReading.wmdReading() {
attrID = 2, created = DateTime.Now.AddDays(-2), reading = 99.76, userID = 3
},
new ArchG2.Portal.wmArchG201_Svc_fireWmdReading.wmdReading() {
attrID = 3, created = DateTime.Now.AddDays(-5), reading = 82.17, userID = 4
}
};
ArchG2.Portal.Utils.wmArchG201.Factory.DispatchInSsl3(ref flag, (ref string flag_inner) =>
{
// creates the binding, endpoint, etc. programatically to avoid mucking with
// SharePoint web.config.
var wsFireWmdReading = ArchG2.Portal.Utils.wmArchG201.Factory.Get_fireWmdReading(ref flag_inner, LH, Context);
wsFireWmdReading.fireWmdReading(readings);
});
That does the trick -- when I get some more time I'll solve the threading issue (or not).
Since I have no reputation to comment, I'd like to mention that Mat Nadrofsky's answer and code sample for forcing SSL3 is also the solution for an error similar to
An error occurred while making the
HTTP request to https://xxxx/whatever.
This could be due to the fact that the
server certificate is not configured
properly with HTTP.SYS in the HTTPS
case. This could also be caused by a
mismatch of the security binding
between the client and the server.
Just use
// We're using SSL here and not TLS. Without this line, nothing workie.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
as mentioned by Mat. Tested with an SAP NetWeaver PI server in HTTPS. Thanks!
Mat,
I had such issues too and I have a way to avoid using certmgr.exe to add certificates to trusted root on a remote machine.
X509Store store;
store = new X509Store("ROOT", StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(certificate);
The 'certificate object' can be created like this:
X509Certificate2 certificate = new X509Certificate2("Give certificate location path here");

Categories

Resources