Get detailed explanation for invalid SSL certificate (.NET) - c#

My service does health pings to customer web sites and reports their health. One of common issues of web site being down is being something wrong with SSL certificate.
In ServicePointManager.ServerCertificateValidationCallback it is possible to get access to certificates, chain, etc. and to do manual checks (when SslPolicyErrors is not None).
I wonder whether there is a library/method which gives explanation what's wrong (for instance, certificate is expired or root certificate is untrusted, etc.)

The X509Chain class can provide a detailed explanation why some certificate is considered invalid.
var errors = new List<string>();
var chain = new X509Chain();
// certificate is the one you want to check
chain.Build(certificate);
// traverse certificate chain
foreach (var chainElement in chain.ChainElements)
{
// ChainElementStatus contains validation errors
foreach (var status in chainElement.ChainElementStatus)
{
errors.Add(status.Status + " " + chainElement.Certificate + ": " + status.StatusInformation.Trim());
}
}
This is similar to what X509Certificate2.Verify does (if you look into the source code), albeit Verify only returns true or false.

Related

The remote certificate was rejected by the provided RemoteCertificateValidationCallback - how to get more details?

I have prepared a test case for my problem - a very simple .NET 6 Console app:
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
namespace CheckCert
{
internal class Program
{
public const string jsonUrl = "https://wordsbyfarber.com/de/top-5";
static void Main(string[] args)
{
Console.WriteLine($"jsonUrl = {jsonUrl}");
HttpClientHandler handler = new()
{
ServerCertificateCustomValidationCallback = BackendCaValidation,
CheckCertificateRevocationList = true,
};
HttpClient httpClient = new(handler);
string jsonStr = httpClient.GetStringAsync(new Uri(jsonUrl)).Result;
Console.WriteLine($"jsonStr = {jsonStr}");
}
private static bool BackendCaValidation(HttpRequestMessage message,
X509Certificate2? certificate,
X509Chain? chain,
SslPolicyErrors sslPolicyErrors)
{
Console.WriteLine($"sslPolicyErrors = {sslPolicyErrors}");
return SslPolicyErrors.None == sslPolicyErrors;
}
}
}
When I run it, it works as expected, will print SslPolicyErrors.None and the JSON content from my private website, which uses a Let's Encrypt certificate.
However when I change the jsonUrl to the URL of my work server, which I am better not sharing in public, then I end up with SslPolicyErrors.RemoteCertificateChainErrors and a System.AggregateException.
The exception says "look at the inner exception".
So inspect the inner exception and it says:
The remote certificate was rejected by the provided RemoteCertificateValidationCallback
So I keep looking at the certificate and the chain displayed by the Microsoft Edge browser - both for my private website and for the work server.
The work server uses a certificate issued by a self-signed work CA (and there is an intermediate certificate inbetween). All 3 certificates are not expired yet.
My question is: how to get more information here?
Why exactly do I get a SslPolicyErrors.RemoteCertificateChainErrors? is it because of that self-signed corporate CA or maybe because of some signing algorithm?
And also - similar code works for us in another project (an Azure Service Fabric application) without failing. I wonder, what could be the difference?
UPDATE:
I have followed the suggestion by Mr. Spiller (thank you!) and have added the code:
Console.WriteLine("-----------------------------------");
foreach (X509ChainStatus status in chain.ChainStatus)
{
Console.WriteLine($"status = {status.Status}");
}
Now my private Let's Encrypt secured URL looks like this (why is there no chain printed? I can see the chain in the web browser):
And the "faulty" corporate URL looks like this:
My main question is: how to make my app work against the corporate URL, without making it insecure?
I.e. I would probably have to accept the returned SslPolicyErrors.RemoteCertificateChainErrors in my app, but can I still perform some checks?
The parameter X509Chain? chain has a property ChainStatus which you can use to get the status for each element of the certification chain.
Each element in turn has a property Status of type System.Security.Cryptography.X509Certificates.X509ChainStatusFlags (cf. documentation) that should give you the status of each particular element of the certification chain.
In your case one (the only?) element most likely has the status UntrustedRoot.
If you want to connect to your corporate server even though the certificate is not trusted, you can simply return true from the callback. I.e. in BackendCaValidation check whether you are talking to the corporate server and return true even though sslPolicyErrors is not None.
The other (preferred?) way is to trust your corporate CA system-wide. I.e. add the CA to the cert store of your operating system and mark it as trusted.

