Directory entry | Error when using Encryption or SSL - c#

I'm trying to encrypt my Active Directory access with the following code:
// Already tried different paths (LDAP://domain.com, LDAPS://domain.com etc.)
string path = "LDAP://domain.com:636";
var ldapConnection = new DirectoryEntry(path, "loginName", "password");
ldapConnection.AuthenticationType = AuthenticationTypes.Secure; // Works perfectly
ldapConnection.AuthenticationType = AuthenticationTypes.Encryption; // Doesn't work
ldapConnection.AuthenticationType = AuthenticationTypes.SecureSocketsLayer; // Doesn't work
Both Authentication Types that doesn't work throw the same exception:
System.DirectoryServices.DirectoryServicesCOMException (0x8007052E):
The username or password is incorrect.
Firstly I saw that I'm missing a Certificate Server on my AD DS and installed it. But after installation I get the same error. I might need to install/configure more things. If so, then please share resources what needs to be done.
My questions: Do I need any prerequisites (e.x. on the AD DS) to be able to use AuthenticationTypes.Encryption or AuthenticationTypes.SecureSocketsLayer? Or do I need a different user for using this Authentication Types?
Any Help is greatly appreciated.

Try this:
ldapConnection.AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.SecureSocketsLayer;
Secure defines the type of authentication that is used, whereas SecureSocketsLayer defines the type of connection. They serve different purposes, so they can be used together.
But really, you don't need to specify anything. The default is Secure, and if you specify port 636, it will use SSL since that's the only way the server would accept the connection on that port. That's why it works when you only specify Secure.
That's also the reason it fails if you specify SecureSocketsLayer by itself. Once you specify anything, the default (Secure) is discarded and only what you specify is used. Without Secure it will try basic authentication (AKA "simple bind"), which is probably disabled on your domain.
More reading in the documentation for the AuthenticationTypes Enum.

Related

In C# How to get Windows Credentials from credential manager

Every where they are giving answers for getting user......but i want to get credentials from Credential Manager?
I have tried all the ways using .,
1.System.Security.Principal.WindowsIdentity.GetCurrent().Name
**Output:** => "DESKTOP\\India"
This is giving domain name and not credentail manager credentials.
2.I have tried below code..using credential package from nuget
var cm = new Credential();
cm.Target = "Here what i have to put????";
I got a doubt here to what i have to put in cm.target="?????"
3.Tried
Environment.UserName()
Output: "India"(This is not from credential manager credentials)
4.Tried
System.Net.CredentialCache.DefaultCredentials()
Output:
{
`Username:'',`
password:''
}
Here i think we have to send the parametred and these are also not from windows credential manager credentials.
Iam waiting for help from you folks.any help will be appreciated.
In short, Windows credentials under the credential manager can only obtain usernames and not passwords. This is determined by the Native underlying API.
As for how to get the usernames in Windows Credential, please refer to the below link.
https://github.com/AdysTech/CredentialManager
The usage (the target parameter means the target network/internet address, namely the name in the left side):
//get the username of "a" credential. username:b, password:123456
var result=CredentialManager.GetCredentials("a", CredentialManager.CredentialType.Windows);
Console.WriteLine(result.UserName+": "+result.Password);
Result.
Here is a discussion related to this subject.
Retrieve credentials from Windows Credentials Store using C#
wish it is useful to you.
The original answer is confusing. It seems to say only usernames can be retrieved then links a project that can retrieve full credentials. I know this is tagged ASP, but it's also tagged C#. I've had minimal experience with ASP, but the code-behind shouldn't work any differently (and the answer above says nothing about it).
I've been retrieving stored credentials for some time now with PowerShell. Just now, I needed a C# solution (and I can't use that PS package with my project).
https://github.com/meziantou/Meziantou.Framework
Nothing special, but I wrote this now to confirm it worked. StoredCredentialName is the "Internet or network address" field for a generic credential.
Private Function GetCredentialFromStorage() As Net.NetworkCredential
Try
Dim c = Meziantou.Framework.Win32.CredentialManager.ReadCredential(StoredCredentialName)
Return New Net.NetworkCredential(c.UserName, c.Password)
Catch ex As Exception
Return Nothing
End Try
End Function

