Why does sslStream.AuthenticateAsServer require no UAC and alternatives - c#

While tweaking a web proxy code, I noticed that if I run the code in an app with UAC (User Access Control) I get an exception
Here is the location where the exception is thrown:
sslStream.AuthenticateAsServer(_certificate, false, SslProtocols.Tls | SslProtocols.Ssl3 | SslProtocols.Ssl2, false);
Here is the error I get (with UAC)
The server mode SSL must use a certificate with the associated private key.
First question: Why?
2nd question: Is there an alternative? I really would like to run this in UAC (note that http works fine)

It sounds like the private key to your certificate is probably located somewhere with restricted file access. This is so that not just any user can read your private key. What is the purpose of running your service with limited privileges? If the key is only for this service, you may consider removing the security limitations on the folder it is in, but if this key is for your whole domain, I would be hesitant to do that...

Have you installed the certificate in the appropriate certificate store?
From what I remember when working with certificates, the file that you load is only used as a reference to identify the server certificate in one of the Windows certificate stores. If the proxy is running under one account but not the other, maybe you didn't install the certificate in that other account's personal certificate store.

Related

How to programmatically add x509 certificate to local machine store using c#

I understand the question title may be a duplicate but I have not found an answer for my situation yet so here goes;
I have this simple peice of code
// Convert the Filename to an X509 Certificate
X509Certificate2 cert = new X509Certificate2(certificateFilePath);
// Get the server certificate store
X509Store store = new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine);
store.Open(OpenFlags.MaxAllowed);
store.Add(cert); // x509 certificate created from a user supplied filename
But keep being presented with an "Access Denied" exception.
I have read some information that suggests using StorePermissions would solve my issue but I don't think this is relevant in my code.
Having said that, I did test it to to be sure and I couldn't get it to work.
I also found suggestions that changing folder permissions within Windows was the way to go and while this may work(not tested), it doesn't seem practical for what will become distributed code.
I also have to add that as the code will be running as a service on a server, adding the certificates to the current user store also seems wrong.
Is there anyway to programmatically add a certificate into the local machine store?
Thank you to Oscar and Bob for asking the questions and leading me in the right direction +10 to you both :)
My issue, as I think we all knew (even me) was the user running the application had insufficient privilages to add a certificate to the local machine store.
But various attempts to elevate the user permissions were failing for me, let me explain why.
I had 3 seperate projects in my solution, the wcf service which requires the X509certificates, the windows form client and the cryptography class library which, amongst other things, installs the certificates provided via the windows form client.
As most of the code within all 3 projects could run without elevated permissions, I really wanted to only elevate them at the certificate install stage within the class library but I tried to use Process and Verb= "runas" in code and this didn't work.
Then I tried to add a custom manifest but if you try to alter the properties of a class library to use a custom manifest, you'll find the option is disabled.
So I changed things.
My cryptography class is now within my windows form client and I've added the custom manifest to the client. This now means the whole client opens with elevated privilages but I'd rather that than the alternative.
Thank you again

Error : "Impossible to find object or property", when loading a private certificate in a Windows service running as LocalSystem

I am developing a Windows service middleware that uses security certificates to perform authentication when it wants to initialize a secure communication channel with remote applications.
The Windows service is a .Net 3.5 application that uses (via a C++/CLI layer) the C++ WinCrypt library in order to load certificates. In particular I am calling the CertFindCertificateInStore method when i accessing the store to load the certificate. The CertFindCertificateInStore uses the “My” system store as store by default.
I have registered my private certificate in the “My” system store either for the current user (the user logged onto the machine) either for the local machine, via the MMC console. Be sure that the certificate is good and it includes my private key.
When i run the Windows service as current user (again the user logged onto the machine) the certificate is correctly loaded, instead the certificate is not loaded when i run the service as “LocalSystem”. The error "Impossible to find object or property", is then raised when I call the CertFindCertificateInStore. I point up that the “My” store instead continues to be accessible.
(NB: When I open a new session using a different user and I run the same Windows service as user for whom I registered the certificates, the certificate is again correctly loaded)
Then I used either the MMC console either the winhttpcertcfg.exe system tool in order to verify that the “LocalSystem” user has the rights to access my certificate. Afterwards either the MMC console and the winhttpcertcfg.exe tool confirm that THEORETICALLY the user “LocalSystem” has the rights to access the certificate.
Do you have any suggestion to help me to solve this problem?
Thank you in advance for your help,
PS: a similar question has been posted here Certificates: Cannot find the certificate and private key for decryption Error when sign

