LDAPConnection with Certificate Excample - c#

I struggle with LDAPConnection. Does anybody has a good example on how to use LDAPConnection properly? Google doesnt give me something I could work with.
I need to open e LDAP-Connection using SSl and having a Certificate + PW.
// options
string sServer = "LDAP://192.168.50.147:636/dc=data,dc=vzd";
// ldap
LdapDirectoryIdentifier ldapIdent = new LdapDirectoryIdentifier(server: "192.168.50.147", portNumber: 636, fullyQualifiedDnsHostName: true, connectionless: false);
NetworkCredential netCred = new NetworkCredential(myUserName, myCertificate);
// ldap connection
LdapConnection ldapConn = new LdapConnection(ldapIdent);
ldapConn.SessionOptions.SecureSocketLayer = true;
ldapConn.AuthType = AuthType.Negotiate;
ldapConn.SessionOptions.VerifyServerCertificate += delegate { return true; };
ldapConn.ClientCertificates.Add(clsGlobal.TI_Steuer.certX509);
ldapConn.Bind(netCred);
// query
string sQueryFormat = "(&(sn=*e*))"
My questions are:
How do I exactly connect to "LDAP://192.168.50.147:636/dc=data,dc=vzd"?
How do I attach Certificate + Certificate-PW with SSL to that Request?
How do I add a SearchQuery?
How do I get the LDAP's answer?
Thanks in advance. I appreciate every help.

Related

Trying to verify a LDAP over SSL certificate, Server cannot be reached

I'm currently trying to connect to a LDAPS Server using the following VB.NET Code, which should set the right parameters and use the function seen below to verify the certificate when the Bind function is called.
The value of LdapHost is the IP, 10.100.11.10, and the value for LdapPort is 7636.
connection = new LdapConnection(new LdapDirectoryIdentifier(LdapHost, LdapPort));
connection.AuthType = 2; // Negotiate
connection.SessionOptions.SecureSocketLayer = true;
connection.SessionOptions.VerifyServerCertificate = new VerifyServerCertificateCallback(VerifyServerCertificate);
//Both username and password are correct
connection.Credential = new System.Net.NetworkCredential(strUsername, strPassword);
connection.Bind();
This,
But upon trying to verify the Server Certificate, using the following code:
private bool VerifyServerCertificate(LdapConnection ldapConnection, X509Certificate certificate)
{
try
{
X509Certificate2 certificate2 = new X509Certificate2(certificate);
return certificate2.Verify();
}
catch (Exception ex)
{
throw new LdapException(9999, "Invalid certificate or path.");
}
}
It Errors out at the Bind function saying that it cannot connect to the LDAP Server at all with the message "The LDAP Server cannot be reached"
Although upon testing the connection via PowerShell, the Server is available just fine.
Is there something wrong with my verification method? Should I try a different approach entirely?
I have found the reason why the verification did not work.
Using
X509Chain chain = new X509Chain();
X509Certificate2 certificate2 = new X509Certificate2(certificate);
var chainBuilt = chain.Build(certificate2);
LogEvent("Val", 0, "Chain building status: " + chainBuilt);
if (chainBuilt == false) {
foreach (X509ChainStatus chainStatus in chain.ChainStatus)
LogEvent("Val", 0, "Chain error: " + chainStatus.Status + " " + chainStatus.StatusInformation);
chain.Reset();
return false;
} else {
chain.Reset();
return true;
}
if the verification fails helped me understand that the Root Certificate was not trusted on that specific server.
Furthermore, it told me that it could not reach the Revokation Server to check if the Certificate is still valid.
This couldn't be checked though, since the configuration uses a StartTLS certificate, which does not have a Revokation Server.
Therefore, I added
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreRootRevocationUnknown | X509VerificationFlags.IgnoreEndRevocationUnknown | X509VerificationFlags.IgnoreCtlSignerRevocationUnknown;
to ignore every property regarding the Revokation Server. It can now connect as intended.

