I'm developing simple Windows Service sending and receiving data from remote web service.
I decided to use WebClient class for it's simplicity and enhanced it to include certificate in request, like this:
class MyWebClient : WebClient
{
public X509Certificate cert { set; get; }
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest req = (HttpWebRequest)base.GetWebRequest(address);
req.ClientCertificates.Clear();
req.ClientCertificates.Add(cert);
return req;
}
}
This is how i prepare and make requests:
try {
//...
string qry = "Some xml query...";
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate(object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors) { return true; };
X509Certificate cert = X509Certificate.CreateFromCertFile(appPath + #"\data\cert.der");
MyWebClient cl = new MyWebClient();
cl.cert = cert;
string xmlReq = qry;
cl.Headers[HttpRequestHeader.ContentType] = "text/xml";
var data = Encoding.UTF8.GetBytes(xmlReq);
byte[] res = cl.UploadData(apiUrl, data);
cl.Dispose();
string result = Encoding.UTF8.GetString(res);
} catch (Exception ex) {
//...
}
//...
Now, i know that loading certificate from file is not the best idea, but that's not the point. The point is that above code works perfectly in desktop application, but throws "Could not establish secure channel for SSL/TLS" exception in Windows Service.
I tried to install the service under "localService" and "networkService" account and it makes no diffirence.
UPDATE: installing under "user" didn't help also.
Am I missing something?
When you load a certificate with this:
X509Certificate cert = X509Certificate.CreateFromCertFile(appPath + #"\data\cert.der");
You're only loading the certificate. However, for client-certificate authentication to work, you need to provide the private key too (otherwise, anyone could use any certificate).
Following the discussion in the chat room, you've indicated you also have a .p12 (PKCS#12), a .key and .pem file, as will as the .der file you were trying to load.
Usually, these extensions are used in this way:
.der for the certificate itself in DER encoding (binary).
.pem for the certificate itself in PEM encoding (base64-encoding of DER, within ---BEGIN....--- ... --- END --- delimiters).
.key for the private key.
.p12 (or .pfx) for the PKCS#12 file, which will contain both the certificate (and possibly the CA chain) and the private key.
In .Net, the base X509Certificate will not allow you to load a private key along with the certificate. You should look into loading the p12 file into a X509Certificate2 instance: it's essentially a convenience class that not only models certificates, but can associate a private key to the object too.
I'm not sure why this worked as a desktop application and not as a service. My guess is that it would have picked up a cert+private key from the user or machine store automatically in one of the situations. Either way, your desktop application was not authenticating simply with the DER file anyway.
Related
I am seeing an issue with sending an http request to an SSL enabled API.
The error message i get back is -
AuthenticationException: The remote certificate is invalid according to the validation procedure.
based on this request
using (HttpResponseMessage res = client.GetAsync("https://example.com").Result)
{
using (HttpContent content = res.Content)
{
string data = content.ReadAsStringAsync().Result;
if (data != null)
{
Console.WriteLine(data);
}
else
{
Console.WriteLine("Nothing returned");
}
}
}
I've been given a .pem file to verify that the certificate that is being sent back is signed by our CA and having some trouble figuring out how to do that in C#
In python I'm able to resolve the certificate errors by passing the .pem file to the verify parameter e.g.
r = requests.post(url="https://example.com", headers=headers, verify='mypem.pem')
Is there something equivalent in Dotnet Core 3's HttpClient?
Thanks for any assistance!
If you can't set up the cert as trusted for whatever reason, then you can bypass the certificate validation and verify the server yourself. It's much less elegant in .NET unfortunately, and this may not work on all platforms. Refer to this answer on bypass invalid SSL certificate in .net core for more discussion on that.
using (var httpClientHandler = new HttpClientHandler())
{
// Override server certificate validation.
httpClientHandler.ServerCertificateCustomValidationCallback = VerifyServerCertificate;
// ^ if this throws PlatformNotSupportedException (on iOS?), then you have to use
//httpClientHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
// ^ docs: https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclienthandler.dangerousacceptanyservercertificatevalidator?view=netcore-3.0
using (var client = new HttpClient(httpClientHandler))
{
// Make your request...
}
}
I think this implementation of the callback does what you need, "pinning" the CA. From this answer to Force HttpClient to trust single Certificate, with more comments from me. EDIT: That answer's status checking wasn't working, but per this answer linked by Jeremy Farmer, the following approach should:
static bool VerifyServerCertificate(HttpRequestMessage sender, X509Certificate2 certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
try
{
// Possibly required for iOS? :
//if (chain.ChainElements.Count == 0) chain.Build(certificate);
// https://forums.xamarin.com/discussion/180066/httpclienthandler-servercertificatecustomvalidationcallback-receives-empty-certchain
// ^ Sorry that thread is such a mess! But please check it.
// Without having your PEM I am not sure if this approach to loading the cert works, but there are other ways. From the doc:
// "This constructor creates a new X509Certificate2 object using a certificate file name. It supports binary (DER) encoding or Base64 encoding."
X509Certificate2 ca = new X509Certificate2("mypem.pem");
X509Chain chain2 = new X509Chain();
chain2.ChainPolicy.ExtraStore.Add(ca);
// "tell the X509Chain class that I do trust this root certs and it should check just the certs in the chain and nothing else"
chain2.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
// This setup does not have revocation information
chain2.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
// Build the chain and verify
var isValid = chain2.Build(certificate);
var chainRoot = chain2.ChainElements[chain2.ChainElements.Count - 1].Certificate;
isValid = isValid && chainRoot.RawData.SequenceEqual(ca.RawData);
Debug.Assert(isValid == true);
return isValid;
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return false;
}
Sorry I can't test this at the moment, but hope it helps a bit.
I'm trying to implement server authentication in C# (using .NET appln). I would like to achieve the following :
Connect to HTTPS URL using
String sslServerHost = "https://mail.google.com";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(sslServerHost);
If HTTPS request fails, I would be adding the server certificate to the local windows certification store.
X509Certificate cert = request.ServicePoint.Certificate;
//convert the X509Certificate to an X509Certificate2 object by passing it into the constructor
X509Certificate2 cert2 = new X509Certificate2(cert);
X509Store userCaStore = new X509Store(storeName: StoreName.Root, storeLocation: StoreLocation.CurrentUser);
// Code to import server certifictes to windows store.
userCaStore.Open(OpenFlags.ReadOnly);
userCaStore.Add(cert2);
The below is the C# code that I have used for performing server authentication.
As evident from the below code snippet, I have NEITHER ignored the certificate validation NOR added the X509Certificate to the local trust store, but still I was able to establish connection to the HTTPS URL ( WebRequest.create(url) and request.GetResponse() doesn't throw any exceptions )
String sslServerHost = "https://mail.google.com";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(sslServerHost);
request.AllowAutoRedirect = false;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
try
{
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
string responseFromServer = reader.ReadToEnd();
Debug.WriteLine(responseFromServer);
Console.WriteLine(responseFromServer);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Why is it that I'm not getting an exception when trying to access the HTTPS URL given that I have NOT added the server certificates (X509Certificate) to the local windows store.
In short, how to achieve or implement server authentication in C# where if server certificate were not added then the C# code should throw an exception.
For Java, there is a good link https://github.com/escline/InstallCert/blob/master/InstallCert.java which best describes the server authentication mechanism where if client is trying to access an HTTPS Server and if server's certificate is not present in the Java trust store, then JVM throws an exception.
This mechanism doesn't seem to hold good for .NET applications. Any help or insight would be appreciated !!
The reason is probably that mail.google.com is already trusted, since the certificate chain leads up to some root certificate which IS already in your certificate store (in "Trusted Root Certification Authorities").
If you want to test failure, follow a tutorial to create your own CA and certificates using something like OpenSSL. Then set up a web site in IIS with this untrusted certificate.
According with the web request official documentation of the web request The certificate could be installed in My certificate store of the current user. That might explain why it nevers throws an error. You can try to connect to a server without adding the certificate to the cert store and see if that is throwing an error.
The solutions I have found to solve this problem involve setting a callback on ServicePointManager.ServerCertificateValidationCallback
ServicePointManager.ServerCertificateValidationCallback =
MyRemoteCertificateValidationCallback;
public bool MyRemoteCertificateValidationCallback(System.Object sender,
X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
bool isOk = true;
// If there are errors in the certificate chain, look at each error to determine
the cause.
if (sslPolicyErrors != SslPolicyErrors.None) {
for(int i=0; i<chain.ChainStatus.Length; i++) {
if(chain.ChainStatus[i].Status !=
X509ChainStatusFlags.RevocationStatusUnknown) {
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 1, 0);
chain.ChainPolicy.VerificationFlags =
X509VerificationFlags.AllFlags;
bool chainIsValid = chain.Build((X509Certificate2)certificate);
if(!chainIsValid) {
isOk = false;
}
}
}
} else {
X509Certificate2 cert3 = new X509Certificate2(certificate);
bool verify = cert3.Verify();
var cert1 = new X509Certificate2(certificate);
if (cert1.NotAfter <= DateTime.Now)
{
return false;
}
}
return isOk;
}
In our development stage, we created a self-signed certificate, which means I have .cer and .pfx file. When we tried to call the APIs, is there any methods we can use to embed above files in the HTTPS request, so that not every client install the certificate to local trusted certificate store.
Is this possible? I found some APIs which seems like we can do like that, but just cannot get it succeed:
try
{
var secure = new SecureString();
foreach (char s in "password")
{
secure.AppendChar(s);
}
var handler = new WebRequestHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.UseProxy = false;
var certificate = new X509Certificate2(#"C:\httpstest2.pfx", secure);
handler.ClientCertificates.Add(certificate);
using (var httpClient = new HttpClient(handler))
{
httpClient.BaseAddress = new Uri("https://www.abc.com");
var foo = httpClient.GetStringAsync("api/value").Result;
Console.WriteLine(foo);
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Do I need to use X509Certificate instead of X509Certificate2?
If we purchase real certificate from 3rd-party company, can we just go through the validate exception without caring about the certificate issue?
Can you just use this code to ignore any SSL errors
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
Obviously make sure this doesn't make it to production.
Clients only need the public key in the .cer file, which is sent automatically when the https connection is established. But whether the client trusts that certificate is not a decision the server sending the cert should be allowed to make.
You can use a group policy to distribute the certificate to your clients. See http://technet.microsoft.com/en-us/library/cc770315(v=ws.10).aspx for more details.
I am using FileZilla as the server and a DNS service, so that I wouldn't have to use my local machine IP (but I've tried the following methods on both).
After trying System.Net.FtpWebRequest to work, I've read around (including a few posts on SO) and found out that the SSL support is not very adequate with that library. It was working with regular FTP, but when I tried forcing SSL, I was getting a certificate validation error saying: The remote certificate is invalid according to the validation procedure.
So, I've done some searching around and found Alex FTPS Client library. Here's the code I wrote up:
class FTPSWorker
{
public static void UploadFile(string sourceFile, string targetFile, string ftpIP, string ftpUser, string ftpPass)
{
try
{
using (FTPSClient client = new FTPSClient())
{
client.Connect(ftpIP, new NetworkCredential(ftpUser, ftpPass),
ESSLSupportMode.CredentialsRequired | ESSLSupportMode.DataChannelRequested);
client.SetTransferMode(ETransferMode.Binary);
client.PutFile(sourceFile, targetFile);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Unfortunately, I was getting the same exact certificate error. I can, however, access the FTP server perfectly fine using FileZilla client. So, I figured there would have to be a certificate issue.
I should note that my server was showing the following log entries:
Welcome Message
AUTH TLS
234 Using authentication type TLS
SSL connection established
disconnected
While the client (C# WPF application) was getting this error:
The remote certificate is invalid according to the validation procedure.
This is absolutely exact same error if I use the .NET library and MSDN code.
I've done more research and found solutions similar to these:
The remote certificate is invalid according to the validation procedure
"The remote certificate is invalid according to the validation procedure." using Gmail SMTP server
But they just seem like risky hacks... And while they do work, is there a way to have certification information to appear and maybe have user validate it/install it besides the basic Yes/No that it's currently using?
My code right now (I ditched Alex's library and went back to default .NET):
ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(FTPWorker.ValidateServerCertificate);
public class FTPWorker
{
public static void UploadFile(string sourceFile, string targetFile, string ftpIP, string ftpUser, string ftpPass)
{
try
{
string filename = "ftp://" + ftpIP + "/test/" + targetFile;
FtpWebRequest ftpReq = (FtpWebRequest)WebRequest.Create(filename);
ftpReq.Method = WebRequestMethods.Ftp.UploadFile;
ftpReq.Credentials = new NetworkCredential(ftpUser, ftpPass);
ftpReq.UsePassive = true;
ftpReq.EnableSsl = true;
ftpReq.UseBinary = true;
ftpReq.KeepAlive = false;
byte[] b = File.ReadAllBytes(sourceFile);
ftpReq.ContentLength = b.Length;
using (Stream s = ftpReq.GetRequestStream())
{
s.Write(b, 0, b.Length);
}
FtpWebResponse ftpResp = (FtpWebResponse)ftpReq.GetResponse();
if (ftpResp != null)
{
MessageBox.Show(ftpResp.StatusDescription);
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
else
{
if (System.Windows.Forms.MessageBox.Show("The server certificate is not valid.\nAccept?",
"Certificate Validation", System.Windows.Forms.MessageBoxButtons.YesNo,
System.Windows.Forms.MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
return true;
else
return false;
}
}
}
So, for anyone that had the same issue, I ended up just giving the user a warning regarding the certificate and an option to accept or deny based on the link I provided in my original post. In order for a certificate to be validated, it has to be real and not a locally created one. So, that's the only workaround there is for now.
The Alex ftps will do the same certificate validation if you specify it to.
In your client.connect add the remotecertificatevalidationcallback to accept the certificate
client.Connect(ftpIP, new NetworkCredential(ftpUser, ftpPass),
ESSLSupportMode.CredentialsRequired | ESSLSupportMode.DataChannelRequested,
new RemoteCertificateValidationCallback(ValidateTestServerCertificate));
Then below.
private static bool ValidateTestServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
// Accept any certificate
return true;
}
I wanted to use the default .net. but I'm stuck connecting to a server that's using implicit. :(
I have a simple app that uses the C# Web Client class to download a websites HTML. This is a stripped down sample of the code I'm using:
WebClient wc = new WebClient();
wc.Headers.Add("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");
htmlCode = wc.DownloadString("https://www.oig.hhs.gov/exclusions/exclusions_list.asp");
There seems to be an issue with the websites certificate, because I encounter this exception:
The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel." "The remote certificate is invalid according to the validation procedure.
If you copy and paste the link into a browser, it requires you to agree to the risks before allowing you to view the site. Its a government website, so I'm not worried about any viruses or anything. Is there anyway to tell the web client to bypass this issue, and continue to the site?
As far as I know this is because they use a invalid or expired SSL certificate. You can bypass (ignore) it by using:
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
Edit 2015:
This post is getting a lot of upvotes, but I regret my answer. It may remove your error, but it won't fix the issue. Accepting any SSL certificates will leave you vulnerable for man in the middle attacks, so it's generally a very bad idea. I will leave this answer for future reference, but please take note that you should try to fix the issue at the root, namely by making sure the SSL certificate is valid.
Try this:
What you would want to do is, to install the certificate into your local machine.
The procedure if it was not in code would be to do the following:
Open Internet Explorer in Administrator mode (not Chrome).
Go to the site
Select proceed
Click the titelbar / url bar on the certificate, to view the certificate.
Click install certificate.
So you can try to have a look at these other questions and see if they help you:
How can I install a certificate into the local machine store programmatically using c#?
Install certificates in to the Windows Local user certificate store in C#
Try to install the certificate and see if you go to the url again, if it asks to proceed.
****EDIT**:
I read the site certificate, it is because there is a mismatch between the certificate and the site's url.
A simple change would be this:
htmlCode = wc.DownloadString("https://oig.hhs.gov/exclusions/exclusions_list.asp");
Just remove the www.
To go even further building off of Leon Cullens' answer I came up with the following. Like he said in his answer, this should not be something you do as a permanent solution but in a case where you are getting this error intentionally this might help you out. This should add a little more security to situations where you do not have a choice and just need it to work.
My current situation is during development purposes I needed to use a self signed certificate and not a genuine signed certificate. This of course leads to the following error...
"The underlying connection was closed: Could not establish trust
relationship for the SSL/TLS secure channel."
Here is what I did to try and make it just a little more secure and try to avoid the "middle man attacks". I tried to make this as short and simple as possible but wanted to make sure I painted the whole picture to make it easy to understand.
using System;
using System.Text;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
namespace TestSecurity
{
class TestDownload
{
private void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
//Progress in the download
}
private void client_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
{
//Download has completed
}
private bool client_RemoteCertificateValidation(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors)
{
//Check to make sure the domain is correct
X509Certificate2 certificate_details = (X509Certificate2)certificate;
if (certificate_details.Thumbprint == "91A92CA60555DB51BEDDFE1AE4ECE54C8EBEBA97")
{
#region Fingerprint for certificate
string storedFingerprint = "" +
"42048c788db687ed84407da10f78182e3487d1fc31c07ee131151f4e19b360ad2a8c452c2e7d614a5691d5479787fe70dabd" +
"64617465732e626c75656172726f77646576656c6f706d656e742e636f6d3110300e06035504080c0747656f72676961311f" +
"301d060355040a0c16426c7565204172726f7720446576656c6f706d656e74310b3009060355040613025553312f302d0609" +
"2a864886f70d0109011620737570706f727440626c75656172726f77646576656c6f706d656e742e636f6d31123010060355" +
"04070c09436c6576656c616e64301e170d3136303232393231313733335a170d3137303232383231313733335a3081b03129" +
"302706035504030c20757064617465732e626c75656172726f77646576656c6f706d656e742e636f6d3110300e0603550408" +
"42048c788db687ed84407da10f78182e3487d1fc31c07ee131151f4e19b360ad2a8c452c2e7d614a5691d5479787fe70dabd" +
"0613025553312f302d06092a864886f70d0109011620737570706f727440626c75656172726f77646576656c6f706d656e74" +
"2e636f6d3112301006035504070c09436c6576656c616e6430820122300d06092a864886f70d01010105000382010f003082" +
"010a0282010100a1cdf5af6f1bba5cc8495d8061895f39858fde814f5581266505bf4cbe0b26506278bc247963bb7c42f0b8" +
"b00638871932ed7d0a3c6562be8e5b513f24da2768051acde875b53bf94c8ea2cec397145db206b2524c42a2019a0bfa14e2" +
"a7ef0d311235e07b7e0363345fd7f397e365c0865b1b8fa8ad7eebdc1fcdce360db04f2822438621534ae10744155a710641" +
"9a69c16745974a37c5b06917036351b92c06540a6c70aa776c143eef6f7b8ec31c0c40a9eab8a399c9065bea688ea7bd1db2" +
"30af56d2ca0f8983f9e8dacb5613755fbcd8229d7042668a9130468a7480a2afde8c18bab895472ddf1ed2c49291c04e8cc2" +
"ff24db33d231b3a2498c03a5650203010001a34d304b301d0603551d0e0416041476b5c2c82ff138b87c0e2d6c046af4c634" +
"55040a0c16426c75652048c1f54dcb82e3487d1fc31c07ee1313fba9204c7b3232ba9204c7b323a021abcbda85bfca9c9931" +
"092a864886f70d01010b050003820101001ab0dfd318cc2e93a997445d0950ffcb63544c58fe1ded6e234aa7ccdcb5c890b1" +
"61b51ae08c1f54dcb3fbeca9c9932bde91d202b89c0b6f0af1a370017fa9f6a021abcbda85bfecebebc6d6067d4dc1e51ec5" +
"02cf95867516a84f01410cf80d7af4f0d3e9a86cf7b0323dba9204c7b3232c58b2289032a12aaa1ec4f64065da8bbde4fe47" +
"42048c788db687ed84407da10f78182e3487d1fc31c07ee131151f4e19b360ad2a8c452c2e7d614a5691d5479787fe70dabd" +
"de819522bb7ef870595d9738a6acdd39b7fcf6f36948ef2b404c2b6d7ebe577555148ad90013a5c2e812b2b907c808288040" +
"0db6702407585328f7e6c84b40451384391783001174d0";
#endregion
//Use the following to get the server's fingerprint to be saved and compared against
StringBuilder hex = new StringBuilder(certificate_details.RawData.Length * 2);
foreach (byte b in certificate_details.RawData)
hex.AppendFormat("{0:x2}", b);
string serverFingerprint = hex.ToString();
if (serverFingerprint == storedFingerprint) return true;
else return false;
}
else return false;
}
else if (sslPolicyErrors == SslPolicyErrors.None)
return true;
else
return false;
}
public void TestDownload(Uri targetURL, bool useCredentials, string user, string pass)
{
WebClient client = new WebClient();
bool taskCompleted = false;
//Create the event handlers to monitor progress
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
client.DownloadDataCompleted += new DownloadDataCompletedEventHandler(client_DownloadDataCompleted);
ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(client_RemoteCertificateValidation);
//Resolve the host
IPHostEntry host = Dns.GetHostEntry(targetURL.Host);
IPAddress ip = (host.AddressList.Length > 0 ? host.AddressList[0] : null);
//Show the UI the resolved host
//Account for credentials
if (useCredentials)
{
NetworkCredential credentials = new NetworkCredential(
user,
pass);
client.Credentials = credentials;
}
else client.Credentials = null;
//Download file
client.DownloadDataAsync(targetURL, taskCompleted);
//Go to sleep until the file download has completed
do { Thread.Sleep(500); } while (client.IsBusy);
//File download complete
//Destory the event handlers as they are no longer needed
ServicePointManager.ServerCertificateValidationCallback -= client_RemoteCertificateValidation;
client.DownloadProgressChanged -= client_DownloadProgressChanged;
client.DownloadDataCompleted -= client_DownloadDataCompleted;
}
}
}
I had this issue with message:
Could not establish trust relationship for the SSL/TLS secure channel.
I was connecting to a web service on an Apache server using a .NET client.
This is what corrected the problem for me.
(proxy).UserAgent = "Apache-HttpClient/4.1.1 (java 1.5)";
According to this Answer, add this line before using WebClient instance:
ServicePointManager.ServerCertificateValidationCallback = new
RemoteCertificateValidationCallback(delegate { return true; });