I am getting the following error during a web service request to a remote web service:
Could not establish trust relationship for the SSL/TLS secure channel. ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
Is there anyway to ignore this error, and continue?
It seems the remote certificate is not signed.
The site I connect to is www.czebox.cz - so feel free to visit the site, and notice even browsers throw security exceptions.
Add a certificate validation handler. Returning true will allow ignoring the validation error:
ServicePointManager
.ServerCertificateValidationCallback +=
(sender, cert, chain, sslPolicyErrors) => true;
Allowing all certificates is very powerful but it could also be dangerous. If you would like to only allow valid certificates plus some certain certificates it could be done like this.
.NET Core:
using (var httpClientHandler = new HttpClientHandler())
{
httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) =>
{
if (sslPolicyErrors == SslPolicyErrors.None)
{
return true; //Is valid
}
if (cert.GetCertHashString() == "99E92D8447AEF30483B1D7527812C9B7B3A915A7")
{
return true;
}
return false;
};
using (var httpClient = new HttpClient(httpClientHandler))
{
var httpResponse = httpClient.GetAsync("https://example.com").Result;
}
}
.NET Framework:
System.Net.ServicePointManager.ServerCertificateValidationCallback += delegate (
object sender,
X509Certificate cert,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
{
return true; //Is valid
}
if (cert.GetCertHashString() == "99E92D8447AEF30483B1D7527812C9B7B3A915A7")
{
return true;
}
return false;
};
Update:
How to get cert.GetCertHashString() value in Chrome:
Click on Secure or Not Secure in the address bar.
Then click on Certificate -> Details -> Thumbprint and copy the value. Remember to do cert.GetCertHashString().ToLower().
IgnoreBadCertificates Method:
//I use a method to ignore bad certs caused by misc errors
IgnoreBadCertificates();
// after the Ignore call i can do what ever i want...
HttpWebRequest request_data = System.Net.WebRequest.Create(urlquerystring) as HttpWebRequest;
/*
and below the Methods we are using...
*/
/// <summary>
/// Together with the AcceptAllCertifications method right
/// below this causes to bypass errors caused by SLL-Errors.
/// </summary>
public static void IgnoreBadCertificates()
{
System.Net.ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(AcceptAllCertifications);
}
/// <summary>
/// In Short: the Method solves the Problem of broken Certificates.
/// Sometime when requesting Data and the sending Webserverconnection
/// is based on a SSL Connection, an Error is caused by Servers whoes
/// Certificate(s) have Errors. Like when the Cert is out of date
/// and much more... So at this point when calling the method,
/// this behaviour is prevented
/// </summary>
/// <param name="sender"></param>
/// <param name="certification"></param>
/// <param name="chain"></param>
/// <param name="sslPolicyErrors"></param>
/// <returns>true</returns>
private static bool AcceptAllCertifications(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certification, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
return true;
}
The reason it's failing is not because it isn't signed but because the root certificate isn't trusted by your client. Rather than switch off SSL validation, an alternative approach would be to add the root CA cert to the list of CAs your app trusts.
This is the root CA cert that your app currently doesn't trust:
-----BEGIN CERTIFICATE-----
MIIFnDCCBISgAwIBAgIBZDANBgkqhkiG9w0BAQsFADBbMQswCQYDVQQGEwJDWjEs
MCoGA1UECgwjxIxlc2vDoSBwb8WhdGEsIHMucC4gW0nEjCA0NzExNDk4M10xHjAc
BgNVBAMTFVBvc3RTaWdudW0gUm9vdCBRQ0EgMjAeFw0xMDAxMTkwODA0MzFaFw0y
NTAxMTkwODA0MzFaMFsxCzAJBgNVBAYTAkNaMSwwKgYDVQQKDCPEjGVza8OhIHBv
xaF0YSwgcy5wLiBbScSMIDQ3MTE0OTgzXTEeMBwGA1UEAxMVUG9zdFNpZ251bSBS
b290IFFDQSAyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoFz8yBxf
2gf1uN0GGXknvGHwurpp4Lw3ZPWZB6nEBDGjSGIXK0Or6Xa3ZT+tVDTeUUjT133G
7Vs51D6z/ShWy+9T7a1f6XInakewyFj8PT0EdZ4tAybNYdEUO/dShg2WvUyfZfXH
0jmmZm6qUDy0VfKQfiyWchQRi/Ax6zXaU2+X3hXBfvRMr5l6zgxYVATEyxCfOLM9
a5U6lhpyCDf2Gg6dPc5Cy6QwYGGpYER1fzLGsN9stdutkwlP13DHU1Sp6W5ywtfL
owYaV1bqOOdARbAoJ7q8LO6EBjyIVr03mFusPaMCOzcEn3zL5XafknM36Vqtdmqz
iWR+3URAUgqE0wIDAQABo4ICaTCCAmUwgaUGA1UdHwSBnTCBmjAxoC+gLYYraHR0
cDovL3d3dy5wb3N0c2lnbnVtLmN6L2NybC9wc3Jvb3RxY2EyLmNybDAyoDCgLoYs
aHR0cDovL3d3dzIucG9zdHNpZ251bS5jei9jcmwvcHNyb290cWNhMi5jcmwwMaAv
oC2GK2h0dHA6Ly9wb3N0c2lnbnVtLnR0Yy5jei9jcmwvcHNyb290cWNhMi5jcmww
gfEGA1UdIASB6TCB5jCB4wYEVR0gADCB2jCB1wYIKwYBBQUHAgIwgcoagcdUZW50
byBrdmFsaWZpa292YW55IHN5c3RlbW92eSBjZXJ0aWZpa2F0IGJ5bCB2eWRhbiBw
b2RsZSB6YWtvbmEgMjI3LzIwMDBTYi4gYSBuYXZhem55Y2ggcHJlZHBpc3UvVGhp
cyBxdWFsaWZpZWQgc3lzdGVtIGNlcnRpZmljYXRlIHdhcyBpc3N1ZWQgYWNjb3Jk
aW5nIHRvIExhdyBObyAyMjcvMjAwMENvbGwuIGFuZCByZWxhdGVkIHJlZ3VsYXRp
b25zMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQW
BBQVKYzFRWmruLPD6v5LuDHY3PDndjCBgwYDVR0jBHwweoAUFSmMxUVpq7izw+r+
S7gx2Nzw53ahX6RdMFsxCzAJBgNVBAYTAkNaMSwwKgYDVQQKDCPEjGVza8OhIHBv
xaF0YSwgcy5wLiBbScSMIDQ3MTE0OTgzXTEeMBwGA1UEAxMVUG9zdFNpZ251bSBS
b290IFFDQSAyggFkMA0GCSqGSIb3DQEBCwUAA4IBAQBeKtoLQKFqWJEgLNxPbQNN
5OTjbpOTEEkq2jFI0tUhtRx//6zwuqJCzfO/KqggUrHBca+GV/qXcNzNAlytyM71
fMv/VwgL9gBHTN/IFIw100JbciI23yFQTdF/UoEfK/m+IFfirxSRi8LRERdXHTEb
vwxMXIzZVXloWvX64UwWtf4Tvw5bAoPj0O1Z2ly4aMTAT2a+y+z184UhuZ/oGyMw
eIakmFM7M7RrNki507jiSLTzuaFMCpyWOX7ULIhzY6xKdm5iQLjTvExn2JTvVChF
Y+jUu/G0zAdLyeU4vaXdQm1A8AEiJPTd0Z9LAxL6Sq2iraLNN36+NyEK/ts3mPLL
-----END CERTIFICATE-----
You can decode and view this certificate using
this certificate decoder or another certificate decoder
Bypass SSL Certificate....
HttpClientHandler clientHandler = new HttpClientHandler();
clientHandler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };
// Pass the handler to httpclient(from you are calling api)
var client = new HttpClient(clientHandler)
To disable ssl cert validation in client configuration.
<behaviors>
<endpointBehaviors>
<behavior name="DisableSSLCertificateValidation">
<clientCredentials>
<serviceCertificate>
<sslCertificateAuthentication certificateValidationMode="None" />
</serviceCertificate>
</clientCredentials>
</behavior>
This code worked for me. I had to add TLS2 because that's what the URL I am interested in was using.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback +=
(sender, cert, chain, sslPolicyErrors) => { return true; };
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(UserDataUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new
MediaTypeWithQualityHeaderValue("application/json"));
Task<string> response = client.GetStringAsync(UserDataUrl);
response.Wait();
if (response.Exception != null)
{
return null;
}
return JsonConvert.DeserializeObject<UserData>(response.Result);
}
Old, but still helps...
Another great way of achieving the same behavior is through configuration file (web.config)
<system.net>
<settings>
<servicePointManager checkCertificateName="false" checkCertificateRevocationList="false" />
</settings>
</system.net>
NOTE: tested on .net full.
This works for .Net Core.
Call on your Soap client:
client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication =
new X509ServiceCertificateAuthentication()
{
CertificateValidationMode = X509CertificateValidationMode.None,
RevocationMode = X509RevocationMode.NoCheck
};
If you are using sockets directly and are authenticating as the client, then the Service Point Manager callback method won't work. Here's what did work for me. PLEASE USE FOR TESTING PURPOSES ONLY.
var activeStream = new SslStream(networkStream, false, (a, b, c, d) => { return true; });
await activeStream.AuthenticateAsClientAsync("computer.local");
The key here, is to provide the remote certificate validation callback right in the constructor of the SSL stream.
To further expand on BIGNUM's post - Ideally you want a solution that will simulate the conditions you will see in production and modifying your code won't do that and could be dangerous if you forget to take the code out before you deploy it.
You will need a self-signed certificate of some sort. If you know what you're doing you can use the binary BIGNUM posted, but if not you can go hunting for the certificate. If you're using IIS Express you will have one of these already, you'll just have to find it. Open Firefox or whatever browser you like and go to your dev website. You should be able to view the certificate information from the URL bar and depending on your browser you should be able to export the certificate to a file.
Next, open MMC.exe, and add the Certificate snap-in. Import your certificate file into the Trusted Root Certificate Authorities store and that's all you should need. It's important to make sure it goes into that store and not some other store like 'Personal'. If you're unfamiliar with MMC or certificates, there are numerous websites with information how to do this.
Now, your computer as a whole will implicitly trust any certificates that it has generated itself and you won't need to add code to handle this specially. When you move to production it will continue to work provided you have a proper valid certificate installed there. Don't do this on a production server - that would be bad and it won't work for any other clients other than those on the server itself.
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 have a client that calls an API that signs its response messages. The signature validation setup requires special binding that looks like this:
public class SignatureBinding : Binding
{
public override BindingElementCollection CreateBindingElements()
{
var signingElement = new AsymmetricSecurityBindingElement
{
AllowInsecureTransport = false,
RecipientTokenParameters = new X509SecurityTokenParameters(X509KeyIdentifierClauseType.IssuerSerial, SecurityTokenInclusionMode.Never),
InitiatorTokenParameters = new X509SecurityTokenParameters(X509KeyIdentifierClauseType.IssuerSerial, SecurityTokenInclusionMode.AlwaysToRecipient),
DefaultAlgorithmSuite = SecurityAlgorithmSuite.Basic256,
SecurityHeaderLayout = SecurityHeaderLayout.Strict,
MessageProtectionOrder = MessageProtectionOrder.SignBeforeEncrypt,
MessageSecurityVersion = MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10,
AllowSerializedSigningTokenOnReply = true
};
signingElement.SetKeyDerivation(false);
return new BindingElementCollection
{
signingElement,
new HttpsTransportBindingElement()
};
}
}
And in the ClientCredentials behavior:
public class CredentialsBehavior : ClientCredentials
{
public CredentialsBehavior()
{
base.ServiceCertificate.DefaultCertificate = store.FindBySerialNumber(signatureCertSN);
}
//Code omitted
}
I have confirmed that the above code works fine when run from an ordinary computer. The message is sent, the server crafts a response and signs it, it comes back, the client validates the signature, and all is well.
However, there is a failure when running from the intended server, which cannot access CRL services due to firewalls. The ServiceModel call returns an error when I send the message over the channel. The error pertains to the certificate that contains the public key for validating the signature. The error is:
The X.509 certificate CN=somecert.somedomain.com, OU=CE_Operations, O="MyCompany, Inc.", L=City, S=State, C=US chain building failed. The certificate that was used has a trust chain that cannot be verified. Replace the certificate or change the certificateValidationMode. The revocation function was unable to check revocation because the revocation server was offline.
The server exists in a domain that can't access CRLs so I disabled the check with help from this answer:
ServicePointManager.ServerCertificateValidationCallback += ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
ServicePointManager.CheckCertificateRevocationList = false;
However, the error persists. I'm guessing that ServerCertificateValidationCallback only fires for server certificates, and this certificate is different.
How do I convince the service model to allow the use of this certificate without checking the CRL or performing other validation procedures?
Set certificateValidationMode to None to ignore certificate validation X509CertificateValidationMode
This is a behavior, so if you want to do it programmatically you should bind it as new behavior to your service :
ServiceHost host = new ServiceHost(typeof(Service));
ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(IService), new WebHttpBinding(), "http://...");
var endpointClientbehavior = new ClientCredentials();
endpointClientbehavior.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
endpoint.Behaviors.Add(endpointClientbehavior);
I'm trying to create a C# proxy DLL that allow VS2015 Community, on my offline workstation, access to internet through a corporate HTTP proxy with authentication.
Following instruction of this MSDN blog post I'm able to connect VisualStudio to HTTP pages in this way:
namespace VSProxy
{
public class AuthProxyModule : IWebProxy
{
ICredentials crendential = new NetworkCredential("user", "password");
public ICredentials Credentials
{
get
{
return crendential;
}
set
{
crendential = value;
}
}
public Uri GetProxy(Uri destination)
{
ServicePointManager.ServerCertificateValidationCallback = (Header, Cer, Claim, SslPolicyErrors) => true;
return new Uri("http://128.16.0.123:1234", UriKind.Absolute);
}
public bool IsBypassed(Uri host)
{
return host.IsLoopback;
}
}
}
But I'm not able to connect to the account authentication page for Visual Studio Community access.
So, I'm trying to validate Microsoft certificate using DLL.
There is any way can I accomplish HTTPS and certificate issue?
How can I validate the certificate in the webProxy DLL?
If you want to bypass the certificate check altogether, you could set your ServicePointManager.ServerCertificateValidationCallback to always use a delegate which returns true:
var validationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
....
ServicePointManager.ServerCertificateValidationCallback += validationCallback;
I'd wrap that in a try / catch / finally and in the finally, remove the delegate (as it otherwise applies process-wide iirc):
finally
{
ServicePointManager.ServerCertificateValidationCallback -= validationCallback;
}
UPDATE 26/03/18:
If you have control over the creation of the HttpClient, you can pass a HttpClientHandler when you construct it, with its ServerCertificateCustomValidationCallback delegate set to return true. You are effectively limiting the dangerous effect of disabling SSL checking process-wide and limiting it to the use of this HttpClient. Much safer. Code:
var handler = new HttpClientHandler();
// Optional check to enable / disable based on config setting.
if (ConfigurationManager.AppSettings["EnableSslCertificateCheck"] == null ||
Convert.ToBoolean(ConfigurationManager.AppSettings["EnableSslCertificateCheck"]) == false)
{
handler = new HttpClientHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual,
ServerCertificateCustomValidationCallback =
(httpRequestMessage, cert, cetChain, policyErrors) => true
};
}
return new HttpClient(handler);
You may have a SSL proxy certificate company gave. You just import the one into root certificate in IE(i.e. http://www.instructables.com/id/Installing-an-SSL-Certificate-in-Windows-7/, https://bto.bluecoat.com/webguides/sslv/sslva_first_steps/Content/Topics/Configure/ssl_ie_cert.htm)
Or just ignoring certificate validation via .Net config
how to set ServicePointManager.ServerCertificateValidationCallback in web.config
How to stop certificate errors temporarily with WCF services (OzrenTkalcecKrznaric's answer)
In case of Visual Studio 2015, the .Net config file is located at "%PROGRAMFILES(x86)%\Microsoft Visual Studio 14.0\Common7\IDE\devenv.exe.config".
I hope it is helpful.
ServicePointManager.ServerCertificateValidationCallback Is the main access for network certificate validation on .Net if you wish to validate a certificate you must do it here. What I suggest you is to bind the callback somewhere else than inside the GetProxy method. Put it where you initialize the proxy and the perform your certificate validation there.
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'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.