Connecting or Accessing Okta LDAP Interface using c# .NET Client

I have a client which uses Okta LDAP Interface facility. We have a LDAP v3 tool which connects with AD, Open LDAP other LDAP v3 supported servers.
We want to integrate Okta LDAP Interface into our tool as it is LDAPv3 Compatible. Our Code is based on .NET framework + C Sharp.
We are facing some issues/challenges while connecting with Okta LDAP Interface.
We use System.DirectoryServices by Microsoft library provided by microsoft currently. But facing issues with LDAP Interface.
For StartTLS/389
I get the error :
Unwilling to perform. LDAP Error Code 53
More: A secure connection cannot be established. To admin: This service requires TLS. LDAP
For SSL/636
Error: The server is not operational.
Links:
https://learn.microsoft.com/en-us/dotnet/api/system.directoryservices?view=netframework-4.8
https://learn.microsoft.com/en-us/dotnet/api/system.directoryservices.directoryentry?view=netframework-4.8
https://ldapwiki.com/wiki/LDAP_UNWILLING_TO_PERFORM
var oktaLDAPPath = "LDAP://dev-506668.ldap.oktapreview.com:636/ou=users,dc=dev-506668,dc=oktapreview,dc=com";
var un = "uid=*******,dc=dev-506668,dc=oktapreview,dc=com";
var pass = "*******";
var filter = "((objectClass=*))";
try
{
using (var userDirectoryEntry = new DirectoryEntry(oktaLDAPPath, un, pass,AuthenticationTypes.SecureSocketsLayer))
{
using (var directorySearcher = new DirectorySearcher(userDirectoryEntry, filter) { PageSize = 100 })
{
directorySearcher.FindOne();
}
}
}
catch (DirectoryServicesCOMException dex)
{
}
catch (Exception ex)
{
}
Thanks
Update: So I did some testing for myself. I see what's going on.
If you do a DNS lookup on dev-506668.ldap.oktapreview.com, it gives you a CNAME result to op1-ldapi-fb96b0a1937080bd.elb.us-east-1.amazonaws.com.
A browser will use the IP address of the CNAME, but still make the request with the host name that you originally gave it. However, for some reason, when starting an LDAP connection, Windows is using the CNAME to iniatate the connection.
In other words, Windows is changing the request to LDAP://op1-ldapi-fb96b0a1937080bd.elb.us-east-1.amazonaws.com:636. But then it receives the SSL certificate which has the name *.ldap.oktapreview.com and it panics because that doesn't match the name it used to make the request (op1-ldapi-fb96b0a1937080bd.elb.us-east-1.amazonaws.com).
I verified all of this using Wireshark, monitoring traffic on port 636. The SSL Client Hello is using op1-ldapi-fb96b0a1937080bd.elb.us-east-1.amazonaws.com instead of dev-506668.ldap.oktapreview.com.
I don't know of a way to make it not do that. DirectoryEntry has no way to override how it verifies the SSL certificate either. LdapConnection does, which you can see here, but it might be a little harder to work with. I've never used it. (you probably should do some verification of your own and no just return true like that example does).
This might be something you can share with Okta Support anyway.
Original answer:
It sounds like your computer does not trust the SSL certificate that is used on the server. To verify this, I use Chrome. You have to start Chrome like this:
chrome.exe --explicitly-allowed-ports=636
Then you can put this in the address bar:
https://dev-506668.ldap.oktapreview.com:636
If the certificate is not trusted, you will get a big error saying so. You can click the 'Advanced' button to see the reason Chrome gives for it not being trusted. But Chrome will also let you inspect the certificate by clicking on "Not secure" in the address bar to the left of the address, then click 'Certificate'.
There a couple reasons it might not be trusted:
The fully-qualified domain name you are using (dev-506668.ldap.oktapreview.com) doesn't match what is on the certificate. If that's the case, you might be able to just change the domain name you use to match the certificate.
The certificate is not issued by a trusted authority. It could be a self-signed certificate. If this is the case, then you should see an "Install Certificate" button when you view the certificate, which you can use to explicitly trust the certificate. See here for screenshots, starting on step 3. This will only apply to the current computer.
I recently ran into this issue and was pointed to the following solution:
Add the following registry value then restart the server and see if it fixes the issue.
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LDAP\UseHostnameAsAlias
DWORD, set the value to 1
See https://support.microsoft.com/en-us/topic/an-error-occurs-when-you-try-to-establish-ssl-connections-to-the-nodes-by-using-the-alias-name-from-an-ldaps-client-computer-that-is-running-windows-7-or-windows-server-2008-r2-49b6ee93-2c68-a892-8133-612d208dd1b1 for more details.

