MailKit 3.4.2 - "535: 5.7.3 Authentication unsuccessful" - authentication

I am trying to send mail to a client's Exchange server that uses NTLM authentication and am getting a "535: 5.7.3 Authentication unsuccessful" error when using port 587 and on port 465 I get this error "An error occurred while attempting to establish an SSL or TLS connection."
My development environment .Net 4.7.2
The code am using is below
// Add Content to Mime Message
var bodyBuider = new BodyBuilder();
message.Subject = subject;
bodyBuider.HtmlBody = body;
message.Body = bodyBuider.ToMessageBody();
#endregion
#region Send Mail
SmtpClient cli = new SmtpClient();
try
{
// mitigate signed exchange server certificate by an unknown certificate authority
cli.ServerCertificateValidationCallback = (s, c, h, e) => true;
cli.Connect(emailServerInfo.Host, emailServerInfo.Port, SecureSocketOptions.Auto);
// If username and Password is supplied
if (string.IsNullOrEmpty(emailServerInfo.UserName) == false)
{
if (cli.AuthenticationMechanisms.Contains("NTLM"))
{
var ntlm = new SaslMechanismNtlm(emailServerInfo.UserName, emailServerInfo.Password);
cli.Authenticate(ntlm);
}
else
{
cli.Authenticate(emailServerInfo.UserName, emailServerInfo.Password);
}
}
cli.Send(message);
}
catch (Exception ex)
{
this._audit.AddError(new Shared.Models.Audit.AuditErrorAddRequestInfo(ex));
ret.Add(ex.Message);
}
finally
{
cli.Disconnect(true);
}
#endregion
I think this code should work because I tried it with a Sendgrid and the email gets sent. I appreciate any help I can get

You probably need to disable SSL certificate revocation checks.
// mitigate signed exchange server certificate by an unknown certificate authority
cli.ServerCertificateValidationCallback = (s, c, h, e) => true;
cli.CheckCertificateRevocation = false;

Related

What is the difference between public and private HTTPS connections?

I have a Net 5 Blazor server app published on a productive server and used a valid certificate for https requests, which works fine. Newly I added a HubConnection class to support SignalR notifications between web pages. But if I call the web page through an public URL like https://crm.example.com/call, I get the following error, although the same page works fine if I call it through an internal URL like https://10.12.0.151/call:
Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host..
I don't know why it happens, what is the difference between a public and private HTTPS connection? For public connection it uses a valid public certificate and actually works fine if I deactivate the SignalR notification.
Because of the application working just fine if I call the page with an internal URL, seems that all prerequisite for using SignalR are included such as installing WebSocket-Protocol feature on the server and so on.
The following snipet shows the part of code:
try
{
string sHubUrl = NavManager.BaseUri;
sHubUrl = sHubUrl.TrimEnd('/') + "/call";
LogBuilder.LogInfo($"URL in Call.NotificationInit: " + sHubUrl);
hubConnection = new HubConnectionBuilder()
.WithUrl(sHubUrl, options => {
options.UseDefaultCredentials = true;
options.HttpMessageHandlerFactory = (msg) =>
{
if (msg is HttpClientHandler clientHandler)
{
System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls12;
// bypass SSL certificate
clientHandler.ServerCertificateCustomValidationCallback +=
(sender, certificate, chain, sslPolicyErrors) => { return true; };
}
return msg;
};
})
.WithAutomaticReconnect()
.Build();
hubConnection.On<string, string>("NewMessage", ReceivedNotification);
await hubConnection.StartAsync();
}
catch (Exception ex)
{
LogBuilder.LogExecption("Exception at Call.NotificationInit");
}
What else should I do? Can anyone help me to solve this problem?

SSL connectivity to Redis with StackExchange.Redis

