Programatically creating an AD account through secure LDAP - c#

Currently I'm successfully creating AD accounts through C# via an LDAP connection using the PrincipalContext. I create a new UserPrincipal, apply the various properties as required and call save()
Essentially something like this
using(var pc = new PrincipalContext(ContextType.Domain))
{
using(var up = new UserPrincipal(pc))
{
up.SamAccountName = "whatever";
up.EmailAddress = "test#example.com";
up.SetPassword(password);
up.Enabled = true;
up.Save();
}
}
All works fine but now we need to do the same thing over a secure LDAP connection and I'm struggling to find any info online regarding the specifics of how to do this. This makes me think that perhaps there's no difference from how I'm currently doing and instead all I need do is make sure the server supports LDAPS and is configured to use it.
Perhaps SO is wrong forum for this, am happy to move the question to a different forum if so.

The default TCP port for LDAP is 389. That's what's used if you don't tell it otherwise. To use LDAPS, you have to specify the LDAPS port of 636. For example:
using(var pc = new PrincipalContext(ContextType.Domain, "example.com:636"))
Where example.com is your domain name.
However, using LDAPS requires that your computer trusts the SSL certificate that the server uses. Sometimes a self-signed cert is used and that will cause this to fail. PrincipalContext doesn't report certificate errors. It reports it as if the server could not be contacted. So if you have an issue there, you can use this PowerShell script to download the certificate and inspect it:
$webRequest = [Net.WebRequest]::Create("https://example.com:636")
try { $webRequest.GetResponse() } catch {}
$cert = $webRequest.ServicePoint.Certificate
$bytes = $cert.Export([Security.Cryptography.X509Certificates.X509ContentType]::Cert)
set-content -value $bytes -encoding byte -path "certificate.cer"
Change the first line to have your domain name. If that works, there will be a file called certificate.cer that you can double-click on and inspect. If your computer doesn't trust it, you will see a message saying so. If that is a problem, you probably just need to install the root certificate as a "Trusted Root Certificate" on your computer.

Related

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.

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.

C# Connection to Oracle Identity Management LDAP Server

We are working with another company whom has a Oracle Identity Management Server setup. We are to connect to this and authenticate users based on LDAP data retried from the server.
We have tried to plug into this by using an LdapConnection object passing in the server name and port along with the Network credentials they are providing to us so we are using a AuthType of Basic. However on the Bind() we are always failing because it says that we have invalid credentials. We have worked with the client to make sure they are correct and we have been able to log in to Oracle Identity Management with the credentials, although the user name we had to use data from the SearchRequests distinguished name. But even using that we keep on receiving the same error. Client is also using the credentials to connect via Java.
This is an issue where as I think there is no solution really, but does anyone out there have any idea on how to go about doing this? We have the same code running which is working and pulling from Active Directory. So our code should be fine as long as Oracle supports connecting in this fashion. But finding anything in regards to this topic is like pulling teeth.
Anyone have any experience with this out there? Please let me know I would be happy to provide any additional details if needed.
Thanks in advance!
I recently fumbled through connecting to OID using LDAP. Here's the code that ended up working for me:
// make sure the server and port are correct
using (var ldap = new LdapConnection("ldap.company.com:3060"))
{
// make sure to pass the username as a distinguishedName
var dn = string.Format("cn={0},cn=users,dc=company,dc=com", username);
// passing null for the domain worked for me
var credentials = new System.Net.NetworkCredential(dn, password, null);
ldap.AuthType = AuthType.Basic;
try
{
ldap.Bind(credentials);
return true;
}
catch (LdapException ex)
{
return false;
}
}

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

Accessing Impersonated users key store

I am impersonating a service user account in order to connect to a webservice that requires a cert to connect. I have installed the client cert on the service account on the machine which is running the code however I receive the error System.Security.Cryptography.CryptographicException: The system cannot find the file specified.
using (var ctx = new ImpersonationContext("svcAcctUserName", "domain", "password"))
{
var clientCert = new X509Certificate2("filePath", "certPassword");
}
The impersonation code works, for brevity I have left it out but I check to make sure my context is switched to the svcAcctUserName user by logging the Environment.UserName, which shows that I am running as svcAcctUserName. The filePath is correct, again I left it out, but I open and close the file before I create the X509Certificate2 object to make sure I have both access to the file and that my path is correct.
The error is confusing since I provide the path as a parameter and I know for certain the user running the code has access.
EDIT:
Also tried to do this: How to call a Web service by using a client certificate for authentication in an ASP.NET Web application
Although I am not using an asp.net application, I gave it a try anyway. I added the certificates add-in to the mmc, added the "local computer" certificates add in and then imported the cert into the Personal store of the local machine.
I then ran:
WinHttpCertCfg.exe -g -c LOCAL_MACHINE\My -s issuedToName -a domain\svcAcctUserName
Tried running the operation again, still same problem.
What am I missing?
So, as Alex pointed out, I do not understand the underlying architecture of certificate system in windows. However, after performing the above steps and modifying my code to use the X509Store, I have it working. Hopefully this will help someone:
using (var ctx = new ImpersonationContext("svcAcctUserName", "domain", "password"))
{
var store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
var clientCert = store.Certificates.Find(X509FindType.FindByIssuerName, "IssuerNameHere", false);
var clientCert2 = new X509Certificate2(clientCert[0]);
}

Categories

Resources