Use Windows login credentials on DotRas VPN connection

I'm trying to use the Windows login credentials on a DotRas VPN connection. This is what I've tried so far, without any luck:
this.rasDialer.Credentials = CredentialCache.DefaultNetworkCredentials;
this.rasDialer.DialAsync();
and:
this.rasDialer.Credentials = (NetworkCredential) CredentialCache.DefaultCredentials;
this.rasDialer.DialAsync();
I'm getting error code 691 when I'm trying to connect, which is the same error code I get when I'm trying with a blank username and a blank password:
this.rasDialer.Credentials = new NetworkCredential("", "");
this.rasDialer.DialAsync();
This gives me error code 13801:
this.rasDialer.Credentials = new NetworkCredential("dummy", "dummy");
this.rasDialer.DialAsync();
Thanks for any help solving this.
Error code 691 indicates a general authentication failure message from RAS, so if you're seeing that when you're not passing in credentials that would probably be expected.
Per Microsoft's documentation found here:
The credentials returned by the DefaultNetworkCredentials property is applicable only for NTLM, negotiate, and Kerberos-based authentication.
Your ability to use the CredentialCache.DefaultNetworkCredentials property would likely be determined by the settings within the phone book for your connection given that the settings on what the entry can support would likely enable or prevent the use of the property you're attempting to use.
As for what the error 13801 represents, I have not seen that error before so I'm not sure. If I were to venture a guess, depending on your entry settings it could be EAP related. Most RAS related errors are between 600 and 800.
Hopefully the new version makes these sorts of issues less irritating to diagnose.

HttpClient request with x509 Certificate works on Debug but fails in Production (403 forbidden)

I have a black box service I have to call into with simple rest commands that returns xml.
They issued us a certificate that had to be run in IE and installs in to IE's Certificate section. As per their instructions I exported it with the entire chain as a pfx with password.
On the machine that the cert was issued directly to, everything works fine in code
var certHandler = new WebRequestHandler();
certHandler.ClientCertificateOptions = ClientCertificateOption.Manual;
certHandler.UseDefaultCredentials = false;
var certificate = new X509Certificate2(Properties.Resources.SigningCert, "password", X509KeyStorageFlags.DefaultKeySet | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet); //Must be renewed and replaced every year.
certHandler.ClientCertificates.Add(certificate);
//Execute the command
var client = new HttpClient(certHandler);
string result;
try
{
result = await client.GetStringAsync(url);
System.Diagnostics.Debug.WriteLine(result);
}
catch (Exception ex)
{
throw ex;
}
(I've stored the cert in the resources, but it loads fine and loading it from a file works fine too in developer machine.) I also imported it into IE on the server just in case. Obviously this is likely under the wrong cert store, but I couldn't figure out how to load this in globally. I can tell you that the same REST GETs work in IE on the server just like they do on the developer machine. It's only in code that it fails.)
In production, this same code throws a 403 forbidden.
Production (really a beta server) is actually behind the same nat as the as the development machine so they're seeing the same IP come through etc.
Any ideas why it would fail on the server and not on the developer box?
Thanks!
You should use X509KeyStorageFlags according to account under which your app is running. If it is
1) An app that runs under regular Windows User Account you should use
X509Certificate2(Properties.Resources.SigningCert, "password", X509KeyStorageFlags.UserKeySet);
2) Windows Service under LocalSystem, IIS under NetworkService or other services under built in Windows Account, you should use
X509Certificate2(Properties.Resources.SigningCert, "password", X509KeyStorageFlags.MachineKeySet);
Basically, you shouldn't use X509KeyStorageFlags.PersistKeySet in your case - you import certificate from pfx format every time.
Certificate's private key is storing in the container according to flags. So you may have no access to it if you use wrong flags.
DefaultKeySet is not just alias for UserKeySet (msdn) - so choose appropriate flags in every case.
These articles also may be helpfull:
Key Storage and Retrieval
Eight tips for working with X.509 certificates in .NET
How Certificates Work
What I've found is that for whatever reason it won't allow you to grant permissions properly to the key. To work around it, I went to:
X:\Users\All Users\Microsoft\Crypto\RSA\MachineKeys
In explorer and found the key in question (I used deduction by Date but the thumbprint is there so you should be able to match it up.) and then right clicked, took over the file permissions and then set the permissions manually.
Then my code just started working.
Interestingly however, on another machine that I needed to deploy this to, the file doesn't exist in the machinekeys directory so I don't know what I'm going to do there but...

