I set up a FTP server in IIS with an SSL certificate which I created my self (using Makecert.exe and Pvk2Pfx). I attributed the PFX file to my FTP server.
I have a C# script which connects to the FTP server and always gets the following error message:
System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
I installed the certificate in the "Trusted Root Certification Authorities" in the local computer and user.
As it does not authenticate, I took a look via C# on the store:
X509Store store = new X509Store(StoreName.AuthRoot, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
foreach (X509Certificate2 mCert in store.Certificates)
{
var friendlyName = mCert.Issuer;
Console.WriteLine(friendlyName);
}
store.Close();
But my certificate is not listed. When I open the MMC console I see my certificate.
Usually, C# doesn't trust certificates without a trusted root certificate - like in the case of a self-signed certificate. The ServicePointManagerallows to add a function where you can handle trusts yourself.
// Callback used to validate the certificate in an SSL conversation
private static bool ValidateRemoteCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors policyErrors)
{
if (Convert.ToBoolean(ConfigurationManager.AppSettings["IgnoreSslErrors"]))
{
// Allow any old dodgy certificate...
return true;
}
else
{
return policyErrors == SslPolicyErrors.None;
}
}
private static string MakeRequest(string uri, string method, WebProxy proxy)
{
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(uri);
webRequest.AllowAutoRedirect = true;
webRequest.Method = method;
// Allows for validation of SSL conversations
ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(
ValidateRemoteCertificate);
if (proxy != null)
{
webRequest.Proxy = proxy;
}
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)webRequest.GetResponse();
using (Stream s = response.GetResponseStream())
{
using (StreamReader sr = new StreamReader(s))
{
return sr.ReadToEnd();
}
}
}
finally
{
if (response != null)
response.Close();
}
}
From blog post How to accept an invalid SSL certificate programmatically.
As a quick workaround, you could accept all certificates with:
ServicePointManager.ServerCertificateValidationCallback += (o, c, ch, er) => true;
Related
I am trying to download a file from my FTP Server but from my production server (from my laptop I am able to download the file) I get the error "A call to SSPI failed" and the inner exception "The message received was unexpected or badly formatted". This my code:
var ftpServer = "ftp://xxx.yyy.zz/";
var ftpUsername = "aaaaa";
var ftpPassword = "bbbbb";
var downloadedFilePath = "c:\\temp\\";
var downloadedFileName = "file.xls";
FtpWebRequest request = null;
ServicePointManager.SecurityProtocol = ServicePointManager.SecurityProtocol | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate (object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
return true;
};
// Get the object used to communicate with the server.
request = (FtpWebRequest)WebRequest.Create(ftpServer + "file_on_ftp_server.xls");
// download the file
request.Method = WebRequestMethods.Ftp.DownloadFile;
request.Credentials = new NetworkCredential(ftpUsername, ftpPassword);
request.EnableSsl = true;
request.KeepAlive = false;
FtpWebResponse response = (FtpWebResponse)request.GetResponse(); // <--- ERROR
Stream responseStream = response.GetResponseStream();
FileStream writer = new FileStream(downloadedFilePath + downloadedFileName, FileMode.Create);
SSPI is a Windows component.
If you are on Windows 2008, it means it's very old.
The error "The message received was unexpected or badly formatted" seems to indicate that the server certificate
uses an unsupported format (for instance, uses an unknown cipher), and thus can't be processed by the SSPI layer.
You have several solutions:
Upgrade Windows
Downgrade the server certificate
Don't use SSL
Use an alternative FTP library that does not rely on SSPI
To use SSL certificate inside .Net framework we need to provide both certificate and its corresponding private key together. To achieve this we need to use p12(.pfx) file which combined this two. In my project, I have used self-signed certificate using OpenSSL so I used below command to combine certificate and private key
pkcs12 -export -out ca.pfx -inkey ca.key -in ca.crt
pkcs12 -export -out client.pfx -inkey client.key -in client.crt
which will create p12(.pfx) file for each certificate. Then used them into your code like below .
see below lonk for more information
Reference
maybe this link also help you .
Good luck
Based on the previous suggestions your code should look like this:
var ftpServer = "ftp://xxx.yyy.zz/";
var ftpUsername = "aaaaa";
var ftpPassword = "bbbbb";
var downloadedFilePath = "c:\\temp\\";
var downloadedFileName = "file.xls";
var certName = "MyCertName" //Use yours
var password = "MyCertPassword"; //Use yours
FtpWebRequest request = null;
ServicePointManager.SecurityProtocol = ServicePointManager.SecurityProtocol | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate (object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
return true;
};
//Load certificates (one or more)
X509Certificate2Collection certificates = new X509Certificate2Collection();
certificates.Import(certName, password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
// Get the object used to communicate with the server.
request = (FtpWebRequest)WebRequest.Create(ftpServer + "file_on_ftp_server.xls");
// download the file
request.Method = WebRequestMethods.Ftp.DownloadFile;
request.Credentials = new NetworkCredential(ftpUsername, ftpPassword);
request.ClientCertificates = certificates; //Use certificates
request.EnableSsl = true;
request.KeepAlive = false;
FtpWebResponse response = (FtpWebResponse)request.GetResponse(); // <--- ERROR
Stream responseStream = response.GetResponseStream();
FileStream writer = new FileStream(downloadedFilePath + downloadedFileName, FileMode.Create);
Note: I have not tested the solution because I don't have an environment to test it on
The TLS handshake was failing because two servers were unable to agree on a common cipher suite.
IIS Crypto to enable additional cipher suites on web app's server. downloaded and ran IIS Crypto, checkmarked additional cipher suites on its Cipher Suites tab after then restarted the machine.
If diagnose failure, I'd recommend installing Wireshark on the machine with your .NET Core app.
If TLS Handshake Failure again,a message like: Alert (Level: Fatal, Description: Handshake Failure)
Some cipher suites are more secure than others, so check ref link may help to solve your problem.
temporary solution:
set the minimum key length for schannel using windows registry:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\KeyExchangeAlgorithms\Diffie-Hellman]
"ClientMinKeyBitLength"=dword:00000200
Ref: Link1, link2, link3 ,Link4
for firebase notification code
WebRequest tRequest = WebRequest.Create("https://fcm.googleapis.com/fcm/send");
tRequest.Method = "post";
tRequest.ContentType = "application/json";
var data = new{collapse_key = "unassigned", to = deviceToken,data = new
{body = message,title = title,sound = "default"}
};
message to pass for notifaction on mobile
var serializer = new JavaScriptSerializer();
var json = serializer.Serialize(data);
Byte[] byteArray = Encoding.UTF8.GetBytes(json);
tRequest.Headers.Add(string.Format("Authorization: key={0}", applicationId));
tRequest.Headers.Add(string.Format("Sender: id={0}", senderId));
tRequest.ContentLength = byteArray.Length;
error occur here below code
using (Stream dataStream = tRequest.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
using (WebResponse tResponse = tRequest.GetResponse())
{
using (Stream dataStreamResponse = tResponse.GetResponseStream())
{
//code 1
}
}
}
The exception in the title says that you are connecting to an endpoint with TLS encryption, and the certificate exposed by that endpoint is not trusted by you. This means that is not signed with a certificate that you have in your CA (Certificate Authority) Store. Like a self-signed certificate.
If the certificate is self signed, you can add it to your CA Store. If not, you can try to navigate the endpoint with your browser, and look for a copy of the certificate that the endpoint is presenting, to manually trust it. (Beware that by doing this if the endpoint has been already compromised you're manually trusting its certificate.)
You can also avoid this check by adding a custom certificate validation handler that always returns valid! (true). But, please be aware that doing this will expose you to man-in-the-middle attacks, as you'll loose the ability to check the endpoints authenticity.
ServicePointManager
.ServerCertificateValidationCallback +=
(sender, cert, chain, sslPolicyErrors) => true;
I don't why using this line of code stated above
ServicePointManager
.ServerCertificateValidationCallback +=
(sender, cert, chain, sslPolicyErrors) => true;
i could not made it work
So but using it this way worked properly:
internal static bool ValidateServerCertificate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true;
}
ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
The calling web and API site should have same SSL certificate
I use “IIS Express Development Certificate”
Export your certificate to local file
Export Certificate
Import your certificate to “Trusted Root Certification Authorities” folder in “ConsoleCertificate”
Import it into trust root
This worked for me.
ServicePointManager
.ServerCertificateValidationCallback +=
(sender, cert, chain, sslPolicyErrors) => true;
I tested it several times with and without. Definitely took care of the problem.
i am looking to get the data from any given domain names SSL certificate. For example I want to put in any website address e.g. "http://stackoverflow.com" and my code would firstly check if an SSL certificate exists. If it does then I want it to pull out the expiry date of the certificate. [ i am reading Domainnames from DB ]
Example :http://www.digicert.com/help/
i need to create a web service to check expiry date. how can i implement it?? - I have looked up loads of different things such as RequestCertificateValidationCallback and ClientCertificates etc.
I could be completely wrong (hence why I need help) but would I create a HTTPWebRequest and then somehow request the client certificate and specific elements that way?
i tried the example provided #SSL certificate pre-fetch .NET , but i am getting forbitten 403 error.
For this to work your project will need a reference to System.Security:
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
//Do webrequest to get info on secure site
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://mail.google.com");
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
response.Close();
//retrieve the ssl cert and assign it to an X509Certificate object
X509Certificate cert = request.ServicePoint.Certificate;
//convert the X509Certificate to an X509Certificate2 object by passing it into the constructor
X509Certificate2 cert2 = new X509Certificate2(cert);
string cn = cert2.GetIssuerName();
string cedate = cert2.GetExpirationDateString();
string cpub = cert2.GetPublicKeyString();
//display the cert dialog box
X509Certificate2UI.DisplayCertificate(cert2);
.NET Core 2.1 - .NET 5
You can use HttpClientHandler and ServerCertificateCustomValidationCallback Property. (This class is available in .net 4.7.1 and above also).
var handler = new HttpClientHandler
{
UseDefaultCredentials = true,
ServerCertificateCustomValidationCallback = (sender, cert, chain, error) =>
{
/// Access cert object.
return true;
}
};
using (HttpClient client = new HttpClient(handler))
{
using (HttpResponseMessage response = await client.GetAsync("https://mail.google.com"))
{
using (HttpContent content = response.Content)
{
}
}
}
#cdev's solution didn't work for me on .NET Core 2.1. It seems HttpWebRequest is not completely supported on .NET Core.
Here is the function I'm using on .NET Core to get any server's X509 certificate:
// using System;
// using System.Net.Http;
// using System.Security.Cryptography.X509Certificates;
// using System.Threading.Tasks;
static async Task<X509Certificate2> GetServerCertificateAsync(string url)
{
X509Certificate2 certificate = null;
var httpClientHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (_, cert, __, ___) =>
{
certificate = new X509Certificate2(cert.GetRawCertData());
return true;
}
};
var httpClient = new HttpClient(httpClientHandler);
await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, url));
return certificate ?? throw new NullReferenceException();
}
One thing to note is that you might need to set request.AllowAutoRedirect = False. Otherwise, if the server redirects HTTPS to HTTP, you won't be able to get the certificate from the HttpWebRequest object.
Recreating the HttpClient each time you want to make a request is very ineffective and may cause performance issues. Better to create a single readonly client for all the methods. More informations can be found here.
private readonly HttpClientHandler _handler;
private readonly HttpClient _client;
And this is my solution to getting certificate info:
Code inside constructor:
_handler = new HttpClientHandler {
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) =>
{
sender.Properties.Add("Valid", sslPolicyErrors == System.Net.Security.SslPolicyErrors.None);
sender.Properties.Add("Errors", sslPolicyErrors);
return true;
}
};
_client = new HttpClient(_handler);
Then you can read all the variables by:
using var request = new HttpRequestMessage(HttpMethod.Get, "https://www.google.com/");
var response = await _client.SendAsync(request);
var isCertificateValid = (bool)request.Properties["Valid"];
Sorry this is my first post and I'll try and be as descriptive as possible...
I am having an issue converting an HTTPWebRequest to reach an HTTPS website that requires a certificate. On my local dev machine I can create the web request to the site by using my client certificate from my CAC card using a CAC card reader. My problem arises when I have to push this code to our dev/prod server to access this site. I cant read my CAC card when I am on the server but I do have a server cert that I can use that is on our IIS 6.0.
Can you get the client's CAC card cert on the server? Is there a store to look from? Or is there a server store to look from?
I dont know the steps to using this server cert, whether I have to code this or work with my IIS Server Manager to enable it. This is my code on my local dev machine that is working. I added the ServerCertficateValidationCallback to test on the dev server.
protected void Page_Load(object sender, EventArgs e)
{
ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback(ValidateServerCertificate);
ServicePointManager.MaxServicePointIdleTime = 300000;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://url");
request.Accept = "text/xml, */*";
request.Method = "GET";
request.Headers.Add(HttpRequestHeader.AcceptLanguage, "en-us");
request.UserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; .NET CLR 3.5.30729;)";
request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
request.Credentials = CredentialCache.DefaultCredentials;
request.KeepAlive = false;
X509Certificate myCert = null;
X509Store store = new X509Store("My");
store.Open(OpenFlags.ReadOnly);
int i = 0;
foreach (X509Certificate2 m in store.Certificates)
{
if (i == 0)
myCert = m;
i++;
}
if (myCert != null)
{
request.ClientCertificates.Add(myCert);
}
//take the response and create the xml document to parse
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
string str = reader.ReadToEnd();
...
}
public static bool ValidateServerCertificate(object sender, X509Certificate certificate,X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
//certificate is in here
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
sslPE = "SSLPolicyErros: " + sslPolicyErrors;
return false;
}
Is anyone familiar with the process, for deployment, to use that server cert that is trusted by the web site im sending a request to? I want all users who access this site from our site ( our site requires CAC authentication ) to use the server cert. Unless someone has a better idea of using the users client certificate from their CAC card. I am having trouble putting this all together and any help would be appreciated.
The root ca (issuer) of your (CAC) certificate must be trusted by your server. Add the root ca certificate to the trusted root certification authority section of the windows certificate store of your server and restart iis.
It is the first time I have to use certificate authentication.
A commercial partner expose two services, a XML Web Service and a HTTP service. I have to access both of them with .NET clients.
What I have tried
0. Setting up the environment
I have installed the SSLCACertificates (on root and two intermediate) and the client certificate in my local machine (win 7 professional) using certmgr.exe.
1. For the web service
I have the client certificate (der).
The service will be consumed via a .NET proxy.
Here's the code:
OrderWSService proxy = new OrderWSService();
string CertFile = "ClientCert_DER.cer";
proxy.ClientCertificates.Add(new System.Security.Cryptography.X509Certificates.X509Certificate(CertFile));
orderTrackingTO ot = new orderTrackingTO() { order_id = "80", tracking_id = "82", status = stateOrderType.IN_PREPARATION };
resultResponseTO res = proxy.insertOrderTracking(ot);
Exception reported at last statement: The request failed with an empty response.
2. For the HTTP interface
it is a HTTPS interface I have to call through POST method.
The HTTPS request will be send from a .NET client using HTTPWebRequest.
Here's the code:
string PostData = "MyPostData";
//setting the request
HttpWebRequest req;
req = (HttpWebRequest)HttpWebRequest.Create(url);
req.UserAgent = "MyUserAgent";
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
req.ClientCertificates.Add(new System.Security.Cryptography.X509Certificates.X509Certificate(CertFile, "MyPassword"));
//setting the request content
byte[] byteArray = Encoding.UTF8.GetBytes(PostData);
Stream dataStream = req.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
//obtaining the response
WebResponse res = req.GetResponse();
r = new StreamReader(res.GetResponseStream());
Exception reported at last statement: The request was aborted: Could not create SSL/TLS secure channel.
3. Last try: using the browser
In Chrome, after installing the certificates, if I try to access both urls I get a 107 error:
Error 107 (net::ERR_SSL_PROTOCOL_ERROR)
I am stuck.
The following should help you identify the issue, here are two methods to test SSL connectivity one tests the site whilst the other is a callback method to identify why SSL failed. If nothing else it should give you a better idea why it is failing.
When the method is called it will pop up with the select certificate dialog box, obviously when you do this for real you'll want to read from the cert store automatically. The reason I have put this in is because if no valid certificate is found then you will know your problem is with the way the certificate is installed.
The best thing to do is put this code in a simple console app:
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.Net;
private static void CheckSite(string url, string method)
{
X509Certificate2 cert = null;
ServicePointManager.ServerCertificateValidationCallback += ValidateRemoteCertificate;
X509Store store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection certcollection = (X509Certificate2Collection)store.Certificates;
// pick a certificate from the store
cert = X509Certificate2UI.SelectFromCollection(certcollection,
"Caption",
"Message", X509SelectionFlag.SingleSelection)[0];
store.Close();
HttpWebRequest ws = (HttpWebRequest)WebRequest.Create(url);
ws.Credentials = CredentialCache.DefaultCredentials;
ws.Method = method;
if (cert != null)
ws.ClientCertificates.Add(cert);
using (HttpWebResponse webResponse = (HttpWebResponse)ws.GetResponse())
{
using (Stream responseStream = webResponse.GetResponseStream())
{
using (StreamReader responseStreamReader = new StreamReader(responseStream, true))
{
string response = responseStreamReader.ReadToEnd();
Console.WriteLine(response);
responseStreamReader.Close();
}
responseStream.Close();
}
webResponse.Close();
}
}
/// <summary>
/// Certificate validation callback.
/// </summary>
private static bool ValidateRemoteCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
{
// If the certificate is a valid, signed certificate, return true.
if (error == System.Net.Security.SslPolicyErrors.None)
{
return true;
}
Console.WriteLine("X509Certificate [{0}] Policy Error: '{1}'",
cert.Subject,
error.ToString());
return false;
}