C# X509 certificate validation, with Online CRL check, without importing root certificate to trusted root CA certificate store

I'm trying to validate an X509 certificate chain without importing the root CA certificate into the trusted root CA certificate store (in production this code will run in an Azure Function, and you can't add certificates to the trusted root CA certificate store on Azure App Services).
We also need to perform an online CRL check on this certificate chain.
I've searched on this and I see many others are facing the same problem, but none of the suggestions seem to work. I've followed the approach outlined in this SO post, which echoes the suggestions from issue #26449 on the dotnet/runtime GitHub. Here's a small console application (targetting .NET Core 3.1) reproducing the problem:
static void Main(string[] args)
{
var rootCaCertificate = new X509Certificate2("root-ca-cert.cer");
var intermediateCaCertificate = new X509Certificate2("intermediate-ca-cert.cer");
var endUserCertificate = new X509Certificate2("end-user-cert.cer");
var chain = new X509Chain();
chain.ChainPolicy.ExtraStore.Add(rootCaCertificate);
chain.ChainPolicy.ExtraStore.Add(intermediateCaCertificate);
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
chain.Build(endUserCertificate);
chain.Build(new X509Certificate2(endUserCertificate));
var errors = chain.ChainStatus.ToList();
if (!errors.Any())
{
Console.WriteLine("Certificate is valid");
return;
}
foreach (var error in errors)
{
Console.WriteLine($"{error.Status.ToString()}: {error.StatusInformation}");
}
}
When ran this returns three errors:
UntrustedRoot: A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider.
RevocationStatusUnknown: The revocation function was unable to check revocation for the certificate.
OfflineRevocation: The revocation function was unable to check revocation because the revocation server was offline.
However, if I add the root CA certificate to the trusted root CA certificate store then all three errors disappear.
Questions
Is this something wrong with my implementation, or is what I'm trying to do not possible?
What are my options to try to achieve this? A bit of Googling suggests the X509ChainPolicy.CustomTrustStore offered in .NET 5 might save the day. Is Bouncy Castle another option for achieving this?
A bit of Googling suggests the X509ChainPolicy.CustomTrustStore offered in .NET 5 might save the day
Yep.
Instead of putting rootCaCertificate into ExtraStore, put it into CustomTrustStore, then set chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;. Now your provided root is the only root valid for the chain. You can also remove the AllowUnknownCertificateAuthority flag.
OfflineRevocation
This error is slightly misleading. It means "revocation was requested for the chain, but one or more revocation responses is missing". In this case it's missing because the builder didn't ask for it, because it didn't trust the root. (Once you don't trust the root you can't trust the CRLs/OCSP responses, so why ask for them at all?)
RevocationStatusUnknown
Again, the unknown is because it didn't ask for it. This code is different than OfflineRevocation because technically a valid OCSP response is (effectively) "I don't know". That'd be an online/unknown.
UntrustedRoot
Solved by the custom trust code above.
Other things of note: The correct way to determine the certificate is valid is to capture the boolean return value from chain.Build. For your current chain, if you had disabled revocation (chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck) then Build would have returned true... but the UntrustedRoot error would still be present in the ChainStatus output. The boolean return from Build is false if there are any errors that the VerificationFlags didn't say to ignore.
You also only need to call Build once :).
static void Main(string[] args)
{
var rootCaCertificate = new X509Certificate2("root-ca-cert.cer");
var intermediateCaCertificate = new X509Certificate2("intermediate-ca-cert.cer");
var endUserCertificate = new X509Certificate2("end-user-cert.cer");
var chain = new X509Chain();
chain.ChainPolicy.CustomTrustStore.Add(rootCaCertificate);
chain.ChainPolicy.ExtraStore.Add(intermediateCaCertificate);
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
bool success = chain.Build(endUserCertificate);
if (success)
{
return;
}
foreach (X509ChainStatus error in chain.ChainStatus)
{
Console.WriteLine($"{error.Status.ToString()}: {error.StatusInformation}");
}
}
Well, not a full answer, but probably it will get you going.
We've built before an Azure Web App (not a function app, but I guess wouldn't matter) which did exactly what you want. Took us a week or so. Full answer would be posting the code of the whole app, we I obviously cannot do. Some hints though.
We walked around the certificate problem by uploading certificates to the app (TLS settings) and accessing them in code through WEBSITE_LOAD_CERTIFICATES setting (you put thumbprints there, it's also mentioned in the link you posted), than you can get them in code and build your own certificate store:
var certThumbprintsString = Environment.GetEnvironmentVariable("WEBSITE_LOAD_CERTIFICATES");
var certThumbprints = certThumbprintsString.Split(",").ToList();
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
for (var i = 0; i < certThumbprints.Count; i++)
{
store.Certificates.Find(X509FindType.FindByThumbprint, certThumbprint, false);
}
Than we implemented a number of validators: for subject, time, certificate chain (you basically mark your root somehow and go from the certificate you validate up the chain in your store and see if you end up in your root) and CRL.
For CRL Bouncy Castle offers support:
// Get CRL from certificate, fetch it, cache it
var crlParser = new X509CrlParser();
var crl = crlParser.ReadCrl(data);
var isRevoked = crl.IsRevoked(cert);
Getting CRL from the certificate is tricky, but doable (I followed this for the purpose, more or less https://learn.microsoft.com/en-us/archive/blogs/joetalksmicrosoft/pki-authentication-as-a-azure-web-app).

X509Chain.Build() method explanation

I want to validate chain of certificates, I get a X509Certificate2 collection and have to validate if all the certificates build one chain.
Usually, in order to verify the certificates chain I should take the digital signature from the leaf certificate and check if it is signed by the root certificate - but in .NET I can't find a way to extract the signature from the X509Certificate2 object.
Therefore, I thought of using X509Chain.Build() method in the following way:
void ValidateChain(X509Certificate2Collection collection, X509Certificate2 leaf)
{
X509Chain x509Chain = new X509Chain();
x509Chain.ChainPolicy.ExtraStore.AddRange(collection);
bool isValid = x509Chain.Build(leaf);
}
But I have some questions about the build method:
As I understood, the chain was built also from my computer store, and I want that it is built only from the ExtraStore, how can I define this behaviour?
I saw that after the chain was built it doesn't contain the Root Certificate; my question is why, and how can I verify that the chain has Root CA, since this is not part of the chain elements.
I will so appreciate it if someone can explain to me how the Build() method works.
You should use the ChainStatus value after the Build operation. MSDN reference here:
The X509Chain object has a global error status called ChainStatus that should be used for certificate validation. The rules governing certificate validation are complex, and it is easy to oversimplify the validation logic by ignoring the error status of one or more of the elements involved. The global error status takes into consideration the status of each element in the chain.
Try this code snippet out:
bool chainIsValid = false;
var chain = new X509Chain();
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 1, 0);
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
chainIsValid = chain.Build(certificate);

Verify Remote Server X509Certificate using CA Certificate File

I've generated a CA and multiple certificates (signed by CA) using OpenSSL and I have a .NET/C# client and server both using SslStream which each have their own certificates/keys, mutual authentication is enabled and revocation is disabled.
I'm using RemoteCertificateValidationCallback for SslStream to validate the remote server's certificate and I was hoping I could just load the CA's public certificate (as a file) in the program and use it to verify the remote certificate rather then actually installing the CA in the Windows Certificate Store. The problem is the X509Chain won't show anything else unless I install the CA into the store, either will the Windows CryptoAPI shell when I open a PEM version of one of the certificates.
My question is, how can I verify a certificate has been signed by my specific CA just by using the CA's public certificate file without using Windows certificate store or WCF when RemoteCertificateValidationCallback, X509Certificate and X509Chain don't seem to give me anything to work with?
Because the CA certificate is NOT in the root certificate store, you will have within the RemoteCertificateValidationCallback() an error flag of SslPolicyErrors.RemoteCertificateChainErrors ; a possibility is to validate explicitely the certificate chain against your own X509Certificate2Collection, since you are not using the local store.
if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors)
{
X509Chain chain0 = new X509Chain();
chain0.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
// add all your extra certificate chain
chain0.ChainPolicy.ExtraStore.Add(new X509Certificate2(PublicResource.my_ca));
chain0.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
isValid = chain0.Build((X509Certificate2)certificate);
}
You can also re-use the chain passed in the callback, add your extra certificate(s) in the ExtraStore collection, and validate with the AllowUnknownCertificateAuthority flag which is needed since you add untrusted certificate(s) to the chain.
You could also prevent the original error by adding programmatically the CA certificate in the trusted root store (of course it opens a popup, for it is a major security problem to globally add a new trusted CA root) :
var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
X509Certificate2 ca_cert = new X509Certificate2(PublicResource.my_ca);
store.Add(ca_cert);
store.Close();
EDIT: For those who want to clearly test the chain with your CA :
Another possibility is to use the library BouncyCastle to build the certificate chain and validate the trust. The options are clear and errors are easy to understand. In cas of success it will build the chain, otherwise an exception is returned. Sample below :
// rootCerts : collection of CA
// currentCertificate : the one you want to test
var builderParams = new PkixBuilderParameters(rootCerts,
new X509CertStoreSelector { Certificate = currentCertificate });
// crls : The certificate revocation list
builderParams.IsRevocationEnabled = crls.Count != 0;
// validationDate : probably "now"
builderParams.Date = new DateTimeObject(validationDate);
// The indermediate certs are items necessary to create the certificate chain
builderParams.AddStore(X509StoreFactory.Create("Certificate/Collection", new X509CollectionStoreParameters(intermediateCerts)));
builderParams.AddStore(X509StoreFactory.Create("CRL/Collection", new X509CollectionStoreParameters(crls)));
try
{
PkixCertPathBuilderResult result = builder.Build(builderParams);
return result.CertPath.Certificates.Cast<X509Certificate>();
...
How can I verify a certificate has been signed by my specific CA just by using the CA's public certificate file without using Windows certificate store or WCF when RemoteCertificateValidationCallback, X509Certificate and X509Chain don't seem to give me anything to work with?
The following code will avoid the Windows certificate stores and validate the chain. Its a little different than JB's code, especially in the use of flags. The code below does not require AllowUnknownCertificateAuthority (but it does use X509RevocationMode.NoCheck since I don't have a CRL).
The name of the function does not matter. Below, VerifyServerCertificate is the same callback as RemoteCertificateValidationCallback in SslStream class. You can also use it for the ServerCertificateValidationCallback in ServicePointManager.
static bool VerifyServerCertificate(object sender, X509Certificate certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
try
{
String CA_FILE = "ca-cert.der";
X509Certificate2 ca = new X509Certificate2(CA_FILE);
X509Chain chain2 = new X509Chain();
chain2.ChainPolicy.ExtraStore.Add(ca);
// Check all properties
chain2.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
// This setup does not have revocation information
chain2.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
// Build the chain
chain2.Build(new X509Certificate2(certificate));
// Are there any failures from building the chain?
if (chain2.ChainStatus.Length == 0)
return true;
// If there is a status, verify the status is NoError
bool result = chain2.ChainStatus[0].Status == X509ChainStatusFlags.NoError;
Debug.Assert(result == true);
return result;
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return false;
}
I have not figured out how to use this chain (chain2 below) by default such that there's no need for the callback. That is, install it on the ssl socket and the connection will "just work". And I have not figured out how install it such that its passed into the callback. That is, I have to build the chain for each invocation of the callback. I think these are architectural defects in .Net, but I might be missing something obvious.

Novell LDAP C# - Novell.Directory.Ldap - Has anybody made it work?

I'm trying to use the library released by Novell (Novell.Directory.Ldap). Version 2.1.10.
What I've done so far:
I tested the connection with an application (LdapBrowser) and it's working, so its not a communication problem.
It's compiled in Mono, but I'm working with Visual Studio. So created a project with the sources. I also included a reference to Mono.Security, because the project depended on it.
I commented a call (freeWriteSemaphore(semId); ) in the error catching part of the connection, because it was throwing more exceptions. I checked what that call did, and its just a error tracing mechanism.
I followed the basics steps provided in the documentation by Novell (http://www.novell.com/coolsolutions/feature/11204.html).
// Creating an LdapConnection instance
LdapConnection ldapConn= new LdapConnection();
ldapConn.SecureSocketLayer = ldapPort == 636;
//Connect function will create a socket connection to the server
ldapConn.Connect(ldapHost,ldapPort);
//Bind function will Bind the user object Credentials to the Server
ldapConn.Bind(userDN,userPasswd);
Right now it's crashing at the Bind() function. I get the error 91.
So, has someone ever used this library and seen it work? If so, what did you do to make it work, is there some special configuration needed? Is there a way to make it work in .NET environment without Mono (I can have references to Mono dlls, but I don't want it to be installed on the server)?
(UPDATE)
The connection is on port 636, thus using SSL. I checked with WireShark the communication and compared with what I get from LDAP Browser. I've seen that the step where the SSL certicate is communicated, is not done by the LDAP library. So, what is the best way to make it do what its supposed to?
(UPDATE) I checked the documentation and it's indicating that it doesn't support SSL. http://www.novell.com/coolsolutions/feature/11204.html
Authenticate to the LDAP server with
LdapConnection.Bind(). We support only
cleartext authentication. SSL/TLS
support is yet to be added.
But the documentation date from 2004, and since then, many updates have been made. And there is a parameter in the library to define if the connection uses SSL. So now I'm confused.
(UPDATE) Found a more up-to-date documentation : http://developer.novell.com/documentation//ldapcsharp/index.html?page=/documentation//ldapcsharp/cnet/data/bqwa5p0.html. The way the SSL connection is made, is by registering the certificate on the server. The problem is that what I'm doing is not bound to a specific Novell server, so the certificate must be obtained dynamically.
I came looking for a solution to a similar problem. My bind command would fail as well while using the same code from Novell's website. The solution that worked for me was adding a dynamic Certificate Validation Call back. You can read about it here.
// Creating an LdapConnection instance
LdapConnection ldapConn = new LdapConnection();
ldapConn.SecureSocketLayer = true;
ldapConn.UserDefinedServerCertValidationDelegate += new
CertificateValidationCallback(MySSLHandler);
//Connect function will create a socket connection to the server
ldapConn.Connect(ldapHost, ldapPort);
//Bind function will Bind the user object Credentials to the Server
ldapConn.Bind(userDN, userPasswd);
// Searches in the Marketing container and return all child entries just below this
//container i.e. Single level search
LdapSearchResults lsc = ldapConn.Search("ou=users,o=uga",
LdapConnection.SCOPE_SUB,
"objectClass=*",
null,
false);
while (lsc.hasMore())
{
LdapEntry nextEntry = null;
try
{
nextEntry = lsc.next();
}
catch (LdapException e)
{
Console.WriteLine("Error: " + e.LdapErrorMessage);
// Exception is thrown, go for next entry
continue;
}
Console.WriteLine("\n" + nextEntry.DN);
LdapAttributeSet attributeSet = nextEntry.getAttributeSet();
System.Collections.IEnumerator ienum = attributeSet.GetEnumerator();
while (ienum.MoveNext())
{
LdapAttribute attribute = (LdapAttribute)ienum.Current;
string attributeName = attribute.Name;
string attributeVal = attribute.StringValue;
Console.WriteLine(attributeName + "value:" + attributeVal);
}
}
ldapConn.Disconnect();
Console.ReadKey();
}
public static bool MySSLHandler(Syscert.X509Certificate certificate,
int[] certificateErrors)
{
X509Store store = null;
X509Stores stores = X509StoreManager.CurrentUser;
//string input;
store = stores.TrustedRoot;
X509Certificate x509 = null;
X509CertificateCollection coll = new X509CertificateCollection();
byte[] data = certificate.GetRawCertData();
if (data != null)
x509 = new X509Certificate(data);
return true;
}
I finally found a way to make this work.
First, theses posts helped me get on the right track : http://directoryprogramming.net/forums/thread/788.aspx
Second, I got a compiled dll of the Novell LDAP Library and used the Mono.Security.Dll.
The solution:
I added this function to the code
// This is the Callback handler - after "Binding" this is called
public bool MySSLHandler(Syscert.X509Certificate certificate, int[] certificateErrors)
{
X509Store store = null;
X509Stores stores = X509StoreManager.LocalMachine;
store = stores.TrustedRoot;
//Import the details of the certificate from the server.
X509Certificate x509 = null;
X509CertificateCollection coll = new X509CertificateCollection();
byte[] data = certificate.GetRawCertData();
if (data != null)
x509 = new X509Certificate(data);
//List the details of the Server
//if (bindCount == 1)
//{
Response.Write("<b><u>CERTIFICATE DETAILS:</b></u> <br>");
Response.Write(" Self Signed = " + x509.IsSelfSigned + " X.509 version=" + x509.Version + "<br>");
Response.Write(" Serial Number: " + CryptoConvert.ToHex(x509.SerialNumber) + "<br>");
Response.Write(" Issuer Name: " + x509.IssuerName.ToString() + "<br>");
Response.Write(" Subject Name: " + x509.SubjectName.ToString() + "<br>");
Response.Write(" Valid From: " + x509.ValidFrom.ToString() + "<br>");
Response.Write(" Valid Until: " + x509.ValidUntil.ToString() + "<br>");
Response.Write(" Unique Hash: " + CryptoConvert.ToHex(x509.Hash).ToString() + "<br>");
// }
bHowToProceed = true;
if (bHowToProceed == true)
{
//Add the certificate to the store. This is \Documents and Settings\program data\.mono. . .
if (x509 != null)
coll.Add(x509);
store.Import(x509);
if (bindCount == 1)
removeFlag = true;
}
if (bHowToProceed == false)
{
//Remove the certificate added from the store.
if (removeFlag == true && bindCount > 1)
{
foreach (X509Certificate xt509 in store.Certificates)
{
if (CryptoConvert.ToHex(xt509.Hash) == CryptoConvert.ToHex(x509.Hash))
{
store.Remove(x509);
}
}
}
Response.Write("SSL Bind Failed.");
}
return bHowToProceed;
}
And i used it in the binding process
// Create Connection
LdapConnection conn = new LdapConnection();
conn.SecureSocketLayer = true;
Response.Write("Connecting to:" + ldapHost);
conn.UserDefinedServerCertValidationDelegate += new
CertificateValidationCallback(MySSLHandler);
if (bHowToProceed == false)
conn.Disconnect();
if (bHowToProceed == true)
{
conn.Connect(ldapHost, ldapPort);
conn.Bind(loginDN, password);
Response.Write(" SSL Bind Successfull ");
conn.Disconnect();
}
quit = false;
The key elements are using the SSL Handler to dynamically obtain the Certificate, and using X509StoreManager.LocalMachine so that when the website is running its able to save and fetch the certificates.
The UserDefinedServerCertValidationDelegate is obsolete, so if it is an issue with invalid ssl certificates, you can skip the certificatevalidaion this way:
LdapConnectionOptions options = new LdapConnectionOptions()
.ConfigureRemoteCertificateValidationCallback(new CertCallback((a, b, c, d) => true))
.UseSsl();
LdapConnection connection = new LdapConnection(options);
connection.Connect(...);
You should however review if ignoring the certificate is a secure solution for your application.
91 is "cannot connect". Try to put the server in "ldap://x.x.x.x" format, check that userDN is set properly (with domain etc).
I am often using WireShark to see what is going on at the network level (it is aware of LDAP protocol).
I work on Forefront Identity Manager integration. So the code I write always comes from a few calling clients. This may not be appropriate if you are trying to package an application for use "anywhere".
I just wanted to update this thread with a simple solution for Novell servers which have the default TLS/SSL "confidentiality required" option enabled.
1) Make sure you get the SSL certificates off the Novell server you are binding too and enroll those into the trusted store on the executing client / server. There are normally two 1 for the IP and for the hostname dependent on which you will call (DNS preferable)
2) Import the following / add references
using System.DirectoryServices;
using System.DirectoryServices.Protocols;
3) Here is a snippet. Make sure you choose the AuthenticationTypes.SecureSocketsLayer which is key.
// serverAddress = Server IP or DNS (Match SSL certificate)
// ObjectDN = The DN of the user you are binding to
// userName = Account which will be used to make the bind
// password = password of the user which will make the bind
// value = The value you wish to add to the attribute
// Connect to the user in LDAP
DirectoryEntry entry = new DirectoryEntry("LDAP://" + serverAddress + "/" + ObjectDN + ""
, userName
, password
, AuthenticationTypes.SecureSocketsLayer);
// Write the Updated attribute
entry.Properties["attribute"].Value = value;
// Read back the updated Attribute into a label
label.Text = entry.Properties["attribute"].Value.ToString();
I think I may have already offered this answer to someone else in a different question.
[OtherQuestion on LDAP][1]
Two issues I think: 1) What kind of bind are you trying to do? SSL? Clear text? Anonymous?
2) How is it configured on the eDirectory side for LDAP binds?
The tool LDAP Browser, are you referring to the one at this link?
Free LDAP Browser
On the eDirectory side, they can require TLS for all LDAP communication, and they can disallow Anonymous binds.
Can you ask the folks at the other end to enable LDAP tracing (Using DStrace with the +LDAP option enabled, some links for how to use Dstrace on Novell eDirectory look at: Different types of Dstrace Capturing and understand DS Trace for Identity Manager.)
That usually will show an error message that will enlighten you.
My guess is either Require TLS is enabled, and you might not be doing a successful SSL bind.
If so, try to connect on port 636, with SSL enabled, and a fully qualified DN for the user you are trying to login as.
If you are trying with SSL enabled, and you are not getting a pop up box about accepting the tree CA's trusted root certficate, then perhaps the CA or the SSL certificate taht the eDirectory server is user has expired or is broken. (There are any number of causes for this that can be common, and take but a moment to fix).
Usually in Dstrace you will see an error about the SSL certificate if there is a problem. An example from a Novell Identity Manager perspective of an expired certificate is in this article: Certificate Expired As well as some details on how to fix the certificates.
Next possibility is that the DN you are specifying is not quite correct.
Let me know if you need more help.
Following my previous post - if you have to use secure connection, try to use ldaps:// as a prefix to server address.
If there is no SSL/TLS support, you can try this - guidelines and .NET wrapper for OpenLDAP library.
One important point - there are settings for TLS security level in OpenLDAP, so if your LDAP server has self-signed certificate you either have to import it on a client side or set TLS to not check the signing authority *that is less secure of course).
I had gone through this scenario, for me Novell LDAP service running in Kubernetes container. I tried adding CA certificate to the Mono trust store, which will add the file inside "/usr/share/.mono/certs/Trust" in linux container. But nothing did work, still Novell connect not successful for LDAP 636 port.
Finally I made it work in below way:
LdapConnection Connection = new LdapConnection();
Connection.SecureSocketLayer = true;
Connection.UserDefinedServerCertValidationDelegate += new
Novell.Directory.Ldap.RemoteCertificateValidationCallback(LdapSSLHandler);
public bool LdapSSLHandler(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Security.Cryptography.X509Certificates.X509Chain chain,
System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == sslPolicyErrors.None)
{
return true; //Is valid
}
if (certificate.GetCertHashString() == "YOUR CERTIFICATE HASH KEY") // Thumbprint value of the certificate
{
return true;
}
return false;
}

Categories