How should I validate a user's credentials against an ADAM instance over SSL?

Apologies in advance as I haven't had much experience with directories before.
I have an ASP.net application, and I have to validate its users against an Active Directory Application Mode instance running on Server 2k3. I was previously attempting a connection with DirectoryEntry and catching the COMException if the user's credentials (userPrincipalName & password) were wrong, but I had a number of problems when trying to bind as users who weren't a member of any ADAM groups (which is a requirement).
I recently found the System.DirectoryServices.AccountManagement library, which seems a lot more promising, but although it works on my local machine, I'm having some troubles when testing this in our testbed environment. Chances are I'm simply misunderstanding how to use these objects correctly, as I wasn't able to find any great documentation on the matter. Currently I am creating a PrincipalContext with a Windows username and password, then calling the AuthenticateCredentials with the user's userPrincipalName and password. Here's a very short exert of what I'm doing:
using (var serviceContext = new PrincipalContext(
ContextType.ApplicationDirectory,
serverAddress,
rootContainer,
ContextOptions.Negotiate | ContextOptions.SecureSocketLayer,
serviceAccountUsername,
serviceAccountPassword)) {
bool credentialsValid = serviceContext.ValidateCredentials(userID, password, ContextOptions.SecureSocketLayer | ContextOptions.SimpleBind)
}
If the user's credentials are valid, I then go on to perform other operations with that principal context. As I said, this works for both users with and without roles in my own environment, but not in our testbed environment. My old DirectoryEntry way of checking the user's credentials still works with the same configuration.
After a very long morning, I was able to figure out the problem!
The exception message I was receiving when calling ValidateCredentials was extremely vague. After installing Visual Studio 2008 in the test environment (which is on the other side of the country, mind you!), I was able to debug and retrieve the HRESULT of the error. After some very deep searching in to Google, I found some very vague comments about "SSL Warnings" being picked up as other exceptions, and that enabling "SCHANNEL logging" (which I'm very unfamiliar with!) might reveal some more insight. So, after switching that on in the registry and retrying the connection, I was presented with this:
The certificate received from the remote server does not contain the expected name. It is therefore not possible to determine whether we are connecting to the correct server. The server name we were expecting is ADAMServer. The SSL connection request has failed. The attached data contains the server certificate.
I found this rather strange, as the old method of connecting via SSL worked fine. In any case, my co-worker was able to spot the problem - the name on the SSL certificate that had been issued on the server was that of the DNS name ("adam2.net") and not the host name ("adamserver"). Although I'm told that's the norm, it just wasn't resolving the correct name when using PrincipalContext.
Long story short; re-issuing a certificate with the computer name and not the DNS name fixed the problem!

Categories