Certificate validation/installation for FTPS (SSL)? - c#

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. :(

Related

How to fix "Remote certificate is invalid according to the validation procedure"

I am going to try to send emails via Mailkit but ran into problems with the error from "System.Security.Authentication.AuthenticationException" which is "The remote certificate is invalid according to the validation procedure" (translated from danish) My mailserver runs SSL TLS and the TLS supports version 1.2 and 1.3. my code is as below: I do not hope that it is to much code - but I do not know where to enhance the code so it can handle SSL correctly :-(
The error occur in the line "client.Connect("servername", 587, true);"
So my question is: How to avoid this error message via Mailkit?
public void SendMail(string AFromMailAdr, string AFromName, string AToMailAdr, string AToName, string ASubject, string ABody)
{
MimeMessage message = new MimeMessage();
...
using (var client = new MailKit.Net.Smtp.SmtpClient())
{
client.Timeout = 30000;
client.Connect("servername", 587, true);
client.Authenticate("Username", "password");
client.Send(message);
client.Disconnect(true);
}
}
I have googlet a lot until now without finding the correct answer - so therefore I kindly ask here on SO.
To be fair, the underlying problem should be checked/corrected.
You can control how MailKit does the server certificate validation using a ServerCertificateValidationCallback
For debugging purposes you could return true; in the callback function.
Code from the MailKit documentation:
using (var client = new MailKit.Net.Smtp.SmtpClient())
{
// Set our custom SSL certificate validation callback.
client.ServerCertificateValidationCallback = MySslCertificateValidationCallback;
client.Timeout = 30000;
client.Connect("servername", 587, true);
client.Authenticate("Username", "password");
client.Send(message);
client.Disconnect(true);
}
static bool MySslCertificateValidationCallback (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
// If there are no errors, then everything went smoothly.
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
// Note: MailKit will always pass the host name string as the `sender` argument.
var host = (string) sender;
if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable) != 0) {
// This means that the remote certificate is unavailable. Notify the user and return false.
Console.WriteLine ("The SSL certificate was not available for {0}", host);
return false;
}
if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) != 0) {
// This means that the server's SSL certificate did not match the host name that we are trying to connect to.
var certificate2 = certificate as X509Certificate2;
var cn = certificate2 != null ? certificate2.GetNameInfo (X509NameType.SimpleName, false) : certificate.Subject;
Console.WriteLine ("The Common Name for the SSL certificate did not match {0}. Instead, it was {1}.", host, cn);
return false;
}
// The only other errors left are chain errors.
Console.WriteLine ("The SSL certificate for the server could not be validated for the following reasons:");
// The first element's certificate will be the server's SSL certificate (and will match the `certificate` argument)
// while the last element in the chain will typically either be the Root Certificate Authority's certificate -or- it
// will be a non-authoritative self-signed certificate that the server admin created.
foreach (var element in chain.ChainElements) {
// Each element in the chain will have its own status list. If the status list is empty, it means that the
// certificate itself did not contain any errors.
if (element.ChainElementStatus.Length == 0)
continue;
Console.WriteLine ("\u2022 {0}", element.Certificate.Subject);
foreach (var error in element.ChainElementStatus) {
// `error.StatusInformation` contains a human-readable error string while `error.Status` is the corresponding enum value.
Console.WriteLine ("\t\u2022 {0}", error.StatusInformation);
}
}
return false;
}

C# verify server certificate with .pem file

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.

C# - Accessing HTTPS URL from .NET application (without importing server certificates) is not throwing SSL exception

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;
}

Web Client Exception: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel

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; });

SSL communication between Java and C# applications

My goal is to make a secure communication between a Java server and client written in C#.
java server code:
System.setProperty("javax.net.ssl.keyStore","cert/mySrvKeystore");
System.setProperty("javax.net.ssl.keyStorePassword","myPassword");
SSLServerSocketFactory sslserversocketfactory =
(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
SSLServerSocket sslserversocket = = (SSLServerSocket) sslserversocketfactory.createServerSocket(2389);
while(true) {
System.err.println("server w8 new connection");
try {
SSLSocket sslsocket = (SSLSocket) sslserversocket.accept();
//sslsocket.startHandshake();
in = sslsocket.getInputStream();
out = sslsocket.getOutputStream();
out.flush();
String response = new String(receiveMessage());
while (response != "end") {
System.out.println("Server recv="+response);
response = new String(receiveMessage());
sendMessage(("echo="+response).getBytes());
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
and client written in c# :
client = new TcpClient() { SendTimeout = 5000, ReceiveTimeout = 5000 };
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse(host), port);
client.Connect(serverEndPoint);
client.NoDelay = true;
Console.WriteLine("Client connected.");
// Create an SSL stream that will close the client's stream.
SslStream sslStream = new SslStream(client.GetStream(), false, ValidateServerCertificate, null);
// The server name must match the name on the server certificate.
try
{
sslStream.AuthenticateAsClient("someName");
}
catch (AuthenticationException error)
{
Console.WriteLine("Exception: {0}", error.Message);
if (error.InnerException != null)
{
Console.WriteLine("Inner exception: {0}", error.InnerException.Message);
}
Console.WriteLine("Authentication failed - closing the connection.");
client.Close();
return;
}
ASCIIEncoding ascii = new ASCIIEncoding();
SendData(ascii.GetBytes("Hello World"));
and
public static bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
// Do not allow this client to communicate with unauthenticated servers.
return false;
}
and i get the following errors: in c#
A first chance exception of type "System.Security.Authentication.AuthenticationException" occurred in System.dll
Certificate error: RemoteCertificateChainErrors
Exception: The remote certificate is invalid according to the validation procedure.
Authentication failed - closing the connection.
I know that the issue can be the fact that i use different types of certificates, but i don't know how to make a standard sslServerSocket with X509Certificate in java. Can some one help me, with good example, or some advice how can i reach my goal ?
P.S. I was looking to bouncycastle library, causes it has both java, and c# implementation, but i would like to use standard libraries, and built-in functionality of the languages.
From your example, it doesn't look like you need to generate your certificate and private key programmatically. You can generate a certificate in your server keystore with keytool:
keytool -genkey -alias myalias -keystore mykeystore.jks -dname "CN=www.example.com"
Or better, with the SAN extension too, if you're using Java 7's keytool:
keytool -genkey -alias myalias -keystore mykeystore.jks -dname "CN=www.example.com" -ext san=dns:www.example.com
Here, www.example.com is the host name as seen by the client. You can add other things in the Subject DN (dname), but make sure the CN is the host name.
Once it's generated, export your self-signed certificate using:
keytool -export myservercert.crt -alias myalias -keystore mykeystore.jks
You should then be able to import it as a trusted certificate in your Windows certificate store from use from C#.

Categories