Cannot find the certificate in either the LocalMachine store or the CurrentUser store

I have already installed the ssl cert in user PC. And, I also imported the cert under Local computer of Trusted Root Certification Authorities. I am trying to consume the web servcie. In my code I attach the cert as below.
webservice.ClientCertificates.Add(X509Certificate.CreateFromCertFile(certPath));
But I got the following error.
Cannot find the certificate in either the LocalMachine store or the CurrentUser store
What could be the issue? I'm using .Net Framework 2.0. So, not able to use WCF. Thanks.
That means,
probably you have the wrong certificates installed.
you installed the certificates to the wrong place.
An easy way to tell if you have a client certificate installed properly is
launch IE.
Open Tools | Internet options.
In Content tab, click Certificates button.
Then you should see the cert in Personal (read cert path and see whether it contains errors), and will be able to use it in your WinForms application.
I experienced similar issue. In my case it was solved by using MMC and giving Full control rights of the certificate for user IIS_IUSRS. My Website is running under ApplicationPoolIdentity. This answer guided me to the right path: How to give ASP.NET access to a private key in a certificate in the certificate store?
I had the same error when trying to connect to a service that hadn't been set to use SSL. Once I change the configuration of the service to use SSL, the connection worked fine.
Also you may get that error if the account doesn't have access to the private key, which WinHttpCertCfg.exe from the Windows Resource Kit can solve.

WebException Could not establish trust relationship for the SSL/TLS secure channel

My company has developed a .NET web service and a client dll that uses that web service. The webservice is hosted on our server over ssl and the cert is provided and signed by GoDaddy. We have some clients in a hosted environment that are getting the following error message from the client dll when it tries to access our web service.
System.Net.WebException The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.
Our fix has been to have them open IE on the server, which is a challenge in and of itself for a lot of the hosted services, and go to the WSDL url. IE then prompts them with a security alert dialog. It says the cert date is valid and a valid name matching the name of the page, but was issued by a company you have not chosen to trust. When they click Yes to proceed, the client dll can then succesfully connect to the web service and operate as normal.
Does anyone have any idea why GoDaddy would not have been in there valid publishers list? All of the servers we have running has GoDaddy as a valid authority. I'm guessing, for security reasons, they've uninstalled the authority for GoDaddy, but not totally convinced that there's not some other underlying issue.
Unfortunately, I haven't had much luck trying to recreate this locally. If I go into Internet Options and remove the GoDaddy authorities and hit our service, ssl works just fine. I go back into the list of publishers and GoDaddy gets put right back in. So my second question is, How the heck do you get rid of GoDaddy so I can get an invalid cert warning?
Okay, last question. Is there a way in code I can tell the web service to ignore invalid certs. I've seen some posts on doing this programatically with WCF but not old web services.
I fixed this error by adding this line before calling the web method:
System.Net.ServicePointManager.ServerCertificateValidationCallback = (senderX, certificate, chain, sslPolicyErrors) => { return true; };
You may need to install on your servers the intermediate certificates used to sign your SSL certs.
Browsers will attempt to validate the SSL certificate by checking the validation of the chain of certs that signed the SSL cert. If the server doesn't supply the certificate chain with the SSL certificate, the browser may reject the SSL cert. (More of an issue for Firefox than IE). The root certificate must still be installed on the client machine for any of this to work.
VB.NET equivalent is
System.Net.ServicePointManager.ServerCertificateValidationCallback = Function(senderX, certificate, chain, sslPolicyErrors)
Return True
End Function
This is really more of a serverfault question, but I'll add what I can here.
The root cert authority list that windows machines normally trust is updated fairly regularly. This comes down as a windows update to IE. You can see MSDN for more information.
If your clients do not have windows update turned on or are actively ignoring windows updates, which is unfortunately very common for a lot of IT departments, then there's not much you can do other than switch SSL providers.
Basically, they need to get the certificate updates or you need to switch to a cert provider that has a high probability of already being trusted by the machines in question. Typically this means Verisign or Thawte. The third alternative is the route you've been going down: have them manually trust the root CA.
At the end of the day I hate the idea of changing an app in this way simply because the IT departments in question are morons but the real question boils down to how your company wants to handle this.
When you look at the Go Daddy Certification Path of that certificate on the web server, do you see Go Daddy Class xxx or Starfield Class xxx ?
And from your non-legacy client i.e Windows Vista upwards, what does the Go Daddy Certification Path display? Go Daddy Class xxx or Starfield Class xxx ?
And these clients that get the warning, are they legacy clients? i.e WinXP and older?
Root Certificate updates works differently as of Windows Vista.
http://support.microsoft.com/kb/931125
Root certificates on Windows Vista and later are distributed via the automatic root update mechanism – that is, per root certificate. When a user visits a secure Web site (by using HTTPS SSL), reads a secure email (S/MIME), or downloads an ActiveX control that is signed (code signing) and encounters a new root certificate, the Windows certificate chain verification software checks Microsoft Update for the root certificate. If it finds it, it downloads the current Certificate Trust List (CTL) containing the list of all trusted root certificates in the Program, and verifies that the root certificate is listed there; it then downloads the specified root certificate to the system and installs it in the Windows Trusted Root Certification Authorities Store.
You'll probably find that your Go Daddy Certification Path on the web server thinks it's Starfield Class 2 instead of Go Daddy Class 2 so you installed the wrong root certificate. It caught me out as when you view in on the web server it doesn't display a root certificate warning, download and install the Do Daddy class 2 root cert and remove the Starfield one and your problem should dissapear.

Using the "Service Account" certificate store with .NET 2.0 HttpWebRequest

I have a .net 2.0 windows service application written in C# that access a non-soap web service via SSL with HttpWebRequest. My windows service runs under the NETWORK SERVICE account.
I am able to make SSL work by issuing a cert to the web server that my windows service talks to from a CA, then installing the CA's cert to the Local Machine\trusted root store.
What I would like to be able to do is to install the CA's cert to the "service account" trusted root cert store instead of the "local machine" store, and have this "just work."
In other words, it seems that the SSL authentication used by the .net framework is hard coded to check the current user store, and the local machine store for trusted roots when validating an SSL certificate, but is there any clean way to get it to check the "service account" store instead of (or in addition to) the other stores?
The only solution I can come up with is to override the ServicePointManager.ServerCertificateValidationCallback and then P/Invoke to the Crypto API to open the "service account" cert store in Win32, and manually search for the root in there. I'd rather not do that, since it would seem to be a pretty large performance penalty, having to wait for failure from 2 cert stores I don't intend to use anyway.
Possibly, i'm taking entirely the wrong approach here, but it really seems like I'm missing a simply way to solve my problem.
If your code controls the server it is talking to, then why dont you just return "true" from the CertValidationCallback? It is not as if the server cert will change every time you make a request?
Unless you really need the system to also verify other things related to the cert, for eg CRL, expiry etc, you might just make it work by implementing a custom cert validation callback.
If you really want to install the cert into the NETWORK_SERVICE a/c context, then there are tools from Microsoft to help you do that.
http://support.microsoft.com/kb/901183
http://web.asu.edu/community/installing-client-certificate-windows-machine
http://www.derkeiler.com/Newsgroups/microsoft.public.dotnet.security/2008-03/msg00011.html
good luck!

Categories