FTP Error The remote server returned an error: 150 Opening data channel for file download

I have an FTPS site I need to connect to and get a file from. A vendor will be dropping a csv file there daily and I have to retrieve it and process it. My problem is no matter what I try and I can't connect to this site. I realize FTPS is different than SFTP and according to my research my normal method of getting files from FTP should work simply by adding an EnableSsl flag as seen below (ip, port, credentials have been changed obviously):
string uri = "ftp://127.0.0.1:123/";
string filename = "remoteFile.txt";
uri += filename;
var request = (FtpWebRequest)WebRequest.Create(uri);
request.Credentials = new NetworkCredential("user1", "secure-password1");
request.EnableSsl = true;
request.KeepAlive = false;
request.UseBinary = true;
request.UsePassive = true;
ServicePointManager.ServerCertificateValidationCallback = (s, certificate, chain, sslPolicyErrors) => true;
request.Method = WebRequestMethods.Ftp.DownloadFile;
var response = (FtpWebResponse)request.GetResponse(); //<-- error here
Stream responseStream = response.GetResponseStream();
var reader = new StreamReader(responseStream);
var fileContents = reader.ReadToEnd();
reader.Close();
response.Close();
string filePath = #"C:\Temp\localFile.txt";
using var stream = new StreamWriter(filePath);
{
stream.Write(fileContents);
}
I've tried variations of the 4 booleans I set on the request object. In this configuration I get the error in the title. If I switch passive to false I get a timeout. I can connect to this FTP site using WinSCP. There is a certificate on the site and I imported my connection configuration from a co-worker. There is an SHA-1 fingerprint.
I have also tried creating a connection with the WinSCP Nuget package and followed their example, I just can't seem to get the fingerprint correct:
var options = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "ftp://127.0.0.1:21/",
UserName = "user1",
Password = "secure-password1",
SshHostKeyFingerprint = "???",
};
using var session = new WinSCP.Session();
session.Open(options);
No matter what I've tried in that finger print property it doesn't match the pattern they want and I can't find a good example of what it should look like. On the WinSCP page it says to obtain the fingerprint from your administrator, ours provided a certificate file that has an RSA section and a Certificate section. I've tried assigning the whole file to that field, the RSA section, certificate, nothing works. I tried the fingerprint displayed in my working session from WinSCP and that doesn't work.
I've found a few questions on this site with this error but all seem to point to server issues. I figure if I can connect and get files using WinSCP then I should be able to do it through code as well.
thanks
As Martin suggested I opened WinSCP, logged into my FTP site using the app. Then I clicked the Session menu and chose Generate URL/Code... Click the .NET assembly code tab at the top and pasted the code into my project
// Set up session options
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Ftp,
HostName = "127.0.0.1",
PortNumber = 21,
UserName = "user1",
Password = "secure-password1",
FtpSecure = FtpSecure.Explicit,
TlsHostCertificateFingerprint = "2f:f5:ab:e5:f7:27:65:12:30:73:3d:9a:b7:12:88:11:62:0e:6f:a1",
};
using (Session session = new Session())
{
// Connect
session.Open(sessionOptions);
// Your code
}
I hope this helps whoever is stuck on this like I was.

Connecting to FTPS (FTP over SSL) with FluentFTP