I am having a very weird issue with StackExchange.Redis to connect with Redis.
I have enabled SSL on Redis database and I am not able to connect from client to Redis server with SSL certificate with below code.
static RedisConnectionFactory()
{
try
{
string connectionString = "rediscluster:13184";
var options = ConfigurationOptions.Parse(connectionString);
options.Password = "PASSWORD";
options.AllowAdmin = true;
options.AbortOnConnectFail = false;
options.Ssl = true;
options.SslHost = "HOSTNAME";
var certificate = GetCertificateFromThubprint();
options.CertificateSelection += delegate
{
return certificate;
};
Connection = new Lazy<ConnectionMultiplexer>(
() => ConnectionMultiplexer.Connect(options)
);
}
catch (Exception ex)
{
throw new Exception("Unable to connect to Cache Server " + ex);
}
}
public static ConnectionMultiplexer GetConnection() => Connection.Value;
public static IEnumerable<RedisKey> GetCacheKeys()
{
return GetConnection().GetServer("rediscluster", 13184).Keys();
}
// Find certificate based on Thumbprint
private static X509Certificate2 GetCertificateFromThubprint()
{
// Find certificate from "certificate store" based on thumbprint and return
StoreName CertStoreName = StoreName.Root;
string PFXThumbPrint = "NUMBER";
X509Store certLocalMachineStore = new X509Store(CertStoreName, StoreLocation.LocalMachine);
certLocalMachineStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certLocalMachineCollection = certLocalMachineStore.Certificates.Find(
X509FindType.FindByThumbprint, PFXThumbPrint, true);
certLocalMachineStore.Close();
return certLocalMachineCollection[0];
}
However, If I create a console application and connect to Redis with above code then I am able to connect, but If I used same code from my web application to connect to redis then I am not able to connect.
Not sure if I am missing something.
Also, I went through "mgravell" post
In that post he has configured "CertificateValidation" method, In my scenario I want Redis to validate SSL certificate. so I have not implementation validation. And implemented "CertificateSelection" method to provide client certificate.
You can try to validate the cert using CertificateValidation. I tried the following code and it worked for me:
options.CertificateValidation += ValidateServerCertificate;
...
public static bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
return false;
}
In cases like this where you are using a client certificate and it works in a console app but does not work for some other application (you don't say but I guess from an IIS hosted web app), it almost always has to do with whether the account has permission to access the private key.
The console app runs with your account which probably has access to the private key.
To give an account access
open the Local Computer certificate store
find your client certificate
right click and choose "All tasks -> Manage Provate Keys..."
click "Add..." and add the account.
Note: if your adding an IIS App Pool account the format is:
IIS APPPOOL<my app pool name>
Location should be the local machine and not a domain.
I was able to ssl the Redis server I had started on a VM with the following codes.
add stackexchange.redis visual studio
try
{
ConfigurationOptions configurationOptions = new ConfigurationOptions
{
KeepAlive = 0,
AllowAdmin = true,
EndPoints = { { "SERVER IP ADDRESS", 6379 }, { "127.0.0.1", 6379 } },
ConnectTimeout = 5000,
ConnectRetry = 5,
SyncTimeout = 5000,
AbortOnConnectFail = false,
};
configurationOptions.CertificateSelection += delegate
{
var cert = new X509Certificate2("PFX FILE PATH", "");
return cert;
};
ConnectionMultiplexer connection =
ConnectionMultiplexer.Connect(configurationOptions);
IDatabase databaseCache = connection.GetDatabase();
//set value
databaseCache.StringSet("KEYNAME", "KEYVALUE");
//get Value
label_show_value.Text = databaseCache.StringGet("KEYNAME").ToString();
}
catch (Exception e1)
{
}

HttpClient POST request with Client Certificate

I'm trying to make a call to a third-party API which requires a client certificate. I generated the client certificate using the SSL tool and
uploaded this to the third party site. I have generated a successful POST request through Postman, providing the client certificate through
their dialogs.
The Headers are:
X-application = (MyApplicationName)
Content-Type = application/x-www-form-urlencoded
Accept = application/json
Body (x-www-form-urlencoded)
UserName = (username)
Password = (password)
When I perform a similar request through .NET I am receiving an error code indicating the certificate is not present. I have added the certificate to my personal certificate store and verified
the certificate has been added to the webhandler through debugging.
Can anyone suggest what the error might be or how I could diagnose the issue?
static async void LaunchRawHttpClient()
{
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12 | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls;
ServicePointManager.ServerCertificateValidationCallback +=
ValidateServerCertificate;
string page = "https://<URL>";
var handler = new WebRequestHandler();
X509Certificate2 cert = GetMyCert();
if (cert!= null)
{
handler.ClientCertificates.Add(cert);
}
else
{
Console.WriteLine("Cert not found");
Console.ReadLine();
return;
}
// ... Use HttpClient.
using (HttpClient client = new HttpClient(handler))
{
client.DefaultRequestHeaders.Add("X-Application", "<applicationname>");
client.DefaultRequestHeaders.Add("Accept", "application/json");
var nvc = new List<KeyValuePair<string, string>>();
nvc.Add(new KeyValuePair<string, string>("username", "<username>"));
nvc.Add(new KeyValuePair<string, string>("password", "<password>"));
FormUrlEncodedContent reqContent = new FormUrlEncodedContent(nvc);
reqContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/x-www-form-urlencoded");
using (HttpResponseMessage response = await client.PostAsync(page, reqContent))
using (HttpContent content = response.Content)
{
// ... Read the string.
string result = await content.ReadAsStringAsync();
// ... Display the result.
if (result != null)
{
Console.WriteLine(result);
}
}
}
}
static X509Certificate2 GetMyCert()
{
string certThumbprint = "<thumbprint>";
X509Certificate2 cert = null;
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = store.Certificates.Find
(X509FindType.FindByThumbprint, certThumbprint, false);
if (certCollection.Count > 0)
cert = certCollection[0];
store.Close();
return cert;
}
public static bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
{
Console.WriteLine("No SSL Errors");
return true;
}
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
Console.ReadLine();
return false;
}
I receive "No SSL Errors" message x2, followed by the missing certificate status code.
Thanks in advance
Jim
Finally found the answer on this - the problem was the private key file was not being loaded. Postman sent requests successfully, as did Curl. Curl asks for the key file explicity and this was a clue.
In .NET Core - there's a function on the X509Certificate2 object which allows you to copy it to another object combined with the key file. My project is in .NET framework and Core wasn't available.
The option I went for was using openssl to combine the cer and the key file into a pfx, which I loaded into the X509Certificate2 object. The Http Post then succeeded.