I am using IIS in my local machine for testing FTP with SSL connection. I am using the FluentFTP library for connecting to the FTP. I am using the following code to connect to the Server.
FtpClient conn = new FtpClient();
conn.Host = firewallSslDetails.Address;
conn.Credentials = new NetworkCredential(firewallSslDetails.UserName, firewallSslDetails.Password);
conn.SslProtocols = System.Security.Authentication.SslProtocols.Default;
X509Certificate2 cert = new X509Certificate2(#"C:\Users\BizTalk360\Desktop\FtpSites\ServerCert.cer");
conn.EncryptionMode = FtpEncryptionMode.Implicit;
conn.DataConnectionType = FtpDataConnectionType.AutoActive;
conn.DataConnectionEncryption = true;
conn.EnableThreadSafeDataConnections = false;
conn.ClientCertificates.Add(cert);
conn.ValidateCertificate += new FtpSslValidation(OnValidateCertificate);
conn.Connect();
The server is returning me with the following error.
FluentFTP.FtpCommandException: Policy requires SSL.; Win32 error: Access is denied.; Error details: SSL policy requires SSL for control channel.;
For connecting over FTP the above code is working fine and for FTP with SSL it is not working.
As you seem to be connecting to the default port 21 (no explicit port specified anywhere), you need to use the "Explicit" mode:
conn.EncryptionMode = FtpEncryptionMode.Explicit;
If the server uses a self-signed certificate, you may need to verify it programmatically. Do not blindly accept any certificate, as the answer by #Ivan does. That's a security flaw. Validate the specific certificate, e.g. by checking its fingerprint.
See FtpWebRequest "The remote certificate is invalid according to the validation procedure".
//try this ,
var cl = new FtpClient(Server, Port, User, Password);
cl.EncryptionMode = FtpEncryptionMode.Implicit;
cl.DataConnectionType = FtpDataConnectionType.AutoPassive;
cl.DataConnectionEncryption = true;
cl.SslProtocols = protocol;
cl.ValidateCertificate += new FtpSslValidation(OnValidateCertificate);
var cer = new X509Certificate2(certificate);
cl.ClientCertificates.Add(cer);
System.Net.ServicePointManager.ServerCertificateValidationCallback = ServerCertificateValidationCallback;
client.Connect();
void OnValidateCertificate(FtpClient control, FtpSslValidationEventArgs e)
{
// add logic to test if certificate is valid here
e.Accept = true;
}
private bool ServerCertificateValidationCallback(object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
return true;
}

c# Application cannot access the internet if I am connected to a VPN

I am working on an application that needs constant internet connectivity in order to function according to specifications.
The problem that I am facing is that if I connect to a VPN then the application is unable to access the internet at all.
Here is a part of the code where I try to check whether the server is reachable or not:
try
{
using (var client = new System.Net.WebClient())
{
//var networkCredentials = new NetworkCredential("shumais", "password");
//WebProxy myProxy = new WebProxy("192.168.0.61", 8080) { Credentials = networkCredentials };
//WebProxy myProxy = new WebProxy("192.168.0.61", 8080);
//client.Proxy = WebRequest.GetSystemWebProxy();
IWebProxy proxy = WebRequest.GetSystemWebProxy();
proxy.Credentials = CredentialCache.DefaultCredentials;
client.UseDefaultCredentials = true;
client.Proxy = proxy;
using (var stream = client.OpenRead(WebUrls.URL_BASE_REQUEST))
{
_isServerReachable = true;
}
}
}
catch (Exception)
{
_isServerReachable = false;
}
I have got the code working with/without proxies and now just need to get the application to work when I am connected to any VPNs.
Any help will be greatly appreciated, thanks.
If you are using a Microsoft PPTP VPN, you need to uncheck "Use default gateway on remote network" in the TCP/IPv4 advanced settings for the VPN connection.
Since you're trying to access the site with the default network credentials, make sure to add the default proxy to the app.config file, and add a cookie container (seriously sounds stupid but it looks like it's helping other people out too).
Check out my answer to this post: How to use IE proxy server settings and its credentials in .Net Application

LdapConnection vs. PrincipalContext

I have the following two implementations of authenticating users with LDAP and LDAPS and I was wondering which was better / more correct. For the record, both of these work on both SSL and non-SSL connections.
I'm also curious because when watching with Wireshark on the Non-SSL PrincipalContext version, I still see traffic on Port 636. Of the four combinations (Non-SSL LdapConnection, SSL LdapConnection, Non-SSL PrincipalContext, SSL PrincipalContext) it is the only one that has traffic on both Port 389 and 636 instead of just one or the other. What could be causing this?
LDAP Connection Method:
bool userAuthenticated = false;
var domainName = DomainName;
if (useSSL)
{
domainName = domainName + ":636";
}
try
{
using (var ldap = new LdapConnection(domainName))
{
var networkCredential = new NetworkCredential(username, password, domainName);
ldap.SessionOptions.VerifyServerCertificate = new VerifyServerCertificateCallback((con, cer) => true);
ldap.SessionOptions.SecureSocketLayer = useSSL;
ldap.SessionOptions.ProtocolVersion = 3;
ldap.AuthType = AuthType.Negotiate;
ldap.Bind(networkCredential);
}
// If the bind succeeds, we have a valid user/pass.
userAuthenticated = true;
}
catch (LdapException ldapEx)
{
// Error Code 0x31 signifies invalid credentials, anything else will be caught outside.
if (!ldapEx.ErrorCode.Equals(0x31))
{
throw;
}
}
return userAuthenticated;
PrincipalContext Method:
bool userAuthenticated = false;
var domainName = DomainName;
if (useSSL)
{
domainName = domainName + ":636";
ContextOptions options = ContextOptions.SimpleBind | ContextOptions.SecureSocketLayer;
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domainName, null, options))
{
userAuthenticated = pc.ValidateCredentials(username, password, options);
}
}
else
{
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domainName))
{
userAuthenticated = pc.ValidateCredentials(username, password);
}
}
return userAuthenticated;
#DTI-Matt, in the examples above, you use VerifyServerCertificate callback that always returns true. This, essentially, defies the purpose of connecting to LDAP over SSL, as no real certificate check is performed.
While you could implement a real certificate check using X509Chain and/or X509Certificate2 classes, it seems PrincipalContext handles the checks for you.
To summarize, both LdapConnection and PrincipalContext provide very similar functionality, in means of connecting to an LDAP server over plain or SSL connection. You have to supply LdapConnection much more hand-written code for it to work properly. On the other hand, PrincipalContext gives you the same functionality with less code to write by hand.
As a note, connections to port 636 (your default LDAP over SSL port), by non-SSL PrincipalContext may be explained by the fact this class tries to connect as secure as possible.
This is what we ended up with that is working over SSL/Non-SSL.
public bool UserValid(string username, string password, bool useSSL)
{
bool userAuthenticated = false;
var domainName = DomainName;
if (useSSL)
{
domainName = domainName + ":636";
}
try
{
using (var ldap = new LdapConnection(domainName))
{
var networkCredential = new NetworkCredential(username, password, DomainName); // Uses DomainName without the ":636" at all times, SSL or not.
ldap.SessionOptions.VerifyServerCertificate += VerifyServerCertificate;
ldap.SessionOptions.SecureSocketLayer = useSSL;
ldap.AuthType = AuthType.Negotiate;
ldap.Bind(networkCredential);
}
// If the bind succeeds, we have a valid user/pass.
userAuthenticated = true;
}
catch (LdapException ldapEx)
{
// Error Code 0x31 signifies invalid credentials, so return userAuthenticated as false.
if (!ldapEx.ErrorCode.Equals(0x31))
{
throw;
}
}
return userAuthenticated;
}
private bool VerifyServerCertificate(LdapConnection connection, X509Certificate certificate)
{
X509Certificate2 cert = new X509Certificate2(certificate);
if (!cert.Verify())
{
// Could not validate potentially self-signed SSL certificate. Prompting user to install certificate themselves.
X509Certificate2UI.DisplayCertificate(cert);
// Try verifying again as the user may have allowed the certificate, and return the result.
if (!cert.Verify())
{
throw new SecurityException("Could not verify server certificate. Make sure this certificate comes from a trusted Certificate Authority.");
}
}
return true;
}

Categories

Resources