Not able to download file from Dropbox because failing to connect with SSL/TLS channel through WebClient in C#

Trying to download an executable file from Dropbox's private folder to PC in a Windows Service using WebClient.DownloadFile(). But it is throwing error
The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.
What weired is, error occurring only in Windows XP (SP2) and not in Windows 7, 8 & 8.1. (Not tested in XP SP3 and Vista yet.)
Tried with:
WebClient.UseDefaultCredentials is true.
WebClient.Credentials = CredentialCache.DefaultNetworkCredentials or CredentialCache.DefaultCredentials.
http:// in URL instead of https://.
Well, I solved it myself. Answer from this StackOverflow question, which has more votes, helped me to solve it.
My sample as follows:
public static void DownloadFileFromDropbox(string dropboxUrl, string file_name)
WebClient webclient = null;
try
{
webclient = new WebClient();
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidRemoteCertificate);
if (File.Exists(UpdatesDirStr + file_name))
{
File.Delete(UpdatesDirStr + file_name);
}
webclient.DownloadFile(dropboxUrl, UpdatesDirStr + file_name);
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (webclient != null)
{
webclient.Dispose();
webclient = null;
}
}
}
private static bool ValidRemoteCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (certificate.Subject.Contains("dropboxusercontent.com"))
{
return true;
}
else if (certificate.Subject.Contains("dropbox.com"))
{
return true;
}
return false;
}
I still wonder how does it work properly? Because new RemoteCertificateValidationCallback(ValidRemoteCertificate) is not taking dropboxUrl from anywhere. So how X509Certificate certificate param in ValidRemoteCertificate method got the correct certificate from Dropbox.com?

download a certificate from a ldap server in java

Can someone explain to me whether following code is correct to download a certificate ties to a specific person in java? I am getting an exception as "unknown protocol: ldaps".
public void downloadCert() {
String urlStr="ldaps://aServerSomeWhere:636/cn=doe%20john,ou=personnel,o=comany123,c=us?caCertificate;binary";
URL url = null;
try {
url = new URL(urlStr);
URLConnection con = url.openConnection();
InputStream is = con.getInputStream();
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)certFactory.generateCertificate(is);
System.out.println("getVersion: " + cert.getVersion());
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
No it isn't correct. There is no handler for the LDAPS: protocol in the URL/URLConnection system.
You can use JNDI to get the caCertificate attribute of that user, via DirContext